Files
FEDEO/src/routes/resources/customers.ts

330 lines
9.8 KiB
TypeScript

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<string, any[]> = {}
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,
}
})
}