import { createContext, DomainError } from "@tailor-platform/erp-kit/app"; import { createResolver, t } from "@tailor-platform/sdk"; import { getDB } from "@/generated/kysely-tailordb"; import { purchaseModules } from "@/modules"; export default createResolver({ name: "duplicatePurchaseOrder", operation: "mutation", input: { id: t.string().description("Source purchase order ID to duplicate"), }, body: async (context) => { const ctx = createContext(context); const db = getDB("main-db"); return db .transaction() .execute(async (trx) => { const source = await trx .selectFrom("PurchaseOrder") .select([ "id", "companyId", "currencyId", "supplierAccountId", "receivingSiteId", "externalSupplierOrderReference", ]) .where("id", "=", context.input.id) .executeTakeFirst(); if (!source) { throw new DomainError(`Purchase order not found: ${context.input.id}`); } const sourceLines = await trx .selectFrom("PurchaseOrderLine") .select(["itemId", "quantity", "unitPrice", "unitId", "receivingSiteId", "createdAt"]) .where("purchaseOrderId", "=", source.id) // Carry the source PO's line order onto the duplicate by createdAt. .orderBy("createdAt", "asc") .execute(); const result = await purchaseModules.commands.createPurchaseOrder( trx, { // erp-kit 0.39 split create-command input into { header, lines }. header: { companyId: source.companyId, currencyId: source.currencyId, supplierAccountId: source.supplierAccountId, // Date fields are intentionally NOT copied (issue #330): they represent // time-specific commitments. orderDate defaults to today (column is // non-nullable). The user must confirm/enter dates before progressing. orderDate: new Date(), receivingSiteId: source.receivingSiteId ?? undefined, externalSupplierOrderReference: source.externalSupplierOrderReference ?? undefined, }, lines: sourceLines.map((line) => ({ itemId: line.itemId, quantity: line.quantity, unitPrice: line.unitPrice, unitId: line.unitId, receivingSiteId: line.receivingSiteId ?? undefined, })), }, ctx, ); if (!result.ok) { switch (result.error.code) { case "PURCHASE_COMPANY_NOT_FOUND": throw new DomainError(`Company not found: ${source.companyId}`); case "PURCHASE_CURRENCY_MISMATCH": throw new DomainError( `Currency is not the company's base currency: ${source.currencyId}`, ); case "PURCHASE_SUPPLIER_NOT_FOUND": throw new DomainError(`Supplier not found: ${source.supplierAccountId}`); case "PURCHASE_SUPPLIER_NOT_ACTIVE": throw new DomainError( `Supplier is not active: ${source.supplierAccountId}. Reactivate the supplier or edit the source PO before duplicating.`, ); case "PURCHASE_SUPPLIER_ACCOUNT_COMPANY_MISMATCH": throw new DomainError("The supplier account belongs to a different company."); case "PURCHASE_EMPTY_PURCHASE_ORDER_LINES": throw new DomainError("Source purchase order has no lines to duplicate"); case "PURCHASE_ITEM_NOT_FOUND": throw new DomainError(`Item not found: ${result.error.message}`); case "PURCHASE_ITEM_NOT_ACTIVE": throw new DomainError( `Item is not active: ${result.error.message}. Reactivate the item or remove the line from the source PO before duplicating.`, ); case "PURCHASE_ITEM_NOT_PURCHASABLE": throw new DomainError( `Item has no purchasing record: ${result.error.message}. Adopt the item for purchasing or remove the line from the source PO before duplicating.`, ); case "PURCHASE_INVALID_QUANTITY": throw new DomainError(`Invalid quantity: ${result.error.message}`); case "PURCHASE_INVALID_UNIT_PRICE": throw new DomainError(`Invalid unit price: ${result.error.message}`); case "PURCHASE_RECEIVING_SITE_NOT_FOUND": throw new DomainError(`Receiving site not found: ${result.error.message}`); case "PURCHASE_RECEIVING_SITE_REQUIRED": throw new DomainError(`Receiving site is required for line: ${result.error.message}`); case "UNAUTHENTICATED": throw new DomainError("Authentication is required"); case "INSUFFICIENT_PERMISSION": throw new DomainError("You do not have permission to perform this action"); default: throw result.error satisfies never; } } const created = await trx .selectFrom("PurchaseOrder") .select(["id", "docNumber"]) .where("id", "=", result.value.purchaseOrderId) .executeTakeFirstOrThrow(); return { id: created.id, docNumber: created.docNumber, }; }) .catch((err: unknown) => { if (err instanceof DomainError) throw err; throw new Error("Failed to duplicate the purchase order", { cause: err }); }); }, output: t .object({ id: t.string().description("Created purchase order ID"), docNumber: t.string().description("Created purchase order document number"), }) .description("DuplicatePurchaseOrder response"), });