import { createContext, DomainError } from "@tailor-platform/erp-kit/app"; import { createResolver, t } from "@tailor-platform/sdk"; import { getDB } from "@/generated/kysely-tailordb"; import { orgModulesExport } from "@/modules"; export default createResolver({ name: "createCompany", operation: "mutation", input: { legalName: t.string().description("Legal name of the company"), baseCurrencyId: t.string().description("Base currency ID"), taxId: t.string({ optional: true }).description("Tax ID"), registrationNumber: t.string({ optional: true }).description("Registration number"), street: t.string().description("Street address"), city: t.string().description("City"), state: t.string({ optional: true }).description("State or province"), postalCode: t.string().description("Postal code"), country: t.string().description("Country"), }, body: async (context) => { const ctx = createContext(context); const db = getDB("main-db"); return db .transaction() .execute(async (trx) => { // Reject if a Company with the same legalName already exists, // regardless of status. erp-kit's createCompany does a raw INSERT // with no dedup; rather than try to converge an existing row to // the new input (DRAFT activation, status reconciliation, etc.), // fail loudly so the caller has to handle it explicitly. const existing = await trx .selectFrom("Company") .select("id") .where("legalName", "=", context.input.legalName) .limit(1) .executeTakeFirst(); if (existing) { throw new DomainError( `Company with legalName="${context.input.legalName}" already exists (id=${existing.id})`, ); } const createResult = await orgModulesExport.commands.createCompany( trx, { legalName: context.input.legalName, baseCurrencyId: context.input.baseCurrencyId, taxId: context.input.taxId ?? undefined, registrationNumber: context.input.registrationNumber ?? undefined, street: context.input.street, city: context.input.city, state: context.input.state ?? undefined, postalCode: context.input.postalCode, country: context.input.country, }, ctx, ); if (!createResult.ok) { switch (createResult.error.code) { case "ORGANIZATION_INVALID_LEGAL_NAME": throw new DomainError(`Invalid legal name: ${context.input.legalName}`); case "ORGANIZATION_CURRENCY_NOT_FOUND": throw new DomainError(`Currency not found: ${context.input.baseCurrencyId}`); 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 createResult.error satisfies never; } } const activateResult = await orgModulesExport.commands.activateCompany( trx, { companyId: createResult.value.company.id }, ctx, ); if (!activateResult.ok) { switch (activateResult.error.code) { case "ORGANIZATION_COMPANY_NOT_FOUND": throw new DomainError("Company not found after creation"); case "ORGANIZATION_INVALID_STATE": throw new DomainError("Company is not in DRAFT status"); case "ORGANIZATION_MISSING_REQUIRED_FIELDS": throw new DomainError( "Missing required fields for activation: baseCurrencyId, street, city, postalCode, and country are required", ); case "ORGANIZATION_INVALID_BASE_CURRENCY": throw new DomainError(`Base currency is not active: ${context.input.baseCurrencyId}`); 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 activateResult.error satisfies never; } } return { id: createResult.value.company.id }; }) .catch((err: unknown) => { if (err instanceof DomainError) throw err; throw new Error("Failed to create the company", { cause: err }); }); }, output: t .object({ id: t.string().description("Created company ID"), }) .description("Created company"), });