import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { recordBillings, type PurchaseOrder } from "../domain/purchaseOrder"; import type { Transaction } from "../generated/kysely-tailordb"; import { PurchaseOrderLineNotFoundError } from "../lib/errors.generated"; import { createPurchaseOrderRepository, type PurchaseOrderRepository, } from "../repository/purchaseOrderRepository"; export interface PurchaseOrderLineBillingInput { purchaseOrderLineId: string; billedQuantity: string; } export interface RecalculatePurchaseOrderBillingStatusInput { lineBillings: PurchaseOrderLineBillingInput[]; } export async function run( db: Transaction, input: RecalculatePurchaseOrderBillingStatusInput, _ctx: CommandContext, repositoryFactory: (db: Transaction) => PurchaseOrderRepository = createPurchaseOrderRepository, ) { if (input.lineBillings.length === 0) { return ok({ purchaseOrderIds: [] as string[] }); } const lineIds = [...new Set(input.lineBillings.map((billing) => billing.purchaseOrderLineId))]; const repository = repositoryFactory(db); const orders = await repository.findByLineIds(lineIds, { forUpdate: true }); if (orders.length === 0) { return err(new PurchaseOrderLineNotFoundError(lineIds[0])); } const recordedOrders: PurchaseOrder[] = []; for (const order of orders) { const recorded = recordBillings(order, input.lineBillings); if (!recorded.ok) { return recorded; } recordedOrders.push(recorded.value); } // Re-check after locking: a concurrent amend may have deleted a line. const recordedLineIds = new Set( recordedOrders.flatMap((order) => order.lines.map((line) => line.id)), ); const missingLineId = lineIds.find((lineId) => !recordedLineIds.has(lineId)); if (missingLineId) { return err(new PurchaseOrderLineNotFoundError(missingLineId)); } for (const order of recordedOrders) { await repository.save(order); } return ok({ purchaseOrderIds: recordedOrders.map((order) => order.id) }); }