import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { updateDraft } from "../domain/inboundShipment"; import type { InboundShipmentLineSourceDocumentType, InboundShipmentLineStockType, } from "../generated/enums"; import type { Transaction } from "../generated/kysely-tailordb"; import { InvalidStatusError } from "../lib/errors.generated"; import type { InventoryQueries, ItemManagementQueries } from "../module"; import { createInboundShipmentRepository, type InboundShipmentRepository, } from "../repository/inboundShipmentRepository"; import type { InboundShipmentLineInput } from "./createInboundShipment"; export interface InboundShipmentHeaderPatch { effectiveDate?: Date; } export interface InboundShipmentLinePatch { itemId?: string; storageLocationId?: string; quantity?: string; unitId?: string; primaryQuantity?: string; primaryUnitId?: string; unitConversionRate?: string; stockType?: InboundShipmentLineStockType; sourceDocumentType?: InboundShipmentLineSourceDocumentType; sourceDocumentId?: string; sourceLineId?: string; } export interface InboundShipmentLineEdit { lineId: string; linePatch: InboundShipmentLinePatch; } export type UpdateInboundShipmentInput = { id: string; headerPatch?: InboundShipmentHeaderPatch; addLines?: InboundShipmentLineInput[]; updateLines?: InboundShipmentLineEdit[]; removeLineIds?: string[]; }; export async function run, LCF extends Record>( db: Transaction, input: Omit & { headerPatch?: InboundShipmentHeaderPatch & Partial; addLines?: (InboundShipmentLineInput & LCF)[]; updateLines?: { lineId: string; linePatch: InboundShipmentLinePatch & Partial }[]; }, ctx: CommandContext, itemManagementQueries: Pick, inventoryQueries: Pick, repositoryFactory: ( db: Transaction, ) => InboundShipmentRepository = createInboundShipmentRepository, ) { const { id, headerPatch, addLines = [], updateLines = [], removeLineIds = [] } = input; const repository = repositoryFactory(db); const shipment = await repository.findById(id, { forUpdate: true }); if (!shipment) { return err(new InvalidStatusError(id)); } // The refs are a superset: every item and location the merged lines could // reference. Which lines the rules apply to is judged by the domain. const currentLineById = new Map(shipment.lines.map((line) => [line.id, line])); const itemIds = new Set(addLines.map((line) => line.itemId)); for (const { lineId, linePatch } of updateLines) { if (linePatch.itemId !== undefined) { itemIds.add(linePatch.itemId); } const current = currentLineById.get(lineId); if (current) { itemIds.add(current.itemId); } } const itemById = new Map(); for (const itemId of itemIds) { const { item } = (await itemManagementQueries.getItem(db, { id: itemId }, ctx)).value; if (item) { itemById.set(itemId, item); } } const locationIds = new Set(addLines.map((line) => line.storageLocationId)); for (const { linePatch } of updateLines) { if (linePatch.storageLocationId !== undefined) { locationIds.add(linePatch.storageLocationId); } } const storageLocationById = new Map(); for (const locationId of locationIds) { const { storageLocation } = ( await inventoryQueries.getStorageLocation(db, { id: locationId }, ctx) ).value; if (storageLocation) { storageLocationById.set(locationId, storageLocation); } } const { effectiveDate, ...headerCustomFields } = headerPatch ?? {}; const updated = updateDraft( shipment, { headerPatch: { effectiveDate, customFields: headerCustomFields }, addLines: addLines.map((line) => { const { itemId, storageLocationId, quantity, unitId, primaryQuantity, primaryUnitId, unitConversionRate, stockType, sourceDocumentType, sourceDocumentId, sourceLineId, ...lineCustomFields } = line; return { itemId, storageLocationId, quantity, unitId, primaryQuantity, primaryUnitId, unitConversionRate, stockType, sourceDocumentType, sourceDocumentId, sourceLineId, customFields: lineCustomFields, }; }), updateLines: updateLines.map(({ lineId, linePatch }) => { const { itemId, storageLocationId, quantity, unitId, primaryQuantity, primaryUnitId, unitConversionRate, stockType, sourceDocumentType, sourceDocumentId, sourceLineId, ...lineCustomFields } = linePatch; return { lineId, patch: { itemId, storageLocationId, quantity, unitId, primaryQuantity, primaryUnitId, unitConversionRate, stockType, sourceDocumentType, sourceDocumentId, sourceLineId, customFields: lineCustomFields, }, }; }), removeLineIds, }, { itemById, storageLocationById }, ); if (!updated.ok) { return updated; } await repository.save(updated.value); return ok({ inboundShipmentId: id }); }