import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import { accountPayableDocumentLifecycle } from "../db/accountPayableDocument.lifecycle.generated"; import type { Transaction } from "../generated/kysely-tailordb"; import { postCancellationCostCompensation } from "../internal/acquisitionCostAdjustment"; import { syncPurchaseOrderBillingStatusFromAccountPayable } from "../internal/purchaseBillingFeedback"; import { ApDocumentNotFoundError, ApInvalidDocumentStatusError } from "../lib/errors.generated"; import type { InventoryCommands, PurchaseCommands, PurchaseQueries } from "../module"; export interface CancelAccountPayableDocumentInput { id: string; } export async function run( db: Transaction, input: CancelAccountPayableDocumentInput, ctx: CommandContext, purchaseCommands: Pick, purchaseQueries: Pick, inventoryCommands: Pick, ) { const document = await db .selectFrom("AccountPayableDocument") .selectAll() .where("id", "=", input.id) .forUpdate() .executeTakeFirst(); if (!document) return err(new ApDocumentNotFoundError(input.id)); const operation = document.status === "DRAFT" ? "cancelDraft" : "cancelRegistered"; const nextStatus = accountPayableDocumentLifecycle.tryTransition(document.status, operation); if (!nextStatus) return err(new ApInvalidDocumentStatusError(input.id)); const cancelledDocument = await db .updateTable("AccountPayableDocument") .set({ status: nextStatus, cancelledAt: new Date(), }) .where("id", "=", input.id) .returningAll() .executeTakeFirstOrThrow(); if (document.status === "REGISTERED") { const lines = await db .selectFrom("AccountPayableDocumentLine") .selectAll() .where("accountPayableDocumentId", "=", input.id) .execute(); const billingSyncResult = await syncPurchaseOrderBillingStatusFromAccountPayable( db, { document: cancelledDocument, lines }, ctx, purchaseCommands, ); if (!billingSyncResult.ok) return billingSyncResult; // Order price changes skipped this quantity while it counted as billed, so // the difference they missed is declared as it returns to the pool. const compensationResult = await postCancellationCostCompensation( db, { document: cancelledDocument, lines }, ctx, purchaseQueries, inventoryCommands, ); if (!compensationResult.ok) return compensationResult; } return ok({ accountPayableDocument: cancelledDocument }); }