Redone Routes customers,contracts,contacts,vendors

This commit is contained in:
2025-12-06 20:33:27 +01:00
parent 765f3c42c1
commit dff2b05401
4 changed files with 687 additions and 199 deletions

View File

@@ -0,0 +1,244 @@
import { FastifyInstance } from "fastify"
import {
eq,
ilike,
asc,
desc,
and,
count,
inArray,
or
} from "drizzle-orm"
import {
contracts,
customers,
files
} from "../../../db/schema"
// -------------------------------------------------------------
// Helper: SQLLIKE Suche über mehrere Felder
// -------------------------------------------------------------
function buildSearchCondition(table: any, columns: string[], search?: string) {
if (!search) return null
const term = `%${search.toLowerCase()}%`
const conditions = columns
.map(col => table[col])
.filter(Boolean)
.map(col => ilike(col, term))
if (conditions.length === 0) return null
// @ts-ignore
return or(...conditions)
}
export default async function contractsRoutes(server: FastifyInstance) {
// -------------------------------------------------------------
// LIST
// -------------------------------------------------------------
server.get("/resource/contracts", 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
}
let whereCond: any = eq(contracts.tenant, tenantId)
// SQL SEARCH
const searchCond = buildSearchCondition(
contracts,
["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"],
search
)
if (searchCond) whereCond = and(whereCond, searchCond)
// Query
let q = server.db
.select()
.from(contracts)
.where(whereCond)
// SORT
if (sort) {
const field = (contracts as any)[sort]
if (field) {
//@ts-ignore
q = q.orderBy(
ascQuery === "true" ? asc(field) : desc(field)
)
}
}
return await q
})
// -------------------------------------------------------------
// PAGINATED
// -------------------------------------------------------------
server.get("/resource/contracts/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
// -----------------------------------
let whereCond: any = eq(contracts.tenant, tenantId)
if (filters) {
for (const [key, val] of Object.entries(filters)) {
const col = (contracts 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))
}
}
}
// SQL SEARCH
const searchCond = buildSearchCondition(
contracts,
["name", "notes", "contractNumber", "paymentType", "sepaRef", "bankingName"],
search
)
if (searchCond) whereCond = and(whereCond, searchCond)
// -----------------------------------
// COUNT
// -----------------------------------
const totalRes = await server.db
.select({ value: count(contracts.id) })
.from(contracts)
.where(whereCond)
const total = Number(totalRes[0]?.value ?? 0)
// -----------------------------------
// DISTINCT
// -----------------------------------
const distinctValues: Record<string, any[]> = {}
if (distinctColumns) {
for (const colName of distinctColumns.split(",")) {
const col = (contracts as any)[colName.trim()]
if (!col) continue
const rows = await server.db
.select({ v: col })
.from(contracts)
.where(eq(contracts.tenant, tenantId))
distinctValues[colName] =
[...new Set(rows.map(r => r.v).filter(v => v != null && v !== ""))]
.sort()
}
}
// -----------------------------------
// PAGINATION
// -----------------------------------
let offset = pagination?.offset ?? 0
let limit = pagination?.limit ?? 999999
// -----------------------------------
// SORT
// -----------------------------------
let orderField = null
let orderDir: "asc" | "desc" = "asc"
if (sort?.length > 0) {
const s = sort[0]
const col = (contracts as any)[s.field]
if (col) {
orderField = col
orderDir = s.direction === "asc" ? "asc" : "desc"
}
}
// -----------------------------------
// QUERY DATA
// -----------------------------------
let q = server.db
.select()
.from(contracts)
.where(whereCond)
.offset(offset)
.limit(limit)
if (orderField) {
//@ts-ignore
q = orderDir === "asc" ? q.orderBy(asc(orderField)) : q.orderBy(desc(orderField))
}
const data = await q
return {
data,
queryConfig: {
...queryConfig,
total,
totalPages: pagination?.limit ? Math.ceil(total / pagination.limit) : 1,
distinctValues,
search: search || null,
}
}
} catch (e) {
console.error(e)
return reply.code(500).send({ error: "Internal Server Error" })
}
})
// -------------------------------------------------------------
// DETAIL (+ JOINS)
// -------------------------------------------------------------
server.get("/resource/contracts/: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" })
const rows = await server.db
.select()
.from(contracts)
.where(and(eq(contracts.id, Number(id)), eq(contracts.tenant, tenantId)))
.limit(1)
if (!rows.length) return reply.code(404).send({ error: "Not found" })
const contract = rows[0]
const [customerRecord, fileList] = await Promise.all([
contract.customer
? server.db.select().from(customers).where(eq(customers.id, contract.customer))
: [],
server.db.select().from(files).where(eq(files.contract, Number(id))),
])
return {
...contract,
customer: customerRecord[0] ?? null,
files: fileList,
}
})
}