import { ok, type CommandContext } from "@tailor-platform/erp-kit/core"; import { createDraft, validateCustomer, validateOrderItem, type ItemSnapshot, } from "../domain/salesOrder"; import type { Transaction } from "../generated/kysely-tailordb"; import type { BusinessPartnerQueries, ItemManagementQueries, OrganizationQueries } from "../module"; import { createSalesItemRepository, type SalesItemRepository, } from "../repository/salesItemRepository"; import { createSalesOrderRepository, type SalesOrderRepository, } from "../repository/salesOrderRepository"; export interface SalesOrderHeaderInput { companyId: string; customerAccountId: string; currencyId: string; orderDate?: Date; shippingAddress?: string | null; billingAddress?: string | null; } export interface SalesOrderLineInput { itemId: string; description?: string | null; quantity: string; unitPrice: string; unitId?: string | null; } export async function run, LCF extends Record>( db: Transaction, input: { header: SalesOrderHeaderInput & CF; lines: (SalesOrderLineInput & LCF)[]; }, ctx: CommandContext, businessPartnerQueries: Pick, itemManagementQueries: Pick, organizationQueries: Pick, repositoryFactory: (db: Transaction) => SalesOrderRepository = createSalesOrderRepository, salesItemRepositoryFactory: (db: Transaction) => SalesItemRepository = createSalesItemRepository, ) { const { companyId, customerAccountId, currencyId, orderDate, shippingAddress, billingAddress, ...headerCustomFields } = input.header; const { company } = (await organizationQueries.getCompany(db, { id: companyId }, ctx)).value; const { account } = ( await businessPartnerQueries.getCustomerAccount(db, { customerAccountId }, ctx) ).value; const customer = validateCustomer(customerAccountId, companyId, account); if (!customer.ok) { return customer; } const salesItemByItemId = await salesItemRepositoryFactory(db).findByItemIds( input.lines.map((line) => line.itemId), ); const itemSnapshotByItemId = new Map(); for (const line of input.lines) { if (itemSnapshotByItemId.has(line.itemId)) { continue; } const { item } = (await itemManagementQueries.getItem(db, { id: line.itemId }, ctx)).value; const snapshot = validateOrderItem(line.itemId, companyId, { item, salesItem: salesItemByItemId.get(line.itemId) ?? null, }); if (!snapshot.ok) { return snapshot; } itemSnapshotByItemId.set(line.itemId, snapshot.value); } // Every line's itemId was validated above, so this lookup never misses. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const itemSnapshotFor = (itemId: string) => itemSnapshotByItemId.get(itemId)!; const order = createDraft( { companyId, customer: customer.value, currencyId, orderDate, shippingAddress, billingAddress, customFields: headerCustomFields, lines: input.lines.map((line) => { const { itemId, description, quantity, unitPrice, unitId, ...lineCustomFields } = line; return { item: itemSnapshotFor(itemId), description, quantity, unitPrice, unitId, customFields: lineCustomFields, }; }), }, { company }, ); if (!order.ok) { return order; } await repositoryFactory(db).save(order.value); return ok({ salesOrderId: order.value.id }); }