import { FastifyInstance } from "fastify" import { eq, ilike, asc, desc, and, count, inArray, or, } from "drizzle-orm" import { customers, projects, plants, contracts, contacts, createddocuments, statementallocations, files, events, } from "../../../db/schema" // ------------------------------------------------------------- // 🔍 Helper für SQL-Suche über mehrere Spalten // ------------------------------------------------------------- function buildSearchCondition(table: any, columns: string[], search: string) { if (!search || !columns.length) return null const term = `%${search.toLowerCase()}%` const conditions = columns .map((colName) => table[colName]) .filter(Boolean) .map((col) => ilike(col, term)) if (conditions.length === 0) return null // @ts-ignore return or(...conditions) } export default async function customerRoutes(server: FastifyInstance) { // ------------------------------------------------------------- // LIST // ------------------------------------------------------------- server.get("/resource/customers", async (req, reply) => { const tenantId = req.user?.tenant_id if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) const { search, sort, asc: ascQuery } = req.query as { search?: string sort?: string asc?: string } // Basisquery let whereCond: any = eq(customers.tenant, tenantId) // 🔍 SQL-Suche if (search) { const searchCond = buildSearchCondition( customers, ["name", "customerNumber", "firstname", "lastname", "notes"], search ) if (searchCond) { whereCond = and(whereCond, searchCond) } } let baseQuery = server.db .select() .from(customers) .where(whereCond) // Sortierung if (sort) { const field = (customers as any)[sort] if (field) { //@ts-ignore baseQuery = baseQuery.orderBy( ascQuery === "true" ? asc(field) : desc(field) ) } } return await baseQuery }) // ------------------------------------------------------------- // PAGINATED // ------------------------------------------------------------- server.get("/resource/customers/paginated", async (req, reply) => { try { const tenantId = req.user?.tenant_id if (!tenantId) { return reply.code(400).send({ error: "No tenant selected" }) } const queryConfig = req.queryConfig const { pagination, sort, filters, paginationDisabled } = queryConfig const { search, distinctColumns } = req.query as { search?: string distinctColumns?: string } // ---------------------------- // WHERE CONDITIONS (Basis) // ---------------------------- let whereCond: any = eq(customers.tenant, tenantId) // Filters if (filters) { for (const [key, val] of Object.entries(filters)) { const col = (customers as any)[key] if (!col) continue if (Array.isArray(val)) { whereCond = and(whereCond, inArray(col, val)) } else { whereCond = and(whereCond, eq(col, val as any)) } } } // ---------------------------- // 🔍 SEARCH // ---------------------------- if (search && search.trim().length > 0) { const searchCond = buildSearchCondition( customers, ["name", "customerNumber", "firstname", "lastname", "notes"], search.trim() ) if (searchCond) { whereCond = and(whereCond, searchCond) } } // ---------------------------- // COUNT // ---------------------------- const totalRes = await server.db .select({ value: count(customers.id) }) .from(customers) .where(whereCond) const total = Number(totalRes[0]?.value ?? 0) // ---------------------------- // DISTINCT VALUES // ---------------------------- const distinctValues: Record = {} if (distinctColumns) { for (const colName of distinctColumns.split(",").map(v => v.trim())) { const col = (customers as any)[colName] if (!col) continue const rows = await server.db .select({ v: col }) .from(customers) .where(eq(customers.tenant, tenantId)) distinctValues[colName] = [...new Set(rows.map(r => r.v).filter(v => v != null && v !== ""))] .sort() } } // ---------------------------- // PAGINATION // ---------------------------- let offset = 0 let limit = 999999 if (!paginationDisabled && pagination) { offset = pagination.offset limit = pagination.limit } // ---------------------------- // ORDER BY // ---------------------------- let orderField = null let orderDirection: "asc" | "desc" = "asc" if (sort?.length > 0) { const s = sort[0] const col = (customers as any)[s.field] if (col) { orderField = col orderDirection = s.direction === "asc" ? "asc" : "desc" } } // ---------------------------- // QUERY DATA // ---------------------------- let dataQuery = server.db .select() .from(customers) .where(whereCond) .offset(offset) .limit(limit) if (orderField) { //@ts-ignore dataQuery = orderDirection === "asc" ? dataQuery.orderBy(asc(orderField)) : dataQuery.orderBy(desc(orderField)) } const data = await dataQuery // ---------------------------- // CONFIG RESPONSE // ---------------------------- const totalPages = pagination?.limit ? Math.ceil(total / pagination.limit) : 1 const enrichedConfig = { ...queryConfig, total, totalPages, distinctValues, search: search || null, } return { data, queryConfig: enrichedConfig, } } catch (e) { console.log(e) } }) // ------------------------------------------------------------- // DETAIL (mit ALLEN JOINS) // ------------------------------------------------------------- server.get("/resource/customers/:id", async (req, reply) => { const { id } = req.params as { id: string } const tenantId = req.user?.tenant_id if (!tenantId) return reply.code(400).send({ error: "No tenant selected" }) // --- 1) Customer selbst laden const customerRecord = await server.db .select() .from(customers) .where(and(eq(customers.id, Number(id)), eq(customers.tenant, tenantId))) .limit(1) if (!customerRecord.length) { return reply.code(404).send({ error: "Customer not found" }) } const customer = customerRecord[0] // --- 2) Relations: const [ customerProjects, customerPlants, customerContracts, customerContacts, customerDocuments, customerFiles, customerEvents, ] = await Promise.all([ server.db.select().from(projects).where(eq(projects.customer, Number(id))), server.db.select().from(plants).where(eq(plants.customer, Number(id))), server.db.select().from(contracts).where(eq(contracts.customer, Number(id))), server.db.select().from(contacts).where(eq(contacts.customer, Number(id))), server.db .select({ ...createddocuments, allocations: statementallocations, }) .from(createddocuments) .leftJoin( statementallocations, eq(statementallocations.cd_id, createddocuments.id) ) .where(eq(createddocuments.customer, Number(id))), server.db.select().from(files).where(eq(files.customer, Number(id))), server.db.select().from(events).where(eq(events.customer, Number(id))), ]) return { ...customer, projects: customerProjects, plants: customerPlants, contracts: customerContracts, contacts: customerContacts, createddocuments: customerDocuments, files: customerFiles, events: customerEvents, } }) }