Added Paginated Endpoint
Reformatted Code
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { FastifyInstance } from "fastify";
|
import {FastifyInstance} from "fastify";
|
||||||
import {insertHistoryItem } from "../utils/history"
|
import {insertHistoryItem} from "../utils/history"
|
||||||
import {diffObjects} from "../utils/diff";
|
import {diffObjects} from "../utils/diff";
|
||||||
import {sortData} from "../utils/sort";
|
import {sortData} from "../utils/sort";
|
||||||
import {useNextNumberRangeNumber} from "../utils/functions";
|
import {useNextNumberRangeNumber} from "../utils/functions";
|
||||||
@@ -26,7 +26,7 @@ const dataTypes: any[] = {
|
|||||||
label: "Kunden",
|
label: "Kunden",
|
||||||
labelSingle: "Kunde",
|
labelSingle: "Kunde",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
numberRangeHolder: "customerNumber",
|
numberRangeHolder: "customerNumber",
|
||||||
historyItemHolder: "customer",
|
historyItemHolder: "customer",
|
||||||
supabaseSortColumn: "customerNumber",
|
supabaseSortColumn: "customerNumber",
|
||||||
@@ -35,17 +35,17 @@ const dataTypes: any[] = {
|
|||||||
"Allgemeines",
|
"Allgemeines",
|
||||||
"Kontaktdaten"
|
"Kontaktdaten"
|
||||||
],
|
],
|
||||||
showTabs: [{label: 'Informationen'},{label: 'Ansprechpartner'},{label: 'Dateien'},{label: 'Ausgangsbelege'},{label: 'Projekte'},{label: 'Objekte'},{label: 'Termine'},{label: 'Verträge'}]
|
showTabs: [{label: 'Informationen'}, {label: 'Ansprechpartner'}, {label: 'Dateien'}, {label: 'Ausgangsbelege'}, {label: 'Projekte'}, {label: 'Objekte'}, {label: 'Termine'}, {label: 'Verträge'}]
|
||||||
},
|
},
|
||||||
contacts: {
|
contacts: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
label: "Kontakte",
|
label: "Kontakte",
|
||||||
labelSingle: "Kontakt",
|
labelSingle: "Kontakt",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
historyItemHolder: "contact",
|
historyItemHolder: "contact",
|
||||||
supabaseSelectWithInformation: "*, customer(*), vendor(*)",
|
supabaseSelectWithInformation: "*, customer(*), vendor(*)",
|
||||||
showTabs:[
|
showTabs: [
|
||||||
{
|
{
|
||||||
label: 'Informationen',
|
label: 'Informationen',
|
||||||
}
|
}
|
||||||
@@ -57,24 +57,24 @@ const dataTypes: any[] = {
|
|||||||
labelSingle: "Vertrag",
|
labelSingle: "Vertrag",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
numberRangeHolder: "contractNumber",
|
numberRangeHolder: "contractNumber",
|
||||||
redirect:true,
|
redirect: true,
|
||||||
inputColumns: [
|
inputColumns: [
|
||||||
"Allgemeines",
|
"Allgemeines",
|
||||||
"Abrechnung"
|
"Abrechnung"
|
||||||
],
|
],
|
||||||
supabaseSelectWithInformation: "*, customer(*), files(*)",
|
supabaseSelectWithInformation: "*, customer(*), files(*)",
|
||||||
showTabs: [{label: 'Informationen'},{label: 'Dateien'}]
|
showTabs: [{label: 'Informationen'}, {label: 'Dateien'}]
|
||||||
},
|
},
|
||||||
absencerequests: {
|
absencerequests: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
label: "Abwesenheiten",
|
label: "Abwesenheiten",
|
||||||
labelSingle: "Abwesenheit",
|
labelSingle: "Abwesenheit",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
supabaseSortColumn:"startDate",
|
supabaseSortColumn: "startDate",
|
||||||
supabaseSortAscending: false,
|
supabaseSortAscending: false,
|
||||||
supabaseSelectWithInformation: "*",
|
supabaseSelectWithInformation: "*",
|
||||||
historyItemHolder: "absencerequest",
|
historyItemHolder: "absencerequest",
|
||||||
redirect:true,
|
redirect: true,
|
||||||
showTabs: [{label: 'Informationen'}]
|
showTabs: [{label: 'Informationen'}]
|
||||||
},
|
},
|
||||||
plants: {
|
plants: {
|
||||||
@@ -82,17 +82,17 @@ const dataTypes: any[] = {
|
|||||||
label: "Objekte",
|
label: "Objekte",
|
||||||
labelSingle: "Objekt",
|
labelSingle: "Objekt",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
historyItemHolder: "plant",
|
historyItemHolder: "plant",
|
||||||
supabaseSelectWithInformation: "*, customer(id,name)",
|
supabaseSelectWithInformation: "*, customer(id,name)",
|
||||||
showTabs: [
|
showTabs: [
|
||||||
{
|
{
|
||||||
label: "Informationen"
|
label: "Informationen"
|
||||||
},{
|
}, {
|
||||||
label: "Projekte"
|
label: "Projekte"
|
||||||
},{
|
}, {
|
||||||
label: "Aufgaben"
|
label: "Aufgaben"
|
||||||
},{
|
}, {
|
||||||
label: "Dateien"
|
label: "Dateien"
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
@@ -101,7 +101,7 @@ const dataTypes: any[] = {
|
|||||||
label: "Artikel",
|
label: "Artikel",
|
||||||
labelSingle: "Artikel",
|
labelSingle: "Artikel",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
supabaseSelectWithInformation: "*, unit(name)",
|
supabaseSelectWithInformation: "*, unit(name)",
|
||||||
historyItemHolder: "product",
|
historyItemHolder: "product",
|
||||||
showTabs: [
|
showTabs: [
|
||||||
@@ -115,7 +115,7 @@ const dataTypes: any[] = {
|
|||||||
label: "Projekte",
|
label: "Projekte",
|
||||||
labelSingle: "Projekt",
|
labelSingle: "Projekt",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
historyItemHolder: "project",
|
historyItemHolder: "project",
|
||||||
numberRangeHolder: "projectNumber",
|
numberRangeHolder: "projectNumber",
|
||||||
supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*, project(id,name), customer(id,name), plant(id,name)), files(*), createddocuments(*, statementallocations(*)), events(*), times(*, profile(id, fullName))",
|
supabaseSelectWithInformation: "*, customer(id,name), plant(id,name), projecttype(name, id), tasks(*, project(id,name), customer(id,name), plant(id,name)), files(*), createddocuments(*, statementallocations(*)), events(*), times(*, profile(id, fullName))",
|
||||||
@@ -128,17 +128,17 @@ const dataTypes: any[] = {
|
|||||||
{
|
{
|
||||||
key: "phases",
|
key: "phases",
|
||||||
label: "Phasen"
|
label: "Phasen"
|
||||||
},{
|
}, {
|
||||||
key: "tasks",
|
key: "tasks",
|
||||||
label: "Aufgaben"
|
label: "Aufgaben"
|
||||||
},{
|
}, {
|
||||||
key: "files",
|
key: "files",
|
||||||
label: "Dateien"
|
label: "Dateien"
|
||||||
},{
|
}, {
|
||||||
label: "Zeiten"
|
label: "Zeiten"
|
||||||
},{
|
}, {
|
||||||
label: "Ausgangsbelege"
|
label: "Ausgangsbelege"
|
||||||
},{
|
}, {
|
||||||
label: "Termine"
|
label: "Termine"
|
||||||
}/*,{
|
}/*,{
|
||||||
key: "timetracking",
|
key: "timetracking",
|
||||||
@@ -156,7 +156,7 @@ const dataTypes: any[] = {
|
|||||||
label: "Fahrzeuge",
|
label: "Fahrzeuge",
|
||||||
labelSingle: "Fahrzeug",
|
labelSingle: "Fahrzeug",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
historyItemHolder: "vehicle",
|
historyItemHolder: "vehicle",
|
||||||
supabaseSelectWithInformation: "*, checks(*), files(*)",
|
supabaseSelectWithInformation: "*, checks(*), files(*)",
|
||||||
showTabs: [
|
showTabs: [
|
||||||
@@ -174,7 +174,7 @@ const dataTypes: any[] = {
|
|||||||
label: "Lieferanten",
|
label: "Lieferanten",
|
||||||
labelSingle: "Lieferant",
|
labelSingle: "Lieferant",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
numberRangeHolder: "vendorNumber",
|
numberRangeHolder: "vendorNumber",
|
||||||
historyItemHolder: "vendor",
|
historyItemHolder: "vendor",
|
||||||
supabaseSortColumn: "vendorNumber",
|
supabaseSortColumn: "vendorNumber",
|
||||||
@@ -182,7 +182,7 @@ const dataTypes: any[] = {
|
|||||||
showTabs: [
|
showTabs: [
|
||||||
{
|
{
|
||||||
label: 'Informationen',
|
label: 'Informationen',
|
||||||
},{
|
}, {
|
||||||
label: 'Ansprechpartner',
|
label: 'Ansprechpartner',
|
||||||
}, {
|
}, {
|
||||||
label: 'Dateien',
|
label: 'Dateien',
|
||||||
@@ -212,7 +212,7 @@ const dataTypes: any[] = {
|
|||||||
label: 'Informationen',
|
label: 'Informationen',
|
||||||
}, {
|
}, {
|
||||||
label: 'Dateien',
|
label: 'Dateien',
|
||||||
},{label: 'Inventarartikel'}
|
}, {label: 'Inventarartikel'}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
@@ -240,7 +240,7 @@ const dataTypes: any[] = {
|
|||||||
incominginvoices: {
|
incominginvoices: {
|
||||||
label: "Eingangsrechnungen",
|
label: "Eingangsrechnungen",
|
||||||
labelSingle: "Eingangsrechnung",
|
labelSingle: "Eingangsrechnung",
|
||||||
redirect:true
|
redirect: true
|
||||||
},
|
},
|
||||||
inventoryitems: {
|
inventoryitems: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
@@ -335,7 +335,8 @@ const dataTypes: any[] = {
|
|||||||
redirect: true,
|
redirect: true,
|
||||||
showTabs: [
|
showTabs: [
|
||||||
{
|
{
|
||||||
label: 'Informationen',}
|
label: 'Informationen',
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
profiles: {
|
profiles: {
|
||||||
@@ -421,7 +422,7 @@ const dataTypes: any[] = {
|
|||||||
roles: {
|
roles: {
|
||||||
label: "Rollen",
|
label: "Rollen",
|
||||||
labelSingle: "Rolle",
|
labelSingle: "Rolle",
|
||||||
redirect:true,
|
redirect: true,
|
||||||
historyItemHolder: "role",
|
historyItemHolder: "role",
|
||||||
filters: [],
|
filters: [],
|
||||||
templateColumns: [
|
templateColumns: [
|
||||||
@@ -439,23 +440,23 @@ const dataTypes: any[] = {
|
|||||||
label: "Kostenstellen",
|
label: "Kostenstellen",
|
||||||
labelSingle: "Kostenstelle",
|
labelSingle: "Kostenstelle",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
numberRangeHolder: "number",
|
numberRangeHolder: "number",
|
||||||
historyItemHolder: "costcentre",
|
historyItemHolder: "costcentre",
|
||||||
supabaseSortColumn: "number",
|
supabaseSortColumn: "number",
|
||||||
supabaseSelectWithInformation: "*, project(*), vehicle(*), inventoryitem(*)",
|
supabaseSelectWithInformation: "*, project(*), vehicle(*), inventoryitem(*)",
|
||||||
showTabs: [{label: 'Informationen'},{label: 'Auswertung Kostenstelle'}]
|
showTabs: [{label: 'Informationen'}, {label: 'Auswertung Kostenstelle'}]
|
||||||
},
|
},
|
||||||
ownaccounts: {
|
ownaccounts: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
label: "zusätzliche Buchungskonten",
|
label: "zusätzliche Buchungskonten",
|
||||||
labelSingle: "zusätzliches Buchungskonto",
|
labelSingle: "zusätzliches Buchungskonto",
|
||||||
isStandardEntity: true,
|
isStandardEntity: true,
|
||||||
redirect:true,
|
redirect: true,
|
||||||
historyItemHolder: "ownaccount",
|
historyItemHolder: "ownaccount",
|
||||||
supabaseSortColumn: "number",
|
supabaseSortColumn: "number",
|
||||||
supabaseSelectWithInformation: "*, statementallocations(*, bs_id(*))",
|
supabaseSelectWithInformation: "*, statementallocations(*, bs_id(*))",
|
||||||
showTabs: [{label: 'Informationen'},{label: 'Buchungen'}]
|
showTabs: [{label: 'Informationen'}, {label: 'Buchungen'}]
|
||||||
},
|
},
|
||||||
tickets: {
|
tickets: {
|
||||||
isArchivable: true,
|
isArchivable: true,
|
||||||
@@ -472,7 +473,8 @@ const dataTypes: any[] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default async function resourceRoutes(server: FastifyInstance) {
|
export default async function resourceRoutes(server: FastifyInstance) {
|
||||||
// Liste
|
|
||||||
|
//Liste
|
||||||
server.get("/resource/:resource", async (req, reply) => {
|
server.get("/resource/:resource", async (req, reply) => {
|
||||||
if (!req.user?.tenant_id) {
|
if (!req.user?.tenant_id) {
|
||||||
return reply.code(400).send({ error: "No tenant selected" });
|
return reply.code(400).send({ error: "No tenant selected" });
|
||||||
@@ -499,24 +501,179 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
|||||||
return sorted;
|
return sorted;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Liste Paginated
|
||||||
|
server.get("/resource/:resource/paginated", async (req, reply) => {
|
||||||
|
if (!req.user?.tenant_id) {
|
||||||
|
return reply.code(400).send({error: "No tenant selected"});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const {resource} = req.params as { resource: string }
|
||||||
|
const {queryConfig} = req
|
||||||
|
|
||||||
|
const {pagination, sort, filters, paginationDisabled} = queryConfig
|
||||||
|
const {select, search, searchColumns,distinctColumns} = req.query as {
|
||||||
|
select?: string, search?: string, searchColumns?: string, distinctColumns?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(queryConfig)
|
||||||
|
|
||||||
|
// --- Supabase-Basisabfrage ---
|
||||||
|
let baseQuery = server.supabase
|
||||||
|
.from(resource)
|
||||||
|
.select(select ? select : "*", {count: "exact"}) // 👈 Zählt Ergebnisse direkt mit
|
||||||
|
.eq("tenant", req.user.tenant_id)
|
||||||
|
|
||||||
|
// --- 🔍 Serverseitige Suche über angegebene Spalten ---
|
||||||
|
if (search && search.trim().length > 0) {
|
||||||
|
const cols = searchColumns
|
||||||
|
? searchColumns.split(',').map(c => c.trim()).filter(Boolean)
|
||||||
|
: []
|
||||||
|
|
||||||
|
if (cols.length > 0) {
|
||||||
|
const searchValue = `%${search.trim()}%`
|
||||||
|
|
||||||
|
// JSONB-Unterfelder umwandeln in "->>" Syntax
|
||||||
|
const formattedCols = cols.map(c => {
|
||||||
|
if (c.includes('.')) {
|
||||||
|
const [jsonField, jsonKey] = c.split('.')
|
||||||
|
return `${jsonField}->>${jsonKey}`
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
})
|
||||||
|
|
||||||
|
// or() Query dynamisch zusammenbauen
|
||||||
|
const orConditions = formattedCols
|
||||||
|
.map(f => `${f}.ilike.${searchValue}`)
|
||||||
|
.join(',')
|
||||||
|
|
||||||
|
baseQuery = baseQuery.or(orConditions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Filterung (intelligente Typ-Erkennung) ---
|
||||||
|
for (const [key, val] of Object.entries(queryConfig.filters)) {
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
baseQuery = baseQuery.in(key, val)
|
||||||
|
//@ts-ignore
|
||||||
|
} else if (val === true || val === false || val === null) {
|
||||||
|
baseQuery = baseQuery.is(key, val)
|
||||||
|
} else {
|
||||||
|
baseQuery = baseQuery.eq(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Sortierung ---
|
||||||
|
if (sort.length > 0) {
|
||||||
|
for (const s of sort) {
|
||||||
|
baseQuery = baseQuery.order(s.field, {ascending: s.direction === "asc"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Pagination ---
|
||||||
|
if (!paginationDisabled && pagination) {
|
||||||
|
const {offset, limit} = pagination
|
||||||
|
baseQuery = baseQuery.range(offset, offset + limit - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Abfrage ausführen ---
|
||||||
|
const {data, error, count} = await baseQuery
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
server.log.error(error)
|
||||||
|
return reply.code(400).send({error: error.message})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Distinct-Werte ermitteln ---
|
||||||
|
const distinctValues: Record<string, any[]> = {}
|
||||||
|
if (distinctColumns) {
|
||||||
|
const cols = distinctColumns.split(",").map(c => c.trim()).filter(Boolean)
|
||||||
|
|
||||||
|
for (const col of cols) {
|
||||||
|
const isJson = col.includes(".")
|
||||||
|
const {data: allRows, error: distinctErr} = await server.supabase
|
||||||
|
.from(resource)
|
||||||
|
.select(isJson ? "*" : col)
|
||||||
|
.eq("tenant", req.user.tenant_id)
|
||||||
|
|
||||||
|
if (distinctErr) continue
|
||||||
|
|
||||||
|
const values = (allRows || [])
|
||||||
|
.map(row => {
|
||||||
|
if (isJson) {
|
||||||
|
const [jsonField, jsonKey] = col.split(".")
|
||||||
|
return row?.[jsonField]?.[jsonKey] ?? null
|
||||||
|
}
|
||||||
|
return row?.[col] ?? null
|
||||||
|
})
|
||||||
|
.filter(v => v !== null && v !== undefined && v !== "")
|
||||||
|
distinctValues[col] = [...new Set(values)].sort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Gesamtanzahl & Seitenberechnung ---
|
||||||
|
const total = count
|
||||||
|
const totalPages =
|
||||||
|
!paginationDisabled && pagination?.limit
|
||||||
|
? Math.ceil(total / pagination.limit)
|
||||||
|
: 1
|
||||||
|
|
||||||
|
// --- queryConfig erweitern ---
|
||||||
|
const enrichedConfig = {
|
||||||
|
...queryConfig,
|
||||||
|
total,
|
||||||
|
totalPages,
|
||||||
|
distinctValues,
|
||||||
|
search: search || null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
queryConfig: enrichedConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*const { data, error } = await server.supabase
|
||||||
|
.from(resource)
|
||||||
|
//@ts-ignore
|
||||||
|
.select(select || dataTypes[resource].supabaseSelectWithInformation)
|
||||||
|
.eq("tenant", req.user.tenant_id)
|
||||||
|
if (error) {
|
||||||
|
console.log(error)
|
||||||
|
return reply.code(400).send({ error: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted =sortData(data,sort,asc === "true" ? true : false)
|
||||||
|
|
||||||
|
return sorted;*/
|
||||||
|
});
|
||||||
|
|
||||||
// Detail
|
// Detail
|
||||||
server.get("/resource/:resource/:id/:with_information?", async (req, reply) => {
|
server.get("/resource/:resource/:id/:with_information?", async (req, reply) => {
|
||||||
if (!req.user?.tenant_id) {
|
if (!req.user?.tenant_id) {
|
||||||
return reply.code(400).send({ error: "No tenant selected" });
|
return reply.code(400).send({error: "No tenant selected"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resource, id, with_information } = req.params as { resource: string; id: string, with_information: boolean };
|
const {resource, id, with_information} = req.params as {
|
||||||
const {select } = req.query as { select?: string }
|
resource: string;
|
||||||
|
id: string,
|
||||||
|
with_information: boolean
|
||||||
|
};
|
||||||
|
const {select} = req.query as { select?: string }
|
||||||
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const { data, error } = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*"))
|
const {
|
||||||
|
data,
|
||||||
|
error
|
||||||
|
} = await server.supabase.from(resource).select(with_information ? dataTypes[resource].supabaseSelectWithInformation : (select ? select : "*"))
|
||||||
.eq("id", id)
|
.eq("id", id)
|
||||||
.eq("tenant", req.user.tenant_id)
|
.eq("tenant", req.user.tenant_id)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error || !data) {
|
if (error || !data) {
|
||||||
return reply.code(404).send({ error: "Not found" });
|
return reply.code(404).send({error: "Not found"});
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@@ -525,10 +682,10 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
|||||||
// Create
|
// Create
|
||||||
server.post("/resource/:resource", async (req, reply) => {
|
server.post("/resource/:resource", async (req, reply) => {
|
||||||
if (!req.user?.tenant_id) {
|
if (!req.user?.tenant_id) {
|
||||||
return reply.code(400).send({ error: "No tenant selected" });
|
return reply.code(400).send({error: "No tenant selected"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resource } = req.params as { resource: string };
|
const {resource} = req.params as { resource: string };
|
||||||
const body = req.body as Record<string, any>;
|
const body = req.body as Record<string, any>;
|
||||||
|
|
||||||
const dataType = dataTypes[resource];
|
const dataType = dataTypes[resource];
|
||||||
@@ -538,22 +695,22 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
|||||||
archived: false, // Standardwert
|
archived: false, // Standardwert
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dataType.numberRangeHolder && !body[dataType.numberRangeHolder]) {
|
if (dataType.numberRangeHolder && !body[dataType.numberRangeHolder]) {
|
||||||
const result = await useNextNumberRangeNumber(server,req.user.tenant_id, resource)
|
const result = await useNextNumberRangeNumber(server, req.user.tenant_id, resource)
|
||||||
createData[dataType.numberRangeHolder] = result.usedNumber
|
createData[dataType.numberRangeHolder] = result.usedNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await server.supabase
|
const {data, error} = await server.supabase
|
||||||
.from(resource)
|
.from(resource)
|
||||||
.insert(createData)
|
.insert(createData)
|
||||||
.select("*")
|
.select("*")
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return reply.code(400).send({ error: error.message });
|
return reply.code(400).send({error: error.message});
|
||||||
}
|
}
|
||||||
|
|
||||||
await insertHistoryItem(server,{
|
await insertHistoryItem(server, {
|
||||||
entity: resource,
|
entity: resource,
|
||||||
entityId: data.id,
|
entityId: data.id,
|
||||||
action: "created",
|
action: "created",
|
||||||
@@ -570,40 +727,39 @@ export default async function resourceRoutes(server: FastifyInstance) {
|
|||||||
// UPDATE (inkl. Soft-Delete/Archive)
|
// UPDATE (inkl. Soft-Delete/Archive)
|
||||||
server.put("/resource/:resource/:id", async (req, reply) => {
|
server.put("/resource/:resource/:id", async (req, reply) => {
|
||||||
console.log("hi")
|
console.log("hi")
|
||||||
const { resource, id } = req.params as { resource: string; id: string }
|
const {resource, id} = req.params as { resource: string; id: string }
|
||||||
const body = req.body as Record<string, any>
|
const body = req.body as Record<string, any>
|
||||||
|
|
||||||
const tenantId = (req.user as any)?.tenant_id
|
const tenantId = (req.user as any)?.tenant_id
|
||||||
const userId = (req.user as any)?.user_id
|
const userId = (req.user as any)?.user_id
|
||||||
|
|
||||||
if (!tenantId || !userId) {
|
if (!tenantId || !userId) {
|
||||||
return reply.code(401).send({ error: "Unauthorized" })
|
return reply.code(401).send({error: "Unauthorized"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// vorherige Version für History laden
|
// vorherige Version für History laden
|
||||||
const { data: oldItem } = await server.supabase
|
const {data: oldItem} = await server.supabase
|
||||||
.from(resource)
|
.from(resource)
|
||||||
.select("*")
|
.select("*")
|
||||||
.eq("id", id)
|
.eq("id", id)
|
||||||
.eq("tenant", tenantId)
|
.eq("tenant", tenantId)
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
const { data:newItem, error } = await server.supabase
|
const {data: newItem, error} = await server.supabase
|
||||||
.from(resource)
|
.from(resource)
|
||||||
.update({ ...body, updated_at: new Date().toISOString(), updated_by: userId })
|
.update({...body, updated_at: new Date().toISOString(), updated_by: userId})
|
||||||
.eq("id", id)
|
.eq("id", id)
|
||||||
.eq("tenant", tenantId)
|
.eq("tenant", tenantId)
|
||||||
.select()
|
.select()
|
||||||
.single()
|
.single()
|
||||||
|
|
||||||
if (error) return reply.code(500).send({ error })
|
if (error) return reply.code(500).send({error})
|
||||||
|
|
||||||
const diffs = diffObjects(oldItem, newItem);
|
const diffs = diffObjects(oldItem, newItem);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (const d of diffs) {
|
for (const d of diffs) {
|
||||||
await insertHistoryItem(server,{
|
await insertHistoryItem(server, {
|
||||||
entity: resource,
|
entity: resource,
|
||||||
entityId: id,
|
entityId: id,
|
||||||
action: d.type,
|
action: d.type,
|
||||||
|
|||||||
Reference in New Issue
Block a user