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

245 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
}
})
}