import { createContext, DomainError } from "@tailor-platform/erp-kit/app"; import { createResolver, t } from "@tailor-platform/sdk"; import { getDB } from "@/generated/kysely-tailordb"; import { salesModulesExport } from "@/modules"; /** * Update a draft SalesOrder. * * The customer is deliberately not editable. Pricing was resolved from the * customer at create time, so a different customer means a new order. */ export default createResolver({ name: "updateSalesOrder", operation: "mutation", input: { id: t.string().description("Sales order ID"), shippingAddress: t.string({ optional: true }).description("Shipping address"), billingAddress: t.string({ optional: true }).description("Billing address"), addLines: t .object( { itemId: t.string().description("Item being ordered"), quantity: t.string().description("Ordered quantity"), unitPrice: t.string().description("Unit price"), unitId: t .string({ optional: true }) .description("Unit of measure; defaults to the item's base unit"), }, { array: true, optional: true }, ) .description("New order lines to add"), updateLines: t .object( { lineId: t.string().description("Existing line ID to patch"), itemId: t.string({ optional: true }).description("Item being ordered"), quantity: t.string({ optional: true }).description("Ordered quantity"), unitPrice: t.string({ optional: true }).description("Unit price"), unitId: t.string({ optional: true }).description("Unit of measure"), }, { array: true, optional: true }, ) .description("Existing order lines to patch, keyed by lineId"), removeLineIds: t .string({ array: true, optional: true }) .description("Existing order line IDs to remove"), }, body: async (context) => { const ctx = createContext(context); const db = getDB("main-db"); return db .transaction() .execute(async (trx) => { const addLineInputs = context.input.addLines ?? []; const updateLineInputs = context.input.updateLines ?? []; const removeLineIds = context.input.removeLineIds ?? []; let addLines: { itemId: string; quantity: string; unitPrice: string; unitId?: string; }[] = []; if (addLineInputs.length > 0) { const addedItemIds = [...new Set(addLineInputs.map((line) => line.itemId))]; const itemUnitRows = await trx .selectFrom("Item") .select(["id", "unitId"]) .where("id", "in", addedItemIds) .execute(); const itemUnitMap = new Map(itemUnitRows.map((row) => [row.id, row.unitId])); addLines = addLineInputs.map((line) => ({ itemId: line.itemId, quantity: line.quantity, unitPrice: line.unitPrice, unitId: line.unitId ?? itemUnitMap.get(line.itemId) ?? undefined, })); } // The non-nullable patch fields collapse an explicit null to a no-op. const updateLines = updateLineInputs.map((edit) => ({ lineId: edit.lineId, linePatch: { itemId: edit.itemId ?? undefined, quantity: edit.quantity ?? undefined, unitPrice: edit.unitPrice ?? undefined, unitId: edit.unitId, }, })); const result = await salesModulesExport.commands.updateSalesOrder( trx, { id: context.input.id, headerPatch: { shippingAddress: context.input.shippingAddress, billingAddress: context.input.billingAddress, }, addLines, updateLines, removeLineIds, }, ctx, ); if (!result.ok) { switch (result.error.code) { case "SALES_SALES_ORDER_NOT_FOUND": throw new DomainError(`Sales order not found: ${context.input.id}`); case "SALES_ORDER_NOT_EDITABLE": throw new DomainError(`Sales order is not in DRAFT status: ${context.input.id}`); case "SALES_LINE_NOT_FOUND": throw new DomainError(`Sales order line not found: ${result.error.message}`); case "SALES_EMPTY_ORDER_NOT_ALLOWED": throw new DomainError("A sales order must keep at least one line."); case "SALES_INVALID_ORDER_LINE": throw new DomainError(`Invalid order line: ${result.error.message}`); case "SALES_ITEM_NOT_FOUND": throw new DomainError(`Item not found: ${result.error.message}`); case "SALES_ITEM_NOT_ACTIVE": throw new DomainError(`Item is not active: ${result.error.message}`); case "SALES_ITEM_NOT_SELLABLE": throw new DomainError(`Item has no selling record: ${result.error.message}`); case "SALES_CUSTOMER_NOT_FOUND": throw new DomainError(`Customer not found: ${result.error.message}`); case "SALES_CUSTOMER_NOT_ACTIVE": throw new DomainError(`Customer is not active: ${result.error.message}`); case "SALES_CROSS_COMPANY_REFERENCE": throw new DomainError(`The referenced record belongs to a different company.`); 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.salesOrderId }; }) .catch((err: unknown) => { if (err instanceof DomainError) throw err; throw new Error("Failed to update the sales order", { cause: err }); }); }, output: t .object({ id: t.string().description("Updated sales order ID"), }) .description("UpdateSalesOrder response"), });