feat(admin): add ZTag/SAPID reservation pre-check to equipment CSV import (task #197)
ApplyReservationPreCheckAsync on EquipmentImportBatchService queries active ExternalIdReservation rows in a single round-trip at parse time; rows whose ZTag or SAPID is claimed by a different EquipmentUuid are moved from AcceptedRows to RejectedRows with a descriptive reason. ImportEquipment.razor calls the check after EquipmentCsvImporter.Parse so conflicts appear in the preview before the operator clicks Stage + Finalise. Updated notice banner to reflect the pre-check is now live; 6 new unit tests cover conflict, no-conflict, same-UUID, released- reservation, and empty-input paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,9 +34,9 @@
|
||||
Required columns: @string.Join(", ", EquipmentCsvImporter.RequiredColumns).
|
||||
Optional columns cover the OPC 40010 Identification fields. Paste the file contents
|
||||
or upload directly — the parser runs client-stream-side and shows a row-level preview
|
||||
before anything lands in the draft. ZTag + SAPID uniqueness across the fleet is NOT
|
||||
enforced here yet (see task #197); for now the finalise may fail at commit time if a
|
||||
reservation conflict exists.
|
||||
before anything lands in the draft. ZTag + SAPID reservation conflicts (task #197) are
|
||||
checked at parse time: rows whose ZTag or SAPID is already reserved by a different
|
||||
EquipmentUuid appear in the Rejected list so you can resolve them before finalising.
|
||||
</section>
|
||||
|
||||
<section class="panel notice rise mt-2" style="animation-delay:.08s">
|
||||
@@ -186,14 +186,20 @@
|
||||
_csvText = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
private void ParseAsync()
|
||||
private async Task ParseAsync()
|
||||
{
|
||||
_parseError = null;
|
||||
_parseResult = null;
|
||||
_result = null;
|
||||
try { _parseResult = EquipmentCsvImporter.Parse(_csvText); }
|
||||
_busy = true;
|
||||
try
|
||||
{
|
||||
var raw = EquipmentCsvImporter.Parse(_csvText);
|
||||
_parseResult = await BatchSvc.ApplyReservationPreCheckAsync(raw, CancellationToken.None);
|
||||
}
|
||||
catch (InvalidCsvFormatException ex) { _parseError = ex.Message; }
|
||||
catch (Exception ex) { _parseError = $"Parse failed: {ex.Message}"; }
|
||||
finally { _busy = false; }
|
||||
}
|
||||
|
||||
private async Task StageAndFinaliseAsync()
|
||||
|
||||
Reference in New Issue
Block a user