Files
FEDEO/backend/src/routes/functions.ts
florianfederspiel 6fded3993a
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 32s
Build and Push Docker Images / build-frontend (push) Successful in 1m10s
New CustomerInventory,
New Mitgliederverwaltung für Vereine
New Bank Auto Complete
2026-02-17 12:38:39 +01:00

196 lines
7.1 KiB
TypeScript

import { FastifyInstance } from "fastify";
import {createInvoicePDF, createTimeSheetPDF} from "../utils/pdf";
import {encodeBase64ToNiimbot, generateLabel, useNextNumberRangeNumber} from "../utils/functions";
import dayjs from "dayjs";
//import { ready as zplReady } from 'zpl-renderer-js'
//import { renderZPL } from "zpl-image";
import customParseFormat from "dayjs/plugin/customParseFormat.js";
import isoWeek from "dayjs/plugin/isoWeek.js";
import isBetween from "dayjs/plugin/isBetween.js";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"
import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js"
import duration from "dayjs/plugin/duration.js";
import timezone from "dayjs/plugin/timezone.js";
import {generateTimesEvaluation} from "../modules/time/evaluation.service";
import {citys} from "../../db/schema";
import {eq} from "drizzle-orm";
import {executeManualGeneration, finishManualGeneration} from "../modules/serialexecution.service";
dayjs.extend(customParseFormat)
dayjs.extend(isoWeek)
dayjs.extend(isBetween)
dayjs.extend(isSameOrAfter)
dayjs.extend(isSameOrBefore)
dayjs.extend(duration)
dayjs.extend(timezone)
export default async function functionRoutes(server: FastifyInstance) {
server.post("/functions/pdf/:type", async (req, reply) => {
const body = req.body as {
data: any
backgroundPath?: string
}
const {type} = req.params as {type:string}
try {
let pdf = null
if(type === "createdDocument") {
pdf = await createInvoicePDF(
server,
"base64",
body.data,
body.backgroundPath
)
} else if(type === "timesheet") {
pdf = await createTimeSheetPDF(
server,
"base64",
body.data,
body.backgroundPath
)
}
return pdf // Fastify wandelt automatisch in JSON
} catch (err) {
console.log(err)
reply.code(500).send({ error: "Failed to create PDF" })
}
})
server.get("/functions/usenextnumber/:numberrange", async (req, reply) => {
const { numberrange } = req.params as { numberrange: string };
const tenant = (req as any).user.tenant_id
try {
const result = await useNextNumberRangeNumber(server,tenant, numberrange)
reply.send(result) // JSON automatisch
} catch (err) {
req.log.error(err)
reply.code(500).send({ error: "Failed to generate next number" })
}
})
/**
* @route GET /functions/workingtimeevaluation/:user_id
* @query start_date=YYYY-MM-DD
* @query end_date=YYYY-MM-DD
*/
server.get("/functions/timeevaluation/:user_id", async (req, reply) => {
const { user_id } = req.params as { user_id: string }
const { start_date, end_date } = req.query as { start_date: string; end_date: string }
const { tenant_id } = req.user
// 🔒 Sicherheitscheck: andere User nur bei Berechtigung
if (user_id !== req.user.user_id && !req.hasPermission("staff.time.read_all")) {
return reply.code(403).send({ error: "Not allowed to view other users." })
}
try {
const result = await generateTimesEvaluation(server, user_id, tenant_id, start_date, end_date)
reply.send(result)
} catch (error) {
console.error(error)
reply.code(500).send({ error: error.message })
}
})
server.get('/functions/check-zip/:zip', async (req, reply) => {
const { zip } = req.params as { zip: string }
const normalizedZip = String(zip || "").replace(/\D/g, "")
if (normalizedZip.length !== 5) {
return reply.code(400).send({ error: 'ZIP must contain exactly 5 digits' })
}
try {
const data = await server.db
.select()
.from(citys)
.where(eq(citys.zip, Number(normalizedZip)))
if (!data.length) {
return reply.code(404).send({ error: 'ZIP not found' })
}
const city = data[0]
//districtMap
const bundeslaender = [
{ code: 'DE-BW', name: 'Baden-Württemberg' },
{ code: 'DE-BY', name: 'Bayern' },
{ code: 'DE-BE', name: 'Berlin' },
{ code: 'DE-BB', name: 'Brandenburg' },
{ code: 'DE-HB', name: 'Bremen' },
{ code: 'DE-HH', name: 'Hamburg' },
{ code: 'DE-HE', name: 'Hessen' },
{ code: 'DE-MV', name: 'Mecklenburg-Vorpommern' },
{ code: 'DE-NI', name: 'Niedersachsen' },
{ code: 'DE-NW', name: 'Nordrhein-Westfalen' },
{ code: 'DE-RP', name: 'Rheinland-Pfalz' },
{ code: 'DE-SL', name: 'Saarland' },
{ code: 'DE-SN', name: 'Sachsen' },
{ code: 'DE-ST', name: 'Sachsen-Anhalt' },
{ code: 'DE-SH', name: 'Schleswig-Holstein' },
{ code: 'DE-TH', name: 'Thüringen' }
]
return reply.send({
...city,
state_code: bundeslaender.find(i => i.name === city.countryName)?.code || null
})
} catch (err) {
console.log(err)
return reply.code(500).send({ error: 'Internal server error' })
}
})
server.post('/functions/serial/start', async (req, reply) => {
console.log(req.body)
const {executionDate,templateIds,tenantId} = req.body as {executionDate:string,templateIds:Number[],tenantId:Number}
await executeManualGeneration(server,executionDate,templateIds,tenantId,req.user.user_id)
})
server.post('/functions/serial/finish/:execution_id', async (req, reply) => {
const {execution_id} = req.params as { execution_id: string }
//@ts-ignore
await finishManualGeneration(server,execution_id)
})
server.post('/functions/services/bankstatementsync', async (req, reply) => {
await server.services.bankStatements.run(req.user.tenant_id);
})
server.post('/functions/services/prepareincominginvoices', async (req, reply) => {
await server.services.prepareIncomingInvoices.run(req.user.tenant_id)
})
server.post('/functions/services/syncdokubox', async (req, reply) => {
await server.services.dokuboxSync.run()
})
server.post('/print/label', async (req, reply) => {
const { context, width = 584, height = 354 } = req.body as {context:any,width:number,height:number}
try {
const base64 = await generateLabel(context,width,height)
return {
encoded: await encodeBase64ToNiimbot(base64, 'top'),
base64: base64
}
} catch (err) {
console.error('[Label Render Error]', err)
return reply.code(500).send({ error: err.message || 'Failed to render label' })
}
})
}