import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { approve, deriveSupplyPlans, validateOrderItem, validateSupplier, } from "../domain/purchaseOrder"; import type { Transaction } from "../generated/kysely-tailordb"; import { PurchaseOrderNotFoundError } from "../lib/errors.generated"; import type { BusinessPartnerQueries, InventoryCommands, ItemManagementQueries } from "../module"; import { createPurchaseItemRepository, type PurchaseItemRepository, } from "../repository/purchaseItemRepository"; import { createPurchaseOrderRepository, type PurchaseOrderRepository, } from "../repository/purchaseOrderRepository"; export interface ApprovePurchaseOrderInput { id: string; } export async function run( db: Transaction, input: ApprovePurchaseOrderInput, ctx: CommandContext, supplierAccountQueries: Pick, itemManagementQueries: Pick, inventoryCommands: Pick, repositoryFactory: (db: Transaction) => PurchaseOrderRepository = createPurchaseOrderRepository, purchaseItemRepositoryFactory: ( db: Transaction, ) => PurchaseItemRepository = createPurchaseItemRepository, ) { const repository = repositoryFactory(db); const order = await repository.findById(input.id, { forUpdate: true }); if (!order) { return err(new PurchaseOrderNotFoundError(input.id)); } const approved = approve(order); if (!approved.ok) { return approved; } // The referenced master data may have changed since submission. const { account } = ( await supplierAccountQueries.getSupplierAccount( db, { supplierAccountId: order.header.supplierAccountId }, ctx, ) ).value; const supplierSnapshot = validateSupplier( order.header.supplierAccountId, order.header.companyId, account, ); if (!supplierSnapshot.ok) { return supplierSnapshot; } const itemIds = [...new Set(order.lines.map((line) => line.itemId))]; const purchaseItemByItemId = await purchaseItemRepositoryFactory(db).findByItemIds(itemIds); for (const itemId of itemIds) { const { item } = (await itemManagementQueries.getItem(db, { id: itemId }, ctx)).value; const validated = validateOrderItem(itemId, { item, purchaseItem: purchaseItemByItemId.get(itemId) ?? null, }); if (!validated.ok) { return validated; } } await repository.save(approved.value); const supplyPlans = deriveSupplyPlans(approved.value); if (supplyPlans.length > 0) { const syncResult = await inventoryCommands.createInventorySupplyPlan( db, { sourceType: "PURCHASE_ORDER", sourceId: approved.value.id, supplyPlans }, ctx, ); if (!syncResult.ok) return syncResult; } return ok({ purchaseOrderId: approved.value.id }); }