import { err, ok, type ReadonlyDB } from "@tailor-platform/erp-kit/core"; import type { DB } from "../generated/kysely-tailordb"; import { InvalidPaginationError } from "../lib/errors.generated"; export interface ListBusinessPartnersInput { search?: string; role?: "ALL" | "CUSTOMER" | "SUPPLIER"; offset?: number; limit?: number; } export async function run(db: ReadonlyDB, input: ListBusinessPartnersInput) { const role = input.role ?? "ALL"; const offset = input.offset ?? 0; const limit = input.limit ?? 25; if ( !Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100 ) { return err(new InvalidPaginationError(`offset=${offset}, limit=${limit}`)); } let query = db .selectFrom("BusinessPartner as partner") .leftJoin("CustomerAccount as customer", "customer.partnerId", "partner.id") .leftJoin("SupplierAccount as supplier", "supplier.partnerId", "partner.id"); const search = input.search?.trim(); if (search) { const escaped = search.replace(/[\\%_]/g, "\\$&"); query = query.where("partner.name", "ilike", `%${escaped}%`); } // Filter before pagination; DISTINCT keeps multiple company accounts from duplicating partners. // Inactive accounts still count as customer/supplier relationships. if (role === "CUSTOMER") query = query.where("customer.id", "is not", null); if (role === "SUPPLIER") query = query.where("supplier.id", "is not", null); const count = await query .select((eb) => eb.fn.count("partner.id").distinct().as("total")) .executeTakeFirstOrThrow(); const rows = await query .select(["partner.id", "partner.name", "partner.type", "partner.status"]) .select((eb) => [ eb.fn.count("customer.id").distinct().as("customerCount"), eb.fn.count("supplier.id").distinct().as("supplierCount"), ]) .groupBy(["partner.id", "partner.name", "partner.type", "partner.status"]) .orderBy("partner.name") .orderBy("partner.id") .offset(offset) .limit(limit) .execute(); return ok({ items: rows.map(({ customerCount, supplierCount, ...partner }) => ({ ...partner, isCustomer: Number(customerCount) > 0, isSupplier: Number(supplierCount) > 0, })), total: Number(count.total), }); }