import { createContext, DomainError } from "@tailor-platform/erp-kit/app"; import { createResolver, t } from "@tailor-platform/sdk"; import { getDB } from "@/generated/kysely-tailordb"; import { apModulesExport } from "@/modules"; /** * registerAccountPayableDocument — accept a DRAFT invoice for posting. * * Registration is where 3-way matching runs: the native command reconciles * each PURCHASE_ORDER-sourced line against the ordered quantity/price * (purchase.listPurchaseOrderLinesForMatching) and the posted received * quantity (inventory.listPurchaseReceiptQuantitiesForMatching), applying any * supplier InvoiceToleranceConfig. Variances raise SYSTEM * AccountPayableDocumentHold rows. Registration SUCCEEDS even with active * holds — holds block posting, not registration — and the PO billing status is * recalculated from the registered invoice quantities. */ export default createResolver({ name: "registerAccountPayableDocument", operation: "mutation", input: { id: t.string().description("Account payable document ID"), }, body: async (context) => { const ctx = createContext(context); const db = getDB("main-db"); return db .transaction() .execute(async (trx) => { const result = await apModulesExport.commands.registerAccountPayableDocument( trx, { id: context.input.id }, ctx, ); if (!result.ok) { switch (result.error.code) { case "ACCOUNT_PAYABLE_AP_DOCUMENT_NOT_FOUND": throw new DomainError(`Account payable document not found: ${context.input.id}`); case "ACCOUNT_PAYABLE_AP_INVALID_DOCUMENT_STATUS": throw new DomainError(`Only DRAFT invoices can be registered: ${context.input.id}`); case "ACCOUNT_PAYABLE_AP_DUE_SCHEDULE_INVALID": throw new DomainError(`The invoice due schedule is invalid or empty.`); case "ACCOUNT_PAYABLE_AP_LINE_TOTAL_MISMATCH": throw new DomainError(`Line/distribution totals do not reconcile.`); case "ACCOUNT_PAYABLE_AP_MINIMUM_LINES_NOT_MET": throw new DomainError( `The invoice must have at least one line with one distribution.`, ); case "ACCOUNT_PAYABLE_AP_PURCHASE_ORDER_BILLING_SYNC_FAILED": throw new DomainError( `Failed to sync purchase order billing status: ${result.error.message}`, ); case "ACCOUNT_PAYABLE_AP_ACCRUAL_ACCOUNT_UNRESOLVED": throw new DomainError("The accrual account for this invoice could not be resolved."); case "ACCOUNT_PAYABLE_AP_PURCHASE_ORDER_ACCOUNT_MISMATCH": throw new DomainError( "The invoice and purchase order must belong to the same company and supplier account.", ); 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.accountPayableDocument.id, status: result.value.accountPayableDocument.status, }; }) .catch((err: unknown) => { if (err instanceof DomainError) throw err; throw new Error("Failed to register the account payable document", { cause: err }); }); }, output: t .object({ id: t.string().description("Account payable document ID"), status: t.string().description("New document status (REGISTERED)"), }) .description("RegisterAccountPayableDocument response"), });