refactor: remove unused CMS/JDE repositories and data sources
Remove legacy JDE and CMS direct-access code that is no longer used: - Delete ICmsDataSource, IJdeDataSource interfaces - Delete ISearchProcessor, IUpdateProcessor interfaces - Delete IJdeRepository and ICmsRepository (all partials) - Delete JdeRepository and CmsRepository implementations - Delete JdeQueries and CmsQueries - Delete JdeFileDataSource, JdeOracleDataSource - Delete CmsFileDataSource, CmsOracleDataSource - Remove unused methods from LotFinderRepository interfaces - Delete associated unit tests (CmsRepositoryTests, JdeRepositoryTests) All data sync now uses ETL pipelines via DataSync project.
This commit is contained in:
@@ -38,8 +38,6 @@ public static class DataAccessDependencyInjection
|
||||
|
||||
// Register repositories as scoped (per-request lifetime)
|
||||
services.AddScoped<ILotFinderRepository, LotFinderRepository>();
|
||||
services.AddScoped<IJdeRepository, JdeRepository>();
|
||||
services.AddScoped<ICmsRepository, CmsRepository>();
|
||||
|
||||
// Register SqlKata compiler (singleton, thread-safe)
|
||||
services.AddSingleton<SqlServerCompiler>();
|
||||
@@ -74,8 +72,6 @@ public static class DataAccessDependencyInjection
|
||||
|
||||
// Register repositories as scoped (per-request lifetime)
|
||||
services.AddScoped<ILotFinderRepository, LotFinderRepository>();
|
||||
services.AddScoped<IJdeRepository, JdeRepository>();
|
||||
services.AddScoped<ICmsRepository, CmsRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using JdeScoping.Core.Models;
|
||||
using JdeScoping.Core.Models.Quality;
|
||||
|
||||
namespace JdeScoping.DataAccess.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for accessing CMS Oracle database.
|
||||
/// </summary>
|
||||
public interface ICmsRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets Manufacturing Information System (MIS) data from CMS database.
|
||||
/// Uses MisDataTimeoutSeconds timeout due to complex 10-table JOIN.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming MIS data records.</returns>
|
||||
IAsyncEnumerable<MisData> GetMisDataAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using JdeScoping.Core.Models.Inventory;
|
||||
|
||||
namespace JdeScoping.DataAccess.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Inventory (lots) operations for JDE Oracle repository.
|
||||
/// </summary>
|
||||
public partial interface IJdeRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets lot master data from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming lots.</returns>
|
||||
IAsyncEnumerable<Lot> GetLotsAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets lot usage (cardex) transactions from production schema, optionally filtered by last update.
|
||||
/// Uses special LotUsageTimeoutSeconds timeout due to large dataset.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming lot usages.</returns>
|
||||
IAsyncEnumerable<LotUsage> GetLotUsagesAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets lot usage transactions from archive schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming archived lot usages.</returns>
|
||||
IAsyncEnumerable<LotUsage> GetLotUsagesArchiveAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets lot location tracking from JDE Stage view.
|
||||
/// Uses JDE Stage connection.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming lot locations.</returns>
|
||||
IAsyncEnumerable<LotLocation> GetLotLocationsAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
using JdeScoping.Core.Models;
|
||||
using JdeScoping.Core.Models.Inventory;
|
||||
using JdeScoping.Core.Models.Lookup;
|
||||
using JdeScoping.Core.Models.Organization;
|
||||
|
||||
namespace JdeScoping.DataAccess.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Reference data operations for JDE Oracle repository.
|
||||
/// </summary>
|
||||
public partial interface IJdeRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets item master data from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming items.</returns>
|
||||
IAsyncEnumerable<Item> GetItemsAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets user/operator data from production schema.
|
||||
/// Note: Incremental filtering not supported for users (full sync always).
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Ignored (full sync always).</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming users.</returns>
|
||||
IAsyncEnumerable<JdeUser> GetUsersAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets branch business units from production schema (type code 'BP').
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming branches.</returns>
|
||||
IAsyncEnumerable<Branch> GetBranchesAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets profit center business units from production schema (type code 'I3').
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming profit centers.</returns>
|
||||
IAsyncEnumerable<ProfitCenter> GetProfitCentersAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work center business units from production schema (type code 'WC').
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming work centers.</returns>
|
||||
IAsyncEnumerable<WorkCenter> GetWorkCentersAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets status codes from JDE Stage view.
|
||||
/// Uses JDE Stage connection.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming status codes.</returns>
|
||||
IAsyncEnumerable<StatusCode> GetStatusCodesAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets function codes from production schema.
|
||||
/// Note: Does not support incremental filtering (full sync always).
|
||||
/// </summary>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming function codes.</returns>
|
||||
IAsyncEnumerable<FunctionCode> GetFunctionCodesAsync(CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets organization hierarchy (work center to profit center mapping) from production schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming org hierarchy records.</returns>
|
||||
IAsyncEnumerable<OrgHierarchy> GetOrgHierarchyAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets item routing master data from production schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming route masters.</returns>
|
||||
IAsyncEnumerable<RouteMaster> GetRouteMastersAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
using JdeScoping.Core.Models.WorkOrders;
|
||||
|
||||
namespace JdeScoping.DataAccess.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Work order operations for JDE Oracle repository.
|
||||
/// </summary>
|
||||
public partial interface IJdeRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets work orders from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming work orders.</returns>
|
||||
IAsyncEnumerable<WorkOrder> GetWorkOrdersAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work orders from archive schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming archived work orders.</returns>
|
||||
IAsyncEnumerable<WorkOrder> GetWorkOrdersArchiveAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order steps from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming work order steps.</returns>
|
||||
IAsyncEnumerable<WorkOrderStep> GetWorkOrderStepsAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order steps from archive schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming archived work order steps.</returns>
|
||||
IAsyncEnumerable<WorkOrderStep> GetWorkOrderStepsArchiveAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order time transactions from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming work order times.</returns>
|
||||
IAsyncEnumerable<WorkOrderTime> GetWorkOrderTimesAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order time transactions from archive schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming archived work order times.</returns>
|
||||
IAsyncEnumerable<WorkOrderTime> GetWorkOrderTimesArchiveAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order routing transactions from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming work order routings.</returns>
|
||||
IAsyncEnumerable<WorkOrderRouting> GetWorkOrderRoutingsAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order component usage from production schema, optionally filtered by last update.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming work order components.</returns>
|
||||
IAsyncEnumerable<WorkOrderComponent> GetWorkOrderComponentsAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order component usage from archive schema.
|
||||
/// </summary>
|
||||
/// <param name="lastUpdateDt">Optional cutoff for incremental sync.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Streaming archived work order components.</returns>
|
||||
IAsyncEnumerable<WorkOrderComponent> GetWorkOrderComponentsArchiveAsync(DateTime? lastUpdateDt = null, CancellationToken ct = default);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for accessing JDE Oracle database.
|
||||
/// All methods return IAsyncEnumerable for memory-efficient streaming.
|
||||
/// </summary>
|
||||
public partial interface IJdeRepository
|
||||
{
|
||||
}
|
||||
@@ -25,11 +25,6 @@ public class DataAccessOptions
|
||||
/// </summary>
|
||||
public int MisDataTimeoutSeconds { get; set; } = 60000;
|
||||
|
||||
/// <summary>
|
||||
/// Timeout for index rebuild operations in seconds.
|
||||
/// </summary>
|
||||
public int RebuildIndexTimeoutSeconds { get; set; } = 600;
|
||||
|
||||
/// <summary>
|
||||
/// JDE production schema name (e.g., PRODDTA).
|
||||
/// </summary>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// SQL query constants for the CMS Oracle database.
|
||||
/// </summary>
|
||||
public static class CmsQueries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets Manufacturing Information System (MIS) data from CMS database.
|
||||
/// Complex 10-table JOIN through CMS schema (INFODBA).
|
||||
/// </summary>
|
||||
public const string SqlGetMisData = @"
|
||||
SELECT DISTINCT
|
||||
mis.P_PART_NUMBER AS ItemNumber,
|
||||
mis.P_OPERATION_NUMBER AS SequenceNumber,
|
||||
item.PITEM_ID AS MISNumber,
|
||||
itemrev.PITEM_REVISION_ID AS RevID,
|
||||
TRIM(mis.P_SITE) AS BranchCode,
|
||||
zim_test_details.P_SEQ_NUMBER AS CharNumber,
|
||||
zim_test_details.P_TEST_DESC AS TestDescription,
|
||||
zim_test_details.P_SAMPL_TYPE AS SamplingType,
|
||||
zim_test_details.P_SAMPL_VALUE AS SamplingValue,
|
||||
zim_test_details.P_TOOLS AS ToolsGauges,
|
||||
zim_test_details.P_WORK_INTR AS WorkInstructions,
|
||||
Status.PNAME AS Status,
|
||||
Status.PDATE_RELEASED AS ReleaseDate
|
||||
FROM INFODBA.PITEM item
|
||||
INNER JOIN INFODBA.PITEMREVISION itemrev ON (item.PUID = itemrev.RITEMS_TAGU)
|
||||
INNER JOIN INFODBA.PRELEASE_STATUS_LIST listing ON (itemrev.PUID = listing.PUID)
|
||||
INNER JOIN INFODBA.PRELEASESTATUS Status ON (listing.PVALU_0 = Status.PUID)
|
||||
INNER JOIN INFODBA.PIMANRELATION imanrel ON (itemrev.PUID = imanrel.RPRIMARY_OBJECTU)
|
||||
INNER JOIN INFODBA.PFORM form ON (imanrel.RSECONDARY_OBJECTU = form.PUID)
|
||||
INNER JOIN INFODBA.PZIMMERMISDETAILS zim_mis ON (form.RDATA_FILEU = zim_mis.PUID)
|
||||
INNER JOIN INFODBA.P_TEST_DETAILS test_details ON (zim_mis.PUID = test_details.PUID)
|
||||
INNER JOIN INFODBA.P_PART_ASSOCIATION ppa ON (ppa.PUID = test_details.PUID)
|
||||
INNER JOIN INFODBA.PMISDATAOBJECT mis ON (mis.PUID = ppa.PVALU_0)
|
||||
INNER JOIN INFODBA.PZIMTESTDETAILS zim_test_details ON (test_details.PVALU_0 = zim_test_details.PUID)
|
||||
WHERE Status.PNAME IN ('Current', 'BackLevel')";
|
||||
|
||||
/// <summary>
|
||||
/// Gets MIS data updated since specified date from CMS database.
|
||||
/// </summary>
|
||||
public const string SqlGetMisDataFiltered = SqlGetMisData + @"
|
||||
AND Status.PDATE_RELEASED >= :lastUpdateDT";
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Inventory related SQL queries (Lots, Lot Usages, Lot Locations, Items)
|
||||
/// </summary>
|
||||
public static partial class JdeQueries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all lots from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLots = @"
|
||||
SELECT TRIM(lot.IOLOTN) AS LotNumber,
|
||||
TRIM(lot.IOMCU) AS BranchCode,
|
||||
lot.IOITM AS ShortItemNumber,
|
||||
TRIM(lot.IOLITM) AS ItemNumber,
|
||||
lot.IOVEND AS SupplierCode,
|
||||
lot.IOLOTS AS StatusCode,
|
||||
TRIM(lot.IOLOT1) AS Memo1,
|
||||
TRIM(lot.IOLOT2) AS Memo2,
|
||||
TRIM(lot.IOLOT3) AS Memo3,
|
||||
lot.IOUPMJ AS LastUpdateDate,
|
||||
lot.IOTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F4108 lot
|
||||
WHERE TRIM(lot.IOLOTN) IS NOT NULL AND
|
||||
TRIM(lot.IOMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets lots updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLotsFiltered = SqlGetLots + @"
|
||||
AND (lot.IOUPMJ > :dateUpdated OR
|
||||
(lot.IOUPMJ = :dateUpdated AND lot.IOTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all lot usages (cardex) from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLotUsages = @"
|
||||
SELECT lu.ILUKID AS UniqueId,
|
||||
lu.ILDOCO AS WorkOrderNumber,
|
||||
TRIM(lu.ILLOTN) AS LotNumber,
|
||||
TRIM(lu.ILMCU) AS BranchCode,
|
||||
lu.ILITM AS ShortItemNumber,
|
||||
lu.ILTRQT AS Quantity,
|
||||
lu.ILTRDJ AS LastUpdateDate,
|
||||
lu.ILTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F4111 lu
|
||||
WHERE lu.ILDCT = 'IM' AND
|
||||
TRIM(lu.ILLOTN) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets lot usages updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLotUsagesFiltered = SqlGetLotUsages + @"
|
||||
AND (lu.ILTRDJ > :dateUpdated OR
|
||||
(lu.ILTRDJ = :dateUpdated AND lu.ILTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all lot usages from archive schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLotUsagesArchive = @"
|
||||
SELECT lu.ILUKID AS UniqueId,
|
||||
lu.ILDOCO AS WorkOrderNumber,
|
||||
TRIM(lu.ILLOTN) AS LotNumber,
|
||||
TRIM(lu.ILMCU) AS BranchCode,
|
||||
lu.ILITM AS ShortItemNumber,
|
||||
lu.ILTRQT AS Quantity,
|
||||
lu.ILTRDJ AS LastUpdateDate,
|
||||
lu.ILTDAY AS LastUpdateTime
|
||||
FROM {ArchiveSchema}.F4111 lu
|
||||
WHERE lu.ILDCT = 'IM' AND
|
||||
TRIM(lu.ILLOTN) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all lot locations from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLotLocations = @"
|
||||
SELECT TRIM(il.LILOTN) AS LotNumber,
|
||||
il.LIITM AS ShortItemNumber,
|
||||
TRIM(il.LIMCU) AS BranchCode,
|
||||
COALESCE(TRIM(il.LILOCN), ' ') AS Location,
|
||||
il.LIUPMJ AS LastUpdateDate,
|
||||
il.LITDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F41021 il
|
||||
WHERE TRIM(il.LILOTN) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets lot locations updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetLotLocationsFiltered = SqlGetLotLocations + @"
|
||||
AND (il.LIUPMJ > :dateUpdated OR
|
||||
(il.LIUPMJ = :dateUpdated AND il.LITDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetItems = @"
|
||||
SELECT pn.IMITM AS ShortItemNumber,
|
||||
TRIM(pn.IMLITM) AS ItemNumber,
|
||||
TRIM(pn.IMDSC1) AS Description,
|
||||
TRIM(pn.IMPRP4) AS PlanningFamily,
|
||||
TRIM(pn.IMSTKT) AS StockingType,
|
||||
pn.IMUPMJ AS LastUpdateDate,
|
||||
pn.IMTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F4101 pn
|
||||
WHERE TRIM(pn.IMLITM) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets items updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetItemsFiltered = SqlGetItems + @"
|
||||
AND (pn.IMUPMJ > :dateUpdated OR
|
||||
(pn.IMUPMJ = :dateUpdated AND pn.IMTDAY >= :timeUpdated))";
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Lookup related SQL queries (Status Codes, Function Codes)
|
||||
/// </summary>
|
||||
public static partial class JdeQueries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all work order status codes from production schema.
|
||||
/// Status codes are stored in UDC table F0005 with SY='00' and RT='SS'.
|
||||
/// </summary>
|
||||
public const string SqlGetStatusCodes = @"
|
||||
SELECT TRIM(sc.DRKY) AS Code,
|
||||
TRIM(sc.DRDL01) AS Description,
|
||||
sc.DRUPMJ AS LastUpdateDate,
|
||||
sc.DRUPMT AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F0005 sc
|
||||
WHERE TRIM(sc.DRSY) = '00' AND
|
||||
sc.DRRT = 'SS' AND
|
||||
TRIM(sc.DRKY) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets status codes updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetStatusCodesFiltered = SqlGetStatusCodes + @"
|
||||
AND (sc.DRUPMJ > :dateUpdated OR
|
||||
(sc.DRUPMJ = :dateUpdated AND sc.DRUPMT >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all function codes from production schema.
|
||||
/// Function codes are stored in F00192 (MES codes table).
|
||||
/// Uses LISTAGG to concatenate multiple descriptions for same code.
|
||||
/// </summary>
|
||||
public const string SqlGetFunctionCodes = @"
|
||||
SELECT Code,
|
||||
TRIM(LISTAGG(Description, ' ') WITHIN GROUP(ORDER BY Description) ||
|
||||
CASE WHEN MAX(total_lengthb) > 4000 THEN '...' ELSE '' END) AS Description,
|
||||
SYSDATE AS LastUpdateDt
|
||||
FROM (
|
||||
SELECT TRIM(fc.CFKY) AS Code,
|
||||
TRIM(ASCIISTR(fc.CFDS80)) AS Description,
|
||||
SUM(LENGTHB(TRIM(fc.CFDS80))+1) OVER(PARTITION BY TRIM(fc.CFKY) ORDER BY TRIM(fc.CFDS80)) - 1 cumul_lengthb,
|
||||
SUM(LENGTHB(TRIM(fc.CFDS80))+1) OVER(PARTITION BY TRIM(fc.CFKY)) - 1 total_lengthb,
|
||||
COUNT(*) OVER(PARTITION BY TRIM(fc.CFKY)) num_values
|
||||
FROM {ProductionSchema}.F00192 fc
|
||||
WHERE TRIM(fc.CFKY) IS NOT NULL
|
||||
)
|
||||
WHERE total_lengthb <= 4000 OR cumul_lengthb <= 4000 - length('...')
|
||||
GROUP BY Code";
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Organization related SQL queries (Users, Business Units, Org Hierarchy, Route Masters)
|
||||
/// </summary>
|
||||
public static partial class JdeQueries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all JDE users from production schema.
|
||||
/// Uses CTE to get unique users with most recent address book record.
|
||||
/// </summary>
|
||||
public const string SqlGetUsers = @"
|
||||
WITH USER_CTE AS (
|
||||
SELECT ab.ABAN8 AS AddressNumber,
|
||||
TRIM(pro.ULUSER) AS UserId,
|
||||
TRIM(ab.ABALPH) AS FullName,
|
||||
ab.ABUPMJ AS LastUpdateDate,
|
||||
ab.ABUPMT AS LastUpdateTime,
|
||||
ROW_NUMBER() OVER (PARTITION BY ab.ABAN8 ORDER BY ab.ABUPMJ DESC, ab.ABUPMT DESC) RN
|
||||
FROM {ProductionSchema}.F0101 ab
|
||||
LEFT OUTER JOIN {ProductionSchema}.F0092 pro ON (ab.ABAN8 = pro.ULAN8)
|
||||
WHERE ab.ABATE = 'Y'
|
||||
)
|
||||
SELECT AddressNumber,
|
||||
UserId,
|
||||
FullName,
|
||||
LastUpdateDate,
|
||||
LastUpdateTime
|
||||
FROM USER_CTE
|
||||
WHERE RN = 1";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all business units of specified type from production schema.
|
||||
/// Type codes: 'WC' = Work Center, 'PC' = Profit Center, 'BR' = Branch
|
||||
/// </summary>
|
||||
public const string SqlGetBusinessUnits = @"
|
||||
SELECT TRIM(wc.MCMCU) AS Code,
|
||||
TRIM(wc.MCDL01) AS Description,
|
||||
wc.MCUPMJ AS LastUpdateDate,
|
||||
wc.MCUPMT AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F0006 wc
|
||||
WHERE wc.MCSTYL = :typeCode";
|
||||
|
||||
/// <summary>
|
||||
/// Gets business units of specified type updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetBusinessUnitsFiltered = SqlGetBusinessUnits + @"
|
||||
AND (wc.MCUPMJ > :dateUpdated OR
|
||||
(wc.MCUPMJ = :dateUpdated AND wc.MCUPMT >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all organization hierarchy records from production schema.
|
||||
/// Maps work centers to profit centers and branches.
|
||||
/// </summary>
|
||||
public const string SqlGetOrgHierarchy = @"
|
||||
SELECT TRIM(oh.IWMCUW) AS ProfitCenterCode,
|
||||
TRIM(oh.IWMCU) AS WorkCenterCode,
|
||||
TRIM(oh.IWMMCU) AS BranchCode,
|
||||
oh.IWUPMJ AS LastUpdateDate,
|
||||
oh.IWTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F30006 oh
|
||||
WHERE TRIM(oh.IWMCU) IS NOT NULL AND
|
||||
TRIM(oh.IWMMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets org hierarchy records updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetOrgHierarchyFiltered = SqlGetOrgHierarchy + @"
|
||||
AND (oh.IWUPMJ > :dateUpdated OR
|
||||
(oh.IWUPMJ = :dateUpdated AND oh.IWTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all route masters from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetRouteMasters = @"
|
||||
SELECT TRIM(rm.IRMMCU) AS BranchCode,
|
||||
TRIM(rm.IRKITL) AS ItemNumber,
|
||||
TRIM(rm.IRTRT) AS RoutingType,
|
||||
rm.IROPSQ / 10.0 AS SequenceNumber,
|
||||
TRIM(rm.IRURRF) AS FunctionCode,
|
||||
TRIM(rm.IRMCU) AS WorkCenterCode,
|
||||
rm.IREFFF AS StartDateDate,
|
||||
rm.IREFFT AS EndDateDate,
|
||||
rm.IRUPMJ AS LastUpdateDate,
|
||||
rm.IRTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F3003 rm
|
||||
WHERE TRIM(rm.IRKITL) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets route masters updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetRouteMastersFiltered = SqlGetRouteMasters + @"
|
||||
AND (rm.IRUPMJ > :dateUpdated OR
|
||||
(rm.IRUPMJ = :dateUpdated AND rm.IRTDAY >= :timeUpdated))";
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Work order related SQL queries (Work Orders, Steps, Times, Routings, Components)
|
||||
/// </summary>
|
||||
public static partial class JdeQueries
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all work orders from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorders = @"
|
||||
SELECT wo.WADOCO AS WorkOrderNumber,
|
||||
TRIM(wo.WAMMCU) AS BranchCode,
|
||||
TRIM(wo.WALOTN) AS LotNumber,
|
||||
TRIM(wo.WALITM) AS ItemNumber,
|
||||
wo.WAITM AS ShortItemNumber,
|
||||
TRIM(wo.WAPARS) AS ParentWorkOrderNumber,
|
||||
wo.WAUORG / 100.0 AS OrderQuantity,
|
||||
wo.WASOBK / 100.0 AS HeldQuantity,
|
||||
wo.WASOQS / 100.0 AS ShippedQuantity,
|
||||
TRIM(wo.WASRST) AS StatusCode,
|
||||
CASE wo.WADCG WHEN 0 THEN TO_DATE('1900-01-01', 'YYYY-MM-DD')
|
||||
ELSE TO_DATE(wo.WADCG+1900000,'YYYYDDD') END AS StatusCodeUpdateDT,
|
||||
CASE wo.WATRDJ WHEN 0 THEN TO_DATE('1900-01-01', 'YYYY-MM-DD')
|
||||
ELSE TO_DATE(wo.WATRDJ+1900000,'YYYYDDD') END AS IssueDate,
|
||||
CASE wo.WASTRT WHEN 0 THEN TO_DATE('1900-01-01', 'YYYY-MM-DD')
|
||||
ELSE TO_DATE(wo.WASTRT+1900000,'YYYYDDD') END AS StartDate,
|
||||
TRIM(wo.WATRT) AS RoutingType,
|
||||
wo.WAUPMJ AS LastUpdateDate,
|
||||
wo.WATDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F4801 wo";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work orders updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkordersFiltered = SqlGetWorkorders + @"
|
||||
WHERE (wo.WAUPMJ > :dateUpdated OR
|
||||
(wo.WAUPMJ = :dateUpdated AND wo.WATDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all work orders from archive schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkordersArchive = @"
|
||||
SELECT wo.WADOCO AS WorkOrderNumber,
|
||||
TRIM(wo.WAMMCU) AS BranchCode,
|
||||
TRIM(wo.WALOTN) AS LotNumber,
|
||||
TRIM(wo.WALITM) AS ItemNumber,
|
||||
wo.WAITM AS ShortItemNumber,
|
||||
TRIM(wo.WAPARS) AS ParentWorkOrderNumber,
|
||||
wo.WAUORG / 100.0 AS OrderQuantity,
|
||||
wo.WASOBK / 100.0 AS HeldQuantity,
|
||||
wo.WASOQS / 100.0 AS ShippedQuantity,
|
||||
TRIM(wo.WASRST) AS StatusCode,
|
||||
CASE wo.WADCG WHEN 0 THEN TO_DATE('1900-01-01', 'YYYY-MM-DD')
|
||||
ELSE TO_DATE(wo.WADCG+1900000,'YYYYDDD') END AS StatusCodeUpdateDT,
|
||||
CASE wo.WATRDJ WHEN 0 THEN TO_DATE('1900-01-01', 'YYYY-MM-DD')
|
||||
ELSE TO_DATE(wo.WATRDJ+1900000,'YYYYDDD') END AS IssueDate,
|
||||
CASE wo.WASTRT WHEN 0 THEN TO_DATE('1900-01-01', 'YYYY-MM-DD')
|
||||
ELSE TO_DATE(wo.WASTRT+1900000,'YYYYDDD') END AS StartDate,
|
||||
TRIM(wo.WATRT) AS RoutingType,
|
||||
wo.WAUPMJ AS LastUpdateDate,
|
||||
wo.WATDAY AS LastUpdateTime
|
||||
FROM {ArchiveSchema}.F4801 wo";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order steps from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderSteps = @"
|
||||
SELECT wos.WLDOCO AS WorkOrderNumber,
|
||||
wos.WLOPSQ/10 AS StepNumber,
|
||||
TRIM(wos.WLMCU) AS WorkCenterCode,
|
||||
TRIM(wos.WLMMCU) AS BranchCode,
|
||||
TRIM(wos.WLDSC1) AS StepDescription,
|
||||
TRIM(mes.CFDS80) AS FunctionOperationDescription,
|
||||
wos.WLOPSC AS StepTypeCode,
|
||||
CASE wos.WLSTRT WHEN 0 THEN NULL
|
||||
ELSE TO_DATE(wos.WLSTRT+1900000,'YYYYDDD') END AS StartDT,
|
||||
CASE wos.WLSTRX WHEN 0 THEN NULL
|
||||
ELSE TO_DATE(wos.WLSTRX+1900000,'YYYYDDD') END AS EndDT,
|
||||
TRIM(wos.WLURRF) AS FunctionCode,
|
||||
wos.WLSOCN / 100.0 AS ScrappedQuantity,
|
||||
wos.WLUPMJ AS LastUpdateDate,
|
||||
wos.WLTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F3112 wos
|
||||
LEFT OUTER JOIN {ProductionSchema}.F00192 mes ON (wos.WLURRF = mes.CFKY)
|
||||
WHERE TRIM(wos.WLMCU) IS NOT NULL AND
|
||||
TRIM(wos.WLMMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order steps updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderStepsFiltered = SqlGetWorkorderSteps + @"
|
||||
AND (wos.WLUPMJ > :dateUpdated OR
|
||||
(wos.WLUPMJ = :dateUpdated AND wos.WLTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order steps from archive schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderStepsArchive = @"
|
||||
SELECT wos.WLDOCO AS WorkOrderNumber,
|
||||
wos.WLOPSQ/10 AS StepNumber,
|
||||
TRIM(wos.WLMCU) AS WorkCenterCode,
|
||||
TRIM(wos.WLMMCU) AS BranchCode,
|
||||
TRIM(wos.WLDSC1) AS StepDescription,
|
||||
TRIM(mes.CFDS80) AS FunctionOperationDescription,
|
||||
wos.WLOPSC AS StepTypeCode,
|
||||
CASE wos.WLSTRT WHEN 0 THEN NULL
|
||||
ELSE TO_DATE(wos.WLSTRT+1900000,'YYYYDDD') END AS StartDT,
|
||||
CASE wos.WLSTRX WHEN 0 THEN NULL
|
||||
ELSE TO_DATE(wos.WLSTRX+1900000,'YYYYDDD') END AS EndDT,
|
||||
TRIM(wos.WLURRF) AS FunctionCode,
|
||||
wos.WLSOCN / 100.0 AS ScrappedQuantity,
|
||||
wos.WLUPMJ AS LastUpdateDate,
|
||||
wos.WLTDAY AS LastUpdateTime
|
||||
FROM {ArchiveSchema}.F3112 wos
|
||||
LEFT OUTER JOIN {ProductionSchema}.F00192 mes ON (wos.WLURRF = mes.CFKY)
|
||||
WHERE TRIM(wos.WLMCU) IS NOT NULL AND
|
||||
TRIM(wos.WLMMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order time transactions from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderTimes = @"
|
||||
SELECT wot.WTUKID AS UniqueID,
|
||||
wot.WTDOCO AS WorkOrderNumber,
|
||||
wot.WTOPSQ/10 AS StepNumber,
|
||||
TRIM(wot.WTMCU) AS WorkCenterCode,
|
||||
TRIM(wot.WTMMCU) AS BranchCode,
|
||||
wot.WTAN8 AS AddressNumber,
|
||||
CASE wot.WTDGL WHEN 0 THEN NULL
|
||||
ELSE TO_DATE(wot.WTDGL+1900000,'YYYYDDD') END AS GlDate,
|
||||
wot.WTUPMJ AS LastUpdateDate,
|
||||
wot.WTTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F31122 wot
|
||||
WHERE TRIM(wot.WTMCU) IS NOT NULL AND
|
||||
TRIM(wot.WTMMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order times updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderTimesFiltered = SqlGetWorkorderTimes + @"
|
||||
AND (wot.WTUPMJ > :dateUpdated OR
|
||||
(wot.WTUPMJ = :dateUpdated AND wot.WTTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order time transactions from archive schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderTimesArchive = @"
|
||||
SELECT wot.WTUKID AS UniqueID,
|
||||
wot.WTDOCO AS WorkOrderNumber,
|
||||
wot.WTOPSQ/10 AS StepNumber,
|
||||
TRIM(wot.WTMCU) AS WorkCenterCode,
|
||||
TRIM(wot.WTMMCU) AS BranchCode,
|
||||
wot.WTAN8 AS AddressNumber,
|
||||
CASE wot.WTDGL WHEN 0 THEN NULL
|
||||
ELSE TO_DATE(wot.WTDGL+1900000,'YYYYDDD') END AS GlDate,
|
||||
wot.WTUPMJ AS LastUpdateDate,
|
||||
wot.WTTDAY AS LastUpdateTime
|
||||
FROM {ArchiveSchema}.F31122 wot
|
||||
WHERE TRIM(wot.WTMCU) IS NOT NULL AND
|
||||
TRIM(wot.WTMMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order routing transactions from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderRoutings = @"
|
||||
SELECT TRIM(woz.SZEDUS) AS UserID,
|
||||
TRIM(woz.SZEDBT) AS BatchNumber,
|
||||
TRIM(woz.SZEDTN) AS TransactionNumber,
|
||||
woz.SZEDLN AS LineNumber,
|
||||
woz.SZOPSQ / 10.0 AS StepNumber,
|
||||
TRIM(woz.SZMCU) AS WorkCenterCode,
|
||||
woz.SZDOCO AS WorkOrderNumber,
|
||||
TRIM(woz.SZTRT) AS RoutingType,
|
||||
TRIM(woz.SZMMCU) AS BranchCode,
|
||||
TRIM(woz.SZDSC1) AS StepDescription,
|
||||
TRIM(woz.SZURRF) AS FunctionCode,
|
||||
woz.SZTRDJ AS TransactionDate_Date,
|
||||
woz.SZUPMJ AS LastUpdateDate,
|
||||
woz.SZTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F3112Z1 woz
|
||||
WHERE woz.SZTYTN = 'JDERTG' AND
|
||||
woz.SZDRIN = '2' AND
|
||||
woz.SZTNAC = '02' AND
|
||||
woz.SZPID = 'ER31410' AND
|
||||
TRIM(woz.SZEDUS) IS NOT NULL AND
|
||||
TRIM(woz.SZEDBT) IS NOT NULL AND
|
||||
TRIM(woz.SZEDTN) IS NOT NULL AND
|
||||
TRIM(woz.SZMCU) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order routings updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderRoutingsFiltered = SqlGetWorkorderRoutings + @"
|
||||
AND (woz.SZUPMJ > :dateUpdated OR
|
||||
(woz.SZUPMJ = :dateUpdated AND woz.SZTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order component usage from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderComponents = @"
|
||||
SELECT woc.WMUKID AS UniqueID,
|
||||
woc.WMDOCO AS WorkOrderNumber,
|
||||
TRIM(woc.WMLOTN) AS LotNumber,
|
||||
TRIM(woc.WMCMCU) AS BranchCode,
|
||||
woc.WMCPIT AS ShortItemNumber,
|
||||
woc.WMTRQT / 100.0 AS Quantity,
|
||||
woc.WMUPMJ AS LastUpdateDate,
|
||||
woc.WMTDAY AS LastUpdateTime
|
||||
FROM {ProductionSchema}.F3111 woc
|
||||
WHERE TRIM(woc.WMLOTN) IS NOT NULL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order components updated since specified date from production schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderComponentsFiltered = SqlGetWorkorderComponents + @"
|
||||
AND (woc.WMUPMJ > :dateUpdated OR
|
||||
(woc.WMUPMJ = :dateUpdated AND woc.WMTDAY >= :timeUpdated))";
|
||||
|
||||
/// <summary>
|
||||
/// Gets work order component usage from archive schema.
|
||||
/// </summary>
|
||||
public const string SqlGetWorkorderComponentsArchive = @"
|
||||
SELECT woc.WMUKID AS UniqueID,
|
||||
woc.WMDOCO AS WorkOrderNumber,
|
||||
TRIM(woc.WMLOTN) AS LotNumber,
|
||||
TRIM(woc.WMCMCU) AS BranchCode,
|
||||
woc.WMCPIT AS ShortItemNumber,
|
||||
woc.WMTRQT / 100.0 AS Quantity,
|
||||
woc.WMUPMJ AS LastUpdateDate,
|
||||
woc.WMTDAY AS LastUpdateTime
|
||||
FROM {ArchiveSchema}.F3111 woc
|
||||
WHERE TRIM(woc.WMLOTN) IS NOT NULL";
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace JdeScoping.DataAccess.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// SQL query constants for the JDE Oracle database.
|
||||
/// Schema placeholders ({ProductionSchema}, {ArchiveSchema}, {StageSchema}) are replaced at runtime.
|
||||
/// </summary>
|
||||
public static partial class JdeQueries
|
||||
{
|
||||
}
|
||||
@@ -24,88 +24,4 @@ public static partial class LotFinderQueries
|
||||
cte.NumberRecords
|
||||
FROM DU_CTE cte
|
||||
WHERE cte.RN = 1";
|
||||
|
||||
/// <summary>
|
||||
/// Gets column metadata for a table.
|
||||
/// </summary>
|
||||
public const string SqlGetTableColumns = @"
|
||||
SELECT c.name AS Name,
|
||||
CASE t2.name
|
||||
WHEN 'varchar' THEN 'VARCHAR(' + CAST(c.max_length AS VARCHAR(10)) + ')'
|
||||
WHEN 'decimal' THEN 'DECIMAL(' + CAST(c.precision AS VARCHAR(4)) + ',' + CAST(c.scale AS VARCHAR(4)) + ')'
|
||||
ELSE UPPER(t2.name)
|
||||
END AS Definition
|
||||
FROM sys.columns c
|
||||
INNER JOIN sys.types AS t2 ON (c.system_type_id = t2.system_type_id)
|
||||
INNER JOIN sys.tables t ON (c.object_id = t.object_id)
|
||||
WHERE t.name = @name
|
||||
ORDER BY c.column_id";
|
||||
|
||||
/// <summary>
|
||||
/// Gets primary key columns for a table.
|
||||
/// </summary>
|
||||
public const string SqlGetTablePrimaryKey = @"
|
||||
SELECT COLUMN_NAME AS Name
|
||||
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1 AND
|
||||
TABLE_NAME = @name
|
||||
ORDER BY ORDINAL_POSITION";
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds all indices on a table. Use string.Format to inject table name.
|
||||
/// </summary>
|
||||
public const string SqlRebuildIndices = "ALTER INDEX ALL ON {0} REBUILD WITH (FILLFACTOR = 95);";
|
||||
|
||||
/// <summary>
|
||||
/// Post-processing script to set MIS data obsoletion dates.
|
||||
/// </summary>
|
||||
public const string SqlPostprocessMisData = @"
|
||||
SET ANSI_WARNINGS OFF;
|
||||
|
||||
WITH cte AS (
|
||||
SELECT md.MisNumber, md.RevID, md.Status, MIN(md.ReleaseDate) Released
|
||||
FROM dbo.MisData AS md
|
||||
GROUP BY md.MisNumber, md.RevID, md.Status
|
||||
)
|
||||
UPDATE dbo.MisData
|
||||
SET ObsoleteDate = bl.Released
|
||||
FROM cte bl
|
||||
WHERE MisData.MisNumber = bl.MisNumber AND
|
||||
MisData.RevID = bl.RevID AND
|
||||
MisData.Status = 'Current' AND
|
||||
bl.Status = 'BackLevel';
|
||||
|
||||
WITH cte AS (
|
||||
SELECT md.MisNumber, md.RevID, md.Status, MIN(md.ReleaseDate) Released
|
||||
FROM dbo.MisData AS md
|
||||
GROUP BY md.MisNumber, md.RevID, md.Status
|
||||
)
|
||||
UPDATE dbo.MisData
|
||||
SET ObsoleteDate = (SELECT TOP 1 nl.Released
|
||||
FROM cte nl
|
||||
WHERE MisData.MisNumber = nl.MisNumber AND
|
||||
MisData.RevID < nl.RevID AND
|
||||
MisData.Status = nl.Status
|
||||
ORDER BY nl.RevID)
|
||||
WHERE ObsoleteDate IS NULL;
|
||||
|
||||
ALTER INDEX [PK_MisData] ON [dbo].[MisData] REBUILD;";
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new data update tracking record.
|
||||
/// </summary>
|
||||
public const string SqlInsertDataUpdate = @"
|
||||
INSERT INTO dbo.DataUpdate (SourceSystem, SourceData, TableName, StartDT, UpdateType)
|
||||
VALUES (@sourceSystem, @sourceData, @tableName, @startDT, @updateType);
|
||||
SELECT CAST(SCOPE_IDENTITY() AS BIGINT);";
|
||||
|
||||
/// <summary>
|
||||
/// Completes a data update tracking record.
|
||||
/// </summary>
|
||||
public const string SqlCompleteDataUpdate = @"
|
||||
UPDATE dbo.DataUpdate
|
||||
SET EndDT = @endDT,
|
||||
WasSuccessful = @wasSuccessful,
|
||||
NumberRecords = @numberRecords
|
||||
WHERE ID = @id";
|
||||
}
|
||||
|
||||
@@ -52,17 +52,6 @@ public static partial class LotFinderQueries
|
||||
wc.Description LIKE '%' + @filter + '%'
|
||||
ORDER BY wc.Code";
|
||||
|
||||
/// <summary>
|
||||
/// Looks up work centers by codes using STRING_SPLIT.
|
||||
/// </summary>
|
||||
public const string SqlLookupWorkCenters = @"
|
||||
SELECT wc.Code,
|
||||
wc.Description,
|
||||
wc.LastUpdateDT
|
||||
FROM dbo.WorkCenter AS wc
|
||||
WHERE wc.Code IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@workCenterCodes, ','))
|
||||
ORDER BY wc.Code";
|
||||
|
||||
/// <summary>
|
||||
/// Searches profit centers by code or description.
|
||||
/// </summary>
|
||||
@@ -76,17 +65,6 @@ public static partial class LotFinderQueries
|
||||
pc.Description LIKE '%' + @filter + '%'
|
||||
ORDER BY pc.Code";
|
||||
|
||||
/// <summary>
|
||||
/// Looks up profit centers by codes using STRING_SPLIT.
|
||||
/// </summary>
|
||||
public const string SqlLookupProfitCenters = @"
|
||||
SELECT pc.Code,
|
||||
pc.Description,
|
||||
pc.LastUpdateDT
|
||||
FROM dbo.ProfitCenter AS pc
|
||||
WHERE pc.Code IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@profitCenterCodes, ','))
|
||||
ORDER BY pc.Code";
|
||||
|
||||
/// <summary>
|
||||
/// Searches users by user ID, full name, or address number.
|
||||
/// </summary>
|
||||
@@ -102,19 +80,6 @@ public static partial class LotFinderQueries
|
||||
CAST(u.AddressNumber AS VARCHAR(10)) LIKE '%' + @filter + '%'
|
||||
ORDER BY u.UserID, u.FullName";
|
||||
|
||||
/// <summary>
|
||||
/// Looks up users by user IDs or address numbers using STRING_SPLIT.
|
||||
/// </summary>
|
||||
public const string SqlLookupUsers = @"
|
||||
SELECT u.AddressNumber,
|
||||
u.UserID,
|
||||
u.FullName,
|
||||
u.LastUpdateDT
|
||||
FROM dbo.JdeUser AS u
|
||||
WHERE u.UserID IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@userIds, ','))
|
||||
OR CAST(u.AddressNumber AS VARCHAR(20)) IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@userIds, ','))
|
||||
ORDER BY u.UserID";
|
||||
|
||||
/// <summary>
|
||||
/// Looks up lots by lot number and item number using OPENJSON.
|
||||
/// </summary>
|
||||
|
||||
@@ -55,22 +55,4 @@ public static partial class LotFinderQueries
|
||||
SELECT s.Results
|
||||
FROM dbo.Search AS s
|
||||
WHERE s.ID = @id";
|
||||
|
||||
/// <summary>
|
||||
/// Updates search status.
|
||||
/// </summary>
|
||||
public const string SqlUpdateSearchStatus = @"
|
||||
UPDATE dbo.Search
|
||||
SET Status = @status,
|
||||
StartDT = CASE WHEN @status = 2 THEN GETUTCDATE() ELSE StartDT END,
|
||||
EndDT = CASE WHEN @status >= 3 THEN GETUTCDATE() ELSE EndDT END
|
||||
WHERE ID = @id";
|
||||
|
||||
/// <summary>
|
||||
/// Updates search results.
|
||||
/// </summary>
|
||||
public const string SqlUpdateSearchResults = @"
|
||||
UPDATE dbo.Search
|
||||
SET Results = @results
|
||||
WHERE ID = @id";
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dapper;
|
||||
using JdeScoping.Core.Models;
|
||||
using JdeScoping.Core.Models.Quality;
|
||||
using JdeScoping.DataAccess.Options;
|
||||
using JdeScoping.DataAccess.Interfaces;
|
||||
using JdeScoping.DataAccess.Queries;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
|
||||
namespace JdeScoping.DataAccess.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository implementation for the CMS Oracle database.
|
||||
/// </summary>
|
||||
public class CmsRepository : ICmsRepository
|
||||
{
|
||||
private readonly IDbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<CmsRepository> _logger;
|
||||
private readonly IOptions<DataAccessOptions> _options;
|
||||
private const string RepositoryName = "CmsRepository";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CmsRepository"/> class.
|
||||
/// </summary>
|
||||
public CmsRepository(
|
||||
IDbConnectionFactory connectionFactory,
|
||||
ILogger<CmsRepository> logger,
|
||||
IOptions<DataAccessOptions> options)
|
||||
{
|
||||
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<MisData> GetMisDataAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = lastUpdateDt.HasValue
|
||||
? CmsQueries.SqlGetMisDataFiltered
|
||||
: CmsQueries.SqlGetMisData;
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { lastUpdateDT = lastUpdateDt.Value }
|
||||
: null;
|
||||
|
||||
OracleConnection? connection = null;
|
||||
try
|
||||
{
|
||||
connection = await _connectionFactory.CreateCmsConnectionAsync(ct);
|
||||
|
||||
// Use Query with buffered: false for streaming
|
||||
var results = connection.Query<MisData>(
|
||||
sql,
|
||||
parameters,
|
||||
commandTimeout: _options.Value.MisDataTimeoutSeconds,
|
||||
buffered: false);
|
||||
|
||||
foreach (var item in results)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
// Convert ReleaseDate to local time if present
|
||||
if (item.ReleaseDate.HasValue)
|
||||
{
|
||||
item.ReleaseDate = item.ReleaseDate.Value.ToLocalTime();
|
||||
}
|
||||
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using JdeScoping.Core.Helpers;
|
||||
using JdeScoping.Core.Models.Inventory;
|
||||
using JdeScoping.DataAccess.Queries;
|
||||
|
||||
namespace JdeScoping.DataAccess.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Inventory (lots) operations for JDE Oracle repository.
|
||||
/// </summary>
|
||||
public partial class JdeRepository
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<Lot> GetLotsAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetLotsFiltered
|
||||
: JdeQueries.SqlGetLots);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<Lot>(
|
||||
sql, parameters, nameof(GetLotsAsync), "SQL_GET_LOTS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<LotUsage> GetLotUsagesAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetLotUsagesFiltered
|
||||
: JdeQueries.SqlGetLotUsages);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
// Use special lot usage timeout due to large dataset
|
||||
await foreach (var item in StreamQueryAsync<LotUsage>(
|
||||
sql, parameters, nameof(GetLotUsagesAsync), "SQL_GET_LOT_USAGES", ct,
|
||||
_options.Value.LotUsageTimeoutSeconds))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<LotUsage> GetLotUsagesArchiveAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetLotUsagesArchive);
|
||||
|
||||
// Use special lot usage timeout due to large dataset
|
||||
await foreach (var item in StreamQueryAsync<LotUsage>(
|
||||
sql, null, nameof(GetLotUsagesArchiveAsync), "SQL_GET_LOT_USAGES_ARCHIVE", ct,
|
||||
_options.Value.LotUsageTimeoutSeconds))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<LotLocation> GetLotLocationsAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetLotLocationsFiltered
|
||||
: JdeQueries.SqlGetLotLocations);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { lastUpdateDT = lastUpdateDt.Value }
|
||||
: null;
|
||||
|
||||
// Use JDE Stage connection for lot locations
|
||||
await foreach (var item in StreamQueryFromStageAsync<LotLocation>(
|
||||
sql, parameters, nameof(GetLotLocationsAsync), "SQL_GET_LOT_LOCATIONS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using JdeScoping.Core.Helpers;
|
||||
using JdeScoping.Core.Models;
|
||||
using JdeScoping.Core.Models.Inventory;
|
||||
using JdeScoping.Core.Models.Lookup;
|
||||
using JdeScoping.Core.Models.Organization;
|
||||
using JdeScoping.DataAccess.Queries;
|
||||
|
||||
namespace JdeScoping.DataAccess.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Reference data operations for JDE Oracle repository.
|
||||
/// </summary>
|
||||
public partial class JdeRepository
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<Item> GetItemsAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetItemsFiltered
|
||||
: JdeQueries.SqlGetItems);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<Item>(
|
||||
sql, parameters, nameof(GetItemsAsync), "SQL_GET_ITEMS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<JdeUser> GetUsersAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
// Users always do full sync (incremental not supported)
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetUsers);
|
||||
|
||||
await foreach (var item in StreamQueryAsync<JdeUser>(
|
||||
sql, null, nameof(GetUsersAsync), "SQL_GET_USERS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<Branch> GetBranchesAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetBusinessUnitsFiltered
|
||||
: JdeQueries.SqlGetBusinessUnits);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { typeCode = "BP", dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: new { typeCode = "BP", dateUpdated = 0, timeUpdated = 0 };
|
||||
|
||||
await foreach (var item in StreamQueryAsync<Branch>(
|
||||
sql, parameters, nameof(GetBranchesAsync), "SQL_GET_BUSINESS_UNITS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<ProfitCenter> GetProfitCentersAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetBusinessUnitsFiltered
|
||||
: JdeQueries.SqlGetBusinessUnits);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { typeCode = "I3", dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: new { typeCode = "I3", dateUpdated = 0, timeUpdated = 0 };
|
||||
|
||||
await foreach (var item in StreamQueryAsync<ProfitCenter>(
|
||||
sql, parameters, nameof(GetProfitCentersAsync), "SQL_GET_BUSINESS_UNITS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkCenter> GetWorkCentersAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetBusinessUnitsFiltered
|
||||
: JdeQueries.SqlGetBusinessUnits);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { typeCode = "WC", dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: new { typeCode = "WC", dateUpdated = 0, timeUpdated = 0 };
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkCenter>(
|
||||
sql, parameters, nameof(GetWorkCentersAsync), "SQL_GET_BUSINESS_UNITS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<StatusCode> GetStatusCodesAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetStatusCodesFiltered
|
||||
: JdeQueries.SqlGetStatusCodes);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { lastUpdateDT = lastUpdateDt.Value }
|
||||
: null;
|
||||
|
||||
// Use JDE Stage connection for status codes
|
||||
await foreach (var item in StreamQueryFromStageAsync<StatusCode>(
|
||||
sql, parameters, nameof(GetStatusCodesAsync), "SQL_GET_STATUS_CODES", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<FunctionCode> GetFunctionCodesAsync(
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetFunctionCodes);
|
||||
|
||||
await foreach (var item in StreamQueryAsync<FunctionCode>(
|
||||
sql, null, nameof(GetFunctionCodesAsync), "SQL_GET_FUNCTION_CODES", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<OrgHierarchy> GetOrgHierarchyAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetOrgHierarchyFiltered
|
||||
: JdeQueries.SqlGetOrgHierarchy);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<OrgHierarchy>(
|
||||
sql, parameters, nameof(GetOrgHierarchyAsync), "SQL_GET_ORG_HIERARCHY", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<RouteMaster> GetRouteMastersAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetRouteMastersFiltered
|
||||
: JdeQueries.SqlGetRouteMasters);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<RouteMaster>(
|
||||
sql, parameters, nameof(GetRouteMastersAsync), "SQL_GET_ROUTE_MASTERS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using JdeScoping.Core.Helpers;
|
||||
using JdeScoping.Core.Models.WorkOrders;
|
||||
using JdeScoping.DataAccess.Queries;
|
||||
|
||||
namespace JdeScoping.DataAccess.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Work order operations for JDE Oracle repository.
|
||||
/// </summary>
|
||||
public partial class JdeRepository
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrder> GetWorkOrdersAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetWorkordersFiltered
|
||||
: JdeQueries.SqlGetWorkorders);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrder>(
|
||||
sql, parameters, nameof(GetWorkOrdersAsync), "SQL_GET_WORKORDERS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrder> GetWorkOrdersArchiveAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetWorkordersArchive);
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrder>(
|
||||
sql, null, nameof(GetWorkOrdersArchiveAsync), "SQL_GET_WORKORDERS_ARCHIVE", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderStep> GetWorkOrderStepsAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetWorkorderStepsFiltered
|
||||
: JdeQueries.SqlGetWorkorderSteps);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderStep>(
|
||||
sql, parameters, nameof(GetWorkOrderStepsAsync), "SQL_GET_WORKORDER_STEPS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderStep> GetWorkOrderStepsArchiveAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetWorkorderStepsArchive);
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderStep>(
|
||||
sql, null, nameof(GetWorkOrderStepsArchiveAsync), "SQL_GET_WORKORDER_STEPS_ARCHIVE", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderTime> GetWorkOrderTimesAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetWorkorderTimesFiltered
|
||||
: JdeQueries.SqlGetWorkorderTimes);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderTime>(
|
||||
sql, parameters, nameof(GetWorkOrderTimesAsync), "SQL_GET_WORKORDER_TIMES", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderTime> GetWorkOrderTimesArchiveAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetWorkorderTimesArchive);
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderTime>(
|
||||
sql, null, nameof(GetWorkOrderTimesArchiveAsync), "SQL_GET_WORKORDER_TIMES_ARCHIVE", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderRouting> GetWorkOrderRoutingsAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetWorkorderRoutingsFiltered
|
||||
: JdeQueries.SqlGetWorkorderRoutings);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderRouting>(
|
||||
sql, parameters, nameof(GetWorkOrderRoutingsAsync), "SQL_GET_WORKORDER_ROUTINGS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderComponent> GetWorkOrderComponentsAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(
|
||||
lastUpdateDt.HasValue
|
||||
? JdeQueries.SqlGetWorkorderComponentsFiltered
|
||||
: JdeQueries.SqlGetWorkorderComponents);
|
||||
|
||||
var parameters = lastUpdateDt.HasValue
|
||||
? new { dateUpdated = lastUpdateDt.Value.ToJdeDate(), timeUpdated = lastUpdateDt.Value.ToJdeTime() }
|
||||
: null;
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderComponent>(
|
||||
sql, parameters, nameof(GetWorkOrderComponentsAsync), "SQL_GET_WORKORDER_COMPONENTS", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<WorkOrderComponent> GetWorkOrderComponentsArchiveAsync(
|
||||
DateTime? lastUpdateDt = null,
|
||||
[EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
var sql = ApplySchemaReplacements(JdeQueries.SqlGetWorkorderComponentsArchive);
|
||||
|
||||
await foreach (var item in StreamQueryAsync<WorkOrderComponent>(
|
||||
sql, null, nameof(GetWorkOrderComponentsArchiveAsync), "SQL_GET_WORKORDER_COMPONENTS_ARCHIVE", ct))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dapper;
|
||||
using JdeScoping.DataAccess.Options;
|
||||
using JdeScoping.DataAccess.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Oracle.ManagedDataAccess.Client;
|
||||
|
||||
namespace JdeScoping.DataAccess.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository implementation for the JDE Oracle database.
|
||||
/// </summary>
|
||||
public partial class JdeRepository : IJdeRepository
|
||||
{
|
||||
private readonly IDbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<JdeRepository> _logger;
|
||||
private readonly IOptions<DataAccessOptions> _options;
|
||||
private const string RepositoryName = "JdeRepository";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JdeRepository"/> class.
|
||||
/// </summary>
|
||||
public JdeRepository(
|
||||
IDbConnectionFactory connectionFactory,
|
||||
ILogger<JdeRepository> logger,
|
||||
IOptions<DataAccessOptions> options)
|
||||
{
|
||||
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
private string ApplySchemaReplacements(string sql)
|
||||
{
|
||||
return sql
|
||||
.Replace("{ProductionSchema}", _options.Value.ProductionSchema)
|
||||
.Replace("{ArchiveSchema}", _options.Value.ArchiveSchema)
|
||||
.Replace("{StageSchema}", _options.Value.StageSchema);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<T> StreamQueryAsync<T>(
|
||||
string sql,
|
||||
object? parameters,
|
||||
string operation,
|
||||
string queryName,
|
||||
[EnumeratorCancellation] CancellationToken ct,
|
||||
int? timeoutSeconds = null)
|
||||
{
|
||||
OracleConnection? connection = null;
|
||||
try
|
||||
{
|
||||
connection = await _connectionFactory.CreateJdeConnectionAsync(ct);
|
||||
var timeout = timeoutSeconds ?? _options.Value.DefaultTimeoutSeconds;
|
||||
|
||||
// Use Query with buffered: false for streaming
|
||||
var results = connection.Query<T>(
|
||||
sql,
|
||||
parameters,
|
||||
commandTimeout: timeout,
|
||||
buffered: false);
|
||||
|
||||
foreach (var item in results)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<T> StreamQueryFromStageAsync<T>(
|
||||
string sql,
|
||||
object? parameters,
|
||||
string operation,
|
||||
string queryName,
|
||||
[EnumeratorCancellation] CancellationToken ct,
|
||||
int? timeoutSeconds = null)
|
||||
{
|
||||
OracleConnection? connection = null;
|
||||
try
|
||||
{
|
||||
connection = await _connectionFactory.CreateJdeStageConnectionAsync(ct);
|
||||
var timeout = timeoutSeconds ?? _options.Value.DefaultTimeoutSeconds;
|
||||
|
||||
// Use Query with buffered: false for streaming
|
||||
var results = connection.Query<T>(
|
||||
sql,
|
||||
parameters,
|
||||
commandTimeout: timeout,
|
||||
buffered: false);
|
||||
|
||||
foreach (var item in results)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using JdeScoping.Core.Models.Infrastructure;
|
||||
using JdeScoping.DataAccess.Queries;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
namespace JdeScoping.DataAccess.Repositories;
|
||||
|
||||
@@ -33,166 +31,4 @@ public partial class LotFinderRepository
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TableSpec> GetTableSpecAsync(string tableName, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(GetTableSpecAsync);
|
||||
try
|
||||
{
|
||||
var tableSpec = new TableSpec(tableName);
|
||||
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
|
||||
// Load columns
|
||||
var columns = await connection.QueryAsync<ColumnSpec>(
|
||||
LotFinderQueries.SqlGetTableColumns,
|
||||
new { name = tableName },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
tableSpec.Columns.AddRange(columns);
|
||||
|
||||
// Load primary key
|
||||
var pkColumns = await connection.QueryAsync<string>(
|
||||
LotFinderQueries.SqlGetTablePrimaryKey,
|
||||
new { name = tableName },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
foreach (var columnName in pkColumns)
|
||||
{
|
||||
var column = tableSpec.GetColumn(columnName);
|
||||
if (column != null)
|
||||
{
|
||||
tableSpec.PrimaryKey.Add(column);
|
||||
}
|
||||
}
|
||||
|
||||
return tableSpec;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_GET_TABLE_COLUMNS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task RebuildIndicesAsync(string tableName, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(RebuildIndicesAsync);
|
||||
|
||||
// Validate table name against whitelist (SQL injection prevention)
|
||||
if (!ValidTableNames.Contains(tableName))
|
||||
{
|
||||
throw new ArgumentException($"Invalid table name: {tableName}", nameof(tableName));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
var sql = $"ALTER INDEX ALL ON [{tableName}] REBUILD WITH (FILLFACTOR = 95)";
|
||||
await connection.ExecuteAsync(sql, commandTimeout: _options.Value.RebuildIndexTimeoutSeconds);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_REBUILD_INDICES");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task PostProcessMisDataAsync(CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(PostProcessMisDataAsync);
|
||||
try
|
||||
{
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
await connection.ExecuteAsync(
|
||||
LotFinderQueries.SqlPostprocessMisData,
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_POSTPROCESS_MISDATA");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> BulkInsertAsync<T>(string tableName, IEnumerable<T> records, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(BulkInsertAsync);
|
||||
|
||||
// Validate table name against whitelist
|
||||
if (!ValidTableNames.Contains(tableName))
|
||||
{
|
||||
throw new ArgumentException($"Invalid table name: {tableName}", nameof(tableName));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
|
||||
// Use SqlBulkCopy for efficient bulk insert
|
||||
using var bulkCopy = new SqlBulkCopy(connection)
|
||||
{
|
||||
DestinationTableName = $"dbo.[{tableName}]",
|
||||
BulkCopyTimeout = _options.Value.DefaultTimeoutSeconds
|
||||
};
|
||||
|
||||
// Convert records to DataTable
|
||||
var recordList = records.ToList();
|
||||
var dataTable = ToDataTable(recordList);
|
||||
|
||||
await bulkCopy.WriteToServerAsync(dataTable, ct);
|
||||
return recordList.Count;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "BulkInsert");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task TruncateTableAsync(string tableName, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(TruncateTableAsync);
|
||||
|
||||
// Validate table name against whitelist
|
||||
if (!ValidTableNames.Contains(tableName))
|
||||
{
|
||||
throw new ArgumentException($"Invalid table name: {tableName}", nameof(tableName));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
var sql = $"TRUNCATE TABLE dbo.[{tableName}]";
|
||||
await connection.ExecuteAsync(sql, commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "TruncateTable");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,32 +113,6 @@ public partial class LotFinderRepository
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<WorkCenter>> LookupWorkCentersAsync(List<string> codes, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(LookupWorkCentersAsync);
|
||||
try
|
||||
{
|
||||
var workCenterCodesCsv = string.Join(",", codes);
|
||||
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
var result = await connection.QueryAsync<WorkCenter>(
|
||||
LotFinderQueries.SqlLookupWorkCenters,
|
||||
new { workCenterCodes = workCenterCodesCsv },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
return result.ToList();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_LOOKUP_WORK_CENTERS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<ProfitCenter>> SearchProfitCentersAsync(string filter, CancellationToken ct = default)
|
||||
{
|
||||
@@ -163,32 +137,6 @@ public partial class LotFinderRepository
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<ProfitCenter>> LookupProfitCentersAsync(List<string> codes, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(LookupProfitCentersAsync);
|
||||
try
|
||||
{
|
||||
var profitCenterCodesCsv = string.Join(",", codes);
|
||||
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
var result = await connection.QueryAsync<ProfitCenter>(
|
||||
LotFinderQueries.SqlLookupProfitCenters,
|
||||
new { profitCenterCodes = profitCenterCodesCsv },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
return result.ToList();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_LOOKUP_PROFIT_CENTERS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<JdeUser>> SearchUsersAsync(string filter, CancellationToken ct = default)
|
||||
{
|
||||
@@ -213,32 +161,6 @@ public partial class LotFinderRepository
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<JdeUser>> LookupUsersAsync(List<string> userIds, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(LookupUsersAsync);
|
||||
try
|
||||
{
|
||||
var userIdsCsv = string.Join(",", userIds);
|
||||
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
var result = await connection.QueryAsync<JdeUser>(
|
||||
LotFinderQueries.SqlLookupUsers,
|
||||
new { userIds = userIdsCsv },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
return result.ToList();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_LOOKUP_USERS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<Lot>> LookupLotsAsync(List<LotViewModel> lots, CancellationToken ct = default)
|
||||
{
|
||||
|
||||
@@ -152,49 +152,4 @@ public partial class LotFinderRepository
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task UpdateSearchStatusAsync(int id, SearchStatus status, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(UpdateSearchStatusAsync);
|
||||
try
|
||||
{
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
await connection.ExecuteAsync(
|
||||
LotFinderQueries.SqlUpdateSearchStatus,
|
||||
new { id, status = (int)status },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_UPDATE_SEARCH_STATUS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task UpdateSearchResultsAsync(int id, byte[] results, CancellationToken ct = default)
|
||||
{
|
||||
const string operation = nameof(UpdateSearchResultsAsync);
|
||||
try
|
||||
{
|
||||
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
|
||||
await connection.ExecuteAsync(
|
||||
LotFinderQueries.SqlUpdateSearchResults,
|
||||
new { id, results },
|
||||
commandTimeout: _options.Value.DefaultTimeoutSeconds);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogAndThrow(ex, operation, "SQL_UPDATE_SEARCH_RESULTS");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Data;
|
||||
using JdeScoping.Core.Interfaces;
|
||||
using JdeScoping.DataAccess.Options;
|
||||
using JdeScoping.DataAccess.Exceptions;
|
||||
@@ -19,22 +18,6 @@ public partial class LotFinderRepository : ILotFinderRepository
|
||||
private readonly IOptions<DataAccessOptions> _options;
|
||||
private const string RepositoryName = "LotFinderRepository";
|
||||
|
||||
/// <summary>
|
||||
/// Valid table names for index rebuild operations (SQL injection whitelist).
|
||||
/// </summary>
|
||||
private static readonly HashSet<string> ValidTableNames = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Branch", "DataUpdate", "FunctionCode", "Item", "JdeUser",
|
||||
"Lot", "LotLocation", "LotUsage_Curr", "LotUsage_Hist",
|
||||
"MisData", "OrgHierarchy", "ProfitCenter", "RouteMaster",
|
||||
"Search", "StatusCode", "WorkCenter",
|
||||
"WorkOrder_Curr", "WorkOrder_Hist",
|
||||
"WorkOrderComponent_Curr", "WorkOrderComponent_Hist",
|
||||
"WorkOrderRouting",
|
||||
"WorkOrderStep_Curr", "WorkOrderStep_Hist",
|
||||
"WorkOrderTime_Curr", "WorkOrderTime_Hist"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LotFinderRepository"/> class.
|
||||
/// </summary>
|
||||
@@ -83,41 +66,4 @@ public partial class LotFinderRepository : ILotFinderRepository
|
||||
// SQL Server timeout error number: -2
|
||||
return ex.Number == -2;
|
||||
}
|
||||
|
||||
private static DataTable ToDataTable<T>(List<T> items)
|
||||
{
|
||||
var dataTable = new DataTable();
|
||||
var properties = typeof(T).GetProperties()
|
||||
.Where(p => p.CanRead && IsSupportedType(p.PropertyType))
|
||||
.ToArray();
|
||||
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
|
||||
dataTable.Columns.Add(prop.Name, type);
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var row = dataTable.NewRow();
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var value = prop.GetValue(item);
|
||||
row[prop.Name] = value ?? DBNull.Value;
|
||||
}
|
||||
dataTable.Rows.Add(row);
|
||||
}
|
||||
|
||||
return dataTable;
|
||||
}
|
||||
|
||||
private static bool IsSupportedType(Type type)
|
||||
{
|
||||
var underlyingType = Nullable.GetUnderlyingType(type) ?? type;
|
||||
return underlyingType.IsPrimitive
|
||||
|| underlyingType == typeof(string)
|
||||
|| underlyingType == typeof(DateTime)
|
||||
|| underlyingType == typeof(decimal)
|
||||
|| underlyingType == typeof(Guid);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user