using GB5Shared.DTO.Ice; using GB5Shared.DTO.Framework.Login; using IceImportDAL.DTO.IceImportPipeline; namespace IceImportBLL.IceImportPipeline; // Explicit stages so a failed stage can be retried without re-running earlier ones (used by // Phase 2's run-tracking/retry). Phase 1: CommitAsync just runs synchronously and returns the // result directly — no ICEIMPORT.TICEIMPORTRUN persistence yet. // // Interface note: ValidateAsync additionally takes ParsedImportDataSetDTO (beyond the plan's // original (rows, mapDefinition, login, ct) sketch) so HeaderFieldExistenceValidator can check raw // column presence — see IRowValidator's own interface note for why the resolved rows alone can't // answer that. ExecuteAsync is a convenience addition (not in the plan) wiring all four stages // together for the CommitImport endpoint. public interface IIceImportPipeline { Task ParseAsync( Stream file, string fileName, int iceMapId, LoginDTO login, CancellationToken ct); // Raw physical row count of the ROOT entity's resolved source table — BEFORE any object/List // (BOM-style) dedup collapses multiple physical rows sharing the same grouping key into one // header row. This is "Number of Records found in Excel"; MapAndTransformAsync's returned // List.Count is the POST-dedup header count instead ("Number of Headers" — // equivalently, how many times the header entity's own Save/Update service call was made). The // two are equal only when this map has no dedup relationship at all. int CountSourceRecords(ParsedImportDataSetDTO parsed, IceMapDTO mapDefinition); // isUpdateMode gates FixedDuringUpdateResolver/GlobalDuringUpdateResolver (ValueType 8/9) — // false (the CommitImport default) resolves them to null; true (IceImportUpdateService) makes // them behave like ValueType 1/2 respectively. Every other resolver is unaffected. Task> MapAndTransformAsync( ParsedImportDataSetDTO parsed, IceMapDTO mapDefinition, LoginDTO login, CancellationToken ct, bool isUpdateMode = false); Task> ValidateAsync( List rows, ParsedImportDataSetDTO parsedData, IceMapDTO mapDefinition, LoginDTO login, CancellationToken ct); Task CommitAsync( List rows, List validationResults, IceMapDTO mapDefinition, LoginDTO login, long runId, CancellationToken ct); // "/IceImport/Update" path — unlike CommitAsync (Menu/WebService dispatch, insert-or-update via // IEntityPersistenceAdapter), this builds and executes a dynamic parameterized // UPDATE SET ... WHERE ... directly against the physical table resolved via // MENTITY/DBOBJECT/DBOBJECTFIELDS metadata (IEntityMemberFieldMapDAL) — mirrors GB4's // IceImportNewBLL.UpdateEntity, but parameterized (GB4's own version concatenated raw values // into SQL text). IceMapDetailsDTO.IceMapDetailsIsUniqueKey decides SET vs WHERE per field: // false -> WHERE (identifies the row), true -> SET (the value being updated) — confirmed // against real map data and matches GB4's own UpdateEntity exactly (IsUniquekey != 0 -> SET, // == 0 -> WHERE). This is the OPPOSITE of IEntityPersistenceAdapter.FindByNaturalKeyAsync's own // convention for the same flag — a different mechanism (GET-based upsert decision for // CommitImport) serving a different purpose. rows must have been produced by // MapAndTransformAsync(..., isUpdateMode: true) so ValueType 8/9 resolve correctly. Only // root-level rows are processed — no BOM-style child/detail-array support here, matching GB4's // own single-entity (+ optional Addon table, not yet supported) UpdateEntity scope. Task CommitAsUpdateAsync( List rows, List validationResults, IceMapDTO mapDefinition, LoginDTO login, CancellationToken ct); Task ExecuteAsync( Stream file, string fileName, int iceMapId, LoginDTO login, long runId, CancellationToken ct); }