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: "createPurchaseOrder", operation: "mutation", input: { companyId: t.string().description("Company ID"), currencyId: t .string() .description("Transaction currency, which must be the company's base currency"), supplierAccountId: t.string().description("Supplier account ID"), orderDate: t.string().description("Order date (ISO 8601)"), receivingSiteId: t .string({ optional: true }) .description("Default receiving site for all lines"), externalSupplierOrderReference: t .string({ optional: true }) .description("External supplier order reference"), lines: t .object( { itemId: t.string().description("Item ID"), quantity: t.string().description("Quantity"), unitPrice: t.string().description("Unit price"), unitId: t.string().description("Unit ID"), receivingSiteId: t.string({ optional: true }).description("Receiving site ID"), }, { array: true }, ) .description("Order lines"), }, body: async (context) => { const ctx = createContext(context); const db = getDB("main-db"); return db .transaction() .execute(async (trx) => { const result = await purchaseModules.commands.createPurchaseOrder( trx, { // erp-kit 0.39 split create-command input into { header, lines }. // The GraphQL input stays flat; we reshape it here. header: { companyId: context.input.companyId, currencyId: context.input.currencyId, supplierAccountId: context.input.supplierAccountId, orderDate: new Date(context.input.orderDate), receivingSiteId: context.input.receivingSiteId ?? undefined, externalSupplierOrderReference: context.input.externalSupplierOrderReference ?? undefined, }, lines: context.input.lines.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: ${context.input.companyId}`); case "PURCHASE_CURRENCY_MISMATCH": throw new DomainError( `Currency is not the company's base currency: ${context.input.currencyId}`, ); case "PURCHASE_SUPPLIER_NOT_FOUND": throw new DomainError(`Supplier not found: ${context.input.supplierAccountId}`); case "PURCHASE_SUPPLIER_NOT_ACTIVE": throw new DomainError(`Supplier is not active: ${context.input.supplierAccountId}`); 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("Purchase order must have at least one line"); 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}`); case "PURCHASE_ITEM_NOT_PURCHASABLE": throw new DomainError(`Item has no purchasing record: ${result.error.message}`); 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; } } return { id: result.value.purchaseOrderId }; }) .catch((err: unknown) => { if (err instanceof DomainError) throw err; throw new Error("Failed to create the purchase order", { cause: err }); }); }, output: t .object({ id: t.string().description("Created purchase order ID"), }) .description("CreatePurchaseOrder response"), });