import { createContext, DomainError } from "@tailor-platform/erp-kit/app"; import { createResolver, t } from "@tailor-platform/sdk"; import { getDB } from "@/generated/kysely-tailordb"; import { invModulesExport } from "@/modules"; // The costing methods erp-kit supports (0.48): STANDARD holds a per-item // standard cost (PPV on receipts), FIFO consumes cost layers first-in-first-out // at actual cost, AVERAGE values issues at a moving average. All three require // the same six posting accounts (FIFO/AVERAGE simply never post PPV). const COSTING_METHODS = ["STANDARD", "FIFO", "AVERAGE"] as const; type CostingMethod = (typeof COSTING_METHODS)[number]; export default createResolver({ name: "createValuationPolicy", operation: "mutation", input: { name: t.string().description("Valuation policy name"), companyId: t.string().description("Owning company whose ledger the policy's costing posts to"), costingMethod: t .string({ optional: true }) .description("Costing method: STANDARD | FIFO | AVERAGE (defaults to STANDARD)"), isDefault: t .bool({ optional: true }) .description( "Mark as the company's default policy (auto-assigned to items on their first valuation-affecting movement)", ), inventoryAccountId: t.string().description("Posting account for the inventory asset side"), accrualAccountId: t .string() .description("Posting account for the uninvoiced-receipt accrual (GR/IR clearing)"), invoicePriceVarianceAccountId: t .string() .description("Posting account for the invoice-versus-order price difference (clearing)"), cogsAccountId: t.string().description("Posting account for cost of goods sold on issues"), ppvAccountId: t.string().description("Posting account for purchase price variance"), adjustmentAccountId: t .string() .description("Posting account for inventory gain/loss adjustments and scrap write-offs"), standardCostAdjustmentAccountId: t .string() .description("Posting account for standard cost revision revaluations"), consumedPriceVarianceAccountId: t .string() .description( "Posting account for acquisition price variances that cannot be restated to their consumption", ), }, body: async (context) => { const ctx = createContext(context); const db = getDB("main-db"); const costingMethod = (context.input.costingMethod ?? "STANDARD") as CostingMethod; if (!COSTING_METHODS.includes(costingMethod)) { throw new DomainError( `Invalid costingMethod: ${context.input.costingMethod}. Expected one of ${COSTING_METHODS.join(", ")}.`, ); } return db .transaction() .execute(async (trx) => { const result = await invModulesExport.commands.createValuationPolicy( trx, { name: context.input.name, companyId: context.input.companyId, costingMethod, isDefault: context.input.isDefault ?? false, inventoryAccountId: context.input.inventoryAccountId, accrualAccountId: context.input.accrualAccountId, invoicePriceVarianceAccountId: context.input.invoicePriceVarianceAccountId, cogsAccountId: context.input.cogsAccountId, ppvAccountId: context.input.ppvAccountId, adjustmentAccountId: context.input.adjustmentAccountId, standardCostAdjustmentAccountId: context.input.standardCostAdjustmentAccountId, consumedPriceVarianceAccountId: context.input.consumedPriceVarianceAccountId, }, ctx, ); if (!result.ok) { switch (result.error.code) { case "INVENTORY_COMPANY_NOT_FOUND": throw new DomainError(`Company not found: ${context.input.companyId}`); case "INVENTORY_ACCOUNT_NOT_FOUND": throw new DomainError( "A posting account does not exist in the company's chart of accounts", ); case "INVENTORY_DEFAULT_VALUATION_POLICY_ALREADY_EXISTS": throw new DomainError("The company already has a default valuation policy"); 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.valuationPolicy.id }; }) .catch((err: unknown) => { if (err instanceof DomainError) throw err; throw new Error("Failed to create the valuation policy", { cause: err }); }); }, output: t .object({ id: t.string().description("Created valuation policy ID"), }) .description("Created valuation policy"), });