import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { updateDraft } from "../domain/outboundShipment"; import type { OutboundShipmentLineSourceDocumentType, OutboundShipmentLineStockType, } from "../generated/enums"; import type { Transaction } from "../generated/kysely-tailordb"; import { InvalidStatusError } from "../lib/errors.generated"; import type { InventoryQueries, ItemManagementQueries } from "../module"; import { createOutboundShipmentRepository, type OutboundShipmentRepository, } from "../repository/outboundShipmentRepository"; import type { OutboundShipmentLineInput } from "./createOutboundShipment"; export interface OutboundShipmentHeaderPatch { effectiveDate?: Date; } export interface OutboundShipmentLinePatch { itemId?: string; storageLocationId?: string; quantity?: string; unitId?: string; primaryQuantity?: string; primaryUnitId?: string; unitConversionRate?: string; stockType?: OutboundShipmentLineStockType; sourceDocumentType?: OutboundShipmentLineSourceDocumentType; sourceDocumentId?: string; sourceLineId?: string; } export interface OutboundShipmentLineEdit { lineId: string; linePatch: OutboundShipmentLinePatch; } export type UpdateOutboundShipmentInput = { id: string; headerPatch?: OutboundShipmentHeaderPatch; addLines?: OutboundShipmentLineInput[]; updateLines?: OutboundShipmentLineEdit[]; removeLineIds?: string[]; }; export async function run, LCF extends Record>( db: Transaction, input: Omit & { headerPatch?: OutboundShipmentHeaderPatch & Partial; addLines?: (OutboundShipmentLineInput & LCF)[]; updateLines?: { lineId: string; linePatch: OutboundShipmentLinePatch & Partial }[]; }, ctx: CommandContext, itemManagementQueries: Pick, inventoryQueries: Pick, repositoryFactory: ( db: Transaction, ) => OutboundShipmentRepository = createOutboundShipmentRepository, ) { 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({ outboundShipmentId: id }); }