Add Objects API routes
This commit is contained in:
@@ -10,6 +10,7 @@ import { organizationsRouter } from "./routes/organizations.ts";
|
|||||||
import { adminRouter } from "./routes/admin.ts";
|
import { adminRouter } from "./routes/admin.ts";
|
||||||
import { partnershipsRouter } from "./routes/partnerships.ts";
|
import { partnershipsRouter } from "./routes/partnerships.ts";
|
||||||
import { qualificationsRouter } from "./routes/qualifications.ts";
|
import { qualificationsRouter } from "./routes/qualifications.ts";
|
||||||
|
import { objectsRouter } from "./routes/objects.ts";
|
||||||
import { errorHandler } from "./middleware/error.ts";
|
import { errorHandler } from "./middleware/error.ts";
|
||||||
import { requestLogger } from "./middleware/logger.ts";
|
import { requestLogger } from "./middleware/logger.ts";
|
||||||
import { initDB } from "./db/postgres.ts";
|
import { initDB } from "./db/postgres.ts";
|
||||||
@@ -53,6 +54,8 @@ app.use(partnershipsRouter.routes());
|
|||||||
app.use(partnershipsRouter.allowedMethods());
|
app.use(partnershipsRouter.allowedMethods());
|
||||||
app.use(qualificationsRouter.routes());
|
app.use(qualificationsRouter.routes());
|
||||||
app.use(qualificationsRouter.allowedMethods());
|
app.use(qualificationsRouter.allowedMethods());
|
||||||
|
app.use(objectsRouter.routes());
|
||||||
|
app.use(objectsRouter.allowedMethods());
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
app.use((ctx) => {
|
app.use((ctx) => {
|
||||||
|
|||||||
440
src/routes/objects.ts
Normal file
440
src/routes/objects.ts
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
import { Router } from "@oak/oak";
|
||||||
|
import { query, queryOne, execute } from "../db/postgres.ts";
|
||||||
|
import { authMiddleware, requireDisponentOrHigher, requireChef } from "../middleware/auth.ts";
|
||||||
|
import { AppError } from "../middleware/error.ts";
|
||||||
|
|
||||||
|
export const objectsRouter = new Router({ prefix: "/api/objects" });
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// OBJECTS (Wachobjekte)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// GET / - Alle Objekte der Organisation
|
||||||
|
objectsRouter.get("/", authMiddleware, async (ctx) => {
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
const status = ctx.request.url.searchParams.get("status") || "active";
|
||||||
|
const type = ctx.request.url.searchParams.get("type");
|
||||||
|
const search = ctx.request.url.searchParams.get("search");
|
||||||
|
|
||||||
|
let whereClause = "WHERE org_id = $1";
|
||||||
|
const params: any[] = [orgId];
|
||||||
|
let paramIndex = 2;
|
||||||
|
|
||||||
|
if (status !== "all") {
|
||||||
|
whereClause += ` AND status = $${paramIndex++}`;
|
||||||
|
params.push(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
whereClause += ` AND object_type = $${paramIndex++}`;
|
||||||
|
params.push(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
whereClause += ` AND (name ILIKE $${paramIndex} OR city ILIKE $${paramIndex} OR object_number ILIKE $${paramIndex})`;
|
||||||
|
params.push(`%${search}%`);
|
||||||
|
paramIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objects = await query(`
|
||||||
|
SELECT o.*,
|
||||||
|
(SELECT COUNT(*) FROM object_contacts WHERE object_id = o.id) as contact_count,
|
||||||
|
(SELECT COUNT(*) FROM object_checkpoints WHERE object_id = o.id AND active = true) as checkpoint_count
|
||||||
|
FROM objects o
|
||||||
|
${whereClause}
|
||||||
|
ORDER BY o.name
|
||||||
|
`, params);
|
||||||
|
|
||||||
|
ctx.response.body = objects;
|
||||||
|
});
|
||||||
|
|
||||||
|
// GET /types - Objekt-Typen
|
||||||
|
objectsRouter.get("/types", authMiddleware, (ctx) => {
|
||||||
|
ctx.response.body = [
|
||||||
|
{ key: 'building', name: 'Gebäude', icon: '🏢' },
|
||||||
|
{ key: 'event', name: 'Veranstaltung', icon: '🎪' },
|
||||||
|
{ key: 'construction', name: 'Baustelle', icon: '🏗️' },
|
||||||
|
{ key: 'retail', name: 'Einzelhandel', icon: '🛒' },
|
||||||
|
{ key: 'industrial', name: 'Industrie', icon: '🏭' },
|
||||||
|
{ key: 'residential', name: 'Wohnanlage', icon: '🏠' },
|
||||||
|
{ key: 'parking', name: 'Parkhaus/Parkplatz', icon: '🅿️' },
|
||||||
|
{ key: 'hospital', name: 'Krankenhaus/Klinik', icon: '🏥' },
|
||||||
|
{ key: 'school', name: 'Schule/Bildung', icon: '🏫' },
|
||||||
|
{ key: 'other', name: 'Sonstiges', icon: '📍' }
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// GET /:id - Einzelnes Objekt mit Details
|
||||||
|
objectsRouter.get("/:id", authMiddleware, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`
|
||||||
|
SELECT * FROM objects WHERE id = $1 AND org_id = $2
|
||||||
|
`, [objectId, orgId]);
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kontakte laden
|
||||||
|
const contacts = await query(`
|
||||||
|
SELECT * FROM object_contacts WHERE object_id = $1 ORDER BY is_primary DESC, is_emergency DESC, name
|
||||||
|
`, [objectId]);
|
||||||
|
|
||||||
|
// Dienstanweisungen laden
|
||||||
|
const instructions = await query(`
|
||||||
|
SELECT * FROM object_instructions WHERE object_id = $1 ORDER BY sort_order, title
|
||||||
|
`, [objectId]);
|
||||||
|
|
||||||
|
// Dokumente laden
|
||||||
|
const documents = await query(`
|
||||||
|
SELECT * FROM object_documents WHERE object_id = $1 ORDER BY uploaded_at DESC
|
||||||
|
`, [objectId]);
|
||||||
|
|
||||||
|
// Checkpoints laden
|
||||||
|
const checkpoints = await query(`
|
||||||
|
SELECT * FROM object_checkpoints WHERE object_id = $1 ORDER BY sort_order, name
|
||||||
|
`, [objectId]);
|
||||||
|
|
||||||
|
// Anforderungen laden
|
||||||
|
const requirements = await query(`
|
||||||
|
SELECT r.*,
|
||||||
|
COALESCE(qt.name, oqt.name) as qualification_name,
|
||||||
|
COALESCE(qt.icon, oqt.icon) as icon
|
||||||
|
FROM object_requirements r
|
||||||
|
LEFT JOIN qualification_types qt ON r.qualification_type_id = qt.id
|
||||||
|
LEFT JOIN org_qualification_types oqt ON r.org_qualification_type_id = oqt.id
|
||||||
|
WHERE r.object_id = $1
|
||||||
|
`, [objectId]);
|
||||||
|
|
||||||
|
ctx.response.body = {
|
||||||
|
...obj,
|
||||||
|
contacts,
|
||||||
|
instructions,
|
||||||
|
documents,
|
||||||
|
checkpoints,
|
||||||
|
requirements
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST / - Neues Objekt erstellen
|
||||||
|
objectsRouter.post("/", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
const userId = ctx.state.auth.user.id;
|
||||||
|
|
||||||
|
const {
|
||||||
|
name, short_name, object_number, object_type,
|
||||||
|
street, house_number, postal_code, city, country,
|
||||||
|
latitude, longitude, phone, email,
|
||||||
|
description, size_sqm, floors,
|
||||||
|
access_info, parking_info, customer_name, image_url
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
throw new AppError("Name ist erforderlich", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
INSERT INTO objects (
|
||||||
|
org_id, name, short_name, object_number, object_type,
|
||||||
|
street, house_number, postal_code, city, country,
|
||||||
|
latitude, longitude, phone, email,
|
||||||
|
description, size_sqm, floors,
|
||||||
|
access_info, parking_info, customer_name, image_url, created_by
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)
|
||||||
|
RETURNING *
|
||||||
|
`, [
|
||||||
|
orgId, name, short_name, object_number, object_type || 'other',
|
||||||
|
street, house_number, postal_code, city, country || 'Deutschland',
|
||||||
|
latitude, longitude, phone, email,
|
||||||
|
description, size_sqm, floors,
|
||||||
|
access_info, parking_info, customer_name, image_url, userId
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.response.status = 201;
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// PUT /:id - Objekt aktualisieren
|
||||||
|
objectsRouter.put("/:id", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
UPDATE objects SET
|
||||||
|
name = COALESCE($1, name),
|
||||||
|
short_name = COALESCE($2, short_name),
|
||||||
|
object_number = COALESCE($3, object_number),
|
||||||
|
object_type = COALESCE($4, object_type),
|
||||||
|
street = COALESCE($5, street),
|
||||||
|
house_number = COALESCE($6, house_number),
|
||||||
|
postal_code = COALESCE($7, postal_code),
|
||||||
|
city = COALESCE($8, city),
|
||||||
|
country = COALESCE($9, country),
|
||||||
|
latitude = COALESCE($10, latitude),
|
||||||
|
longitude = COALESCE($11, longitude),
|
||||||
|
phone = COALESCE($12, phone),
|
||||||
|
email = COALESCE($13, email),
|
||||||
|
description = COALESCE($14, description),
|
||||||
|
size_sqm = COALESCE($15, size_sqm),
|
||||||
|
floors = COALESCE($16, floors),
|
||||||
|
access_info = COALESCE($17, access_info),
|
||||||
|
parking_info = COALESCE($18, parking_info),
|
||||||
|
customer_name = COALESCE($19, customer_name),
|
||||||
|
image_url = COALESCE($20, image_url),
|
||||||
|
status = COALESCE($21, status),
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = $22 AND org_id = $23
|
||||||
|
RETURNING *
|
||||||
|
`, [
|
||||||
|
body.name, body.short_name, body.object_number, body.object_type,
|
||||||
|
body.street, body.house_number, body.postal_code, body.city, body.country,
|
||||||
|
body.latitude, body.longitude, body.phone, body.email,
|
||||||
|
body.description, body.size_sqm, body.floors,
|
||||||
|
body.access_info, body.parking_info, body.customer_name, body.image_url, body.status,
|
||||||
|
objectId, orgId
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /:id - Objekt löschen (oder archivieren)
|
||||||
|
objectsRouter.delete("/:id", authMiddleware, requireChef, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
const permanent = ctx.request.url.searchParams.get("permanent") === "true";
|
||||||
|
|
||||||
|
if (permanent) {
|
||||||
|
const result = await execute(`
|
||||||
|
DELETE FROM objects WHERE id = $1 AND org_id = $2
|
||||||
|
`, [objectId, orgId]);
|
||||||
|
|
||||||
|
if (result === 0) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Soft delete - archivieren
|
||||||
|
const result = await execute(`
|
||||||
|
UPDATE objects SET status = 'archived', updated_at = NOW()
|
||||||
|
WHERE id = $1 AND org_id = $2
|
||||||
|
`, [objectId, orgId]);
|
||||||
|
|
||||||
|
if (result === 0) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.response.body = { success: true };
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CONTACTS
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// POST /:id/contacts - Kontakt hinzufügen
|
||||||
|
objectsRouter.post("/:id/contacts", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
// Prüfen ob Objekt existiert
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
INSERT INTO object_contacts (object_id, name, role, company, phone, mobile, email, availability, is_primary, is_emergency, notes)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||||
|
RETURNING *
|
||||||
|
`, [
|
||||||
|
objectId, body.name, body.role, body.company, body.phone, body.mobile,
|
||||||
|
body.email, body.availability, body.is_primary || false, body.is_emergency || false, body.notes
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.response.status = 201;
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /:id/contacts/:contactId - Kontakt löschen
|
||||||
|
objectsRouter.delete("/:id/contacts/:contactId", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const { id: objectId, contactId } = ctx.params;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
// Prüfen ob Objekt zur Org gehört
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await execute(`DELETE FROM object_contacts WHERE id = $1 AND object_id = $2`, [contactId, objectId]);
|
||||||
|
ctx.response.body = { success: true };
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// INSTRUCTIONS (Dienstanweisungen)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// POST /:id/instructions - Dienstanweisung hinzufügen
|
||||||
|
objectsRouter.post("/:id/instructions", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
const userId = ctx.state.auth.user.id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
INSERT INTO object_instructions (object_id, title, category, content, sort_order, is_critical, created_by)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING *
|
||||||
|
`, [
|
||||||
|
objectId, body.title, body.category || 'general', body.content,
|
||||||
|
body.sort_order || 0, body.is_critical || false, userId
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.response.status = 201;
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// PUT /:id/instructions/:instructionId - Dienstanweisung aktualisieren
|
||||||
|
objectsRouter.put("/:id/instructions/:instructionId", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const { id: objectId, instructionId } = ctx.params;
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
UPDATE object_instructions SET
|
||||||
|
title = COALESCE($1, title),
|
||||||
|
category = COALESCE($2, category),
|
||||||
|
content = COALESCE($3, content),
|
||||||
|
sort_order = COALESCE($4, sort_order),
|
||||||
|
is_critical = COALESCE($5, is_critical),
|
||||||
|
version = version + 1,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = $6 AND object_id = $7
|
||||||
|
RETURNING *
|
||||||
|
`, [body.title, body.category, body.content, body.sort_order, body.is_critical, instructionId, objectId]);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new AppError("Dienstanweisung nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /:id/instructions/:instructionId
|
||||||
|
objectsRouter.delete("/:id/instructions/:instructionId", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const { id: objectId, instructionId } = ctx.params;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await execute(`DELETE FROM object_instructions WHERE id = $1 AND object_id = $2`, [instructionId, objectId]);
|
||||||
|
ctx.response.body = { success: true };
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// CHECKPOINTS (für Wächterkontrolle)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// POST /:id/checkpoints - Checkpoint hinzufügen
|
||||||
|
objectsRouter.post("/:id/checkpoints", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code generieren falls nicht angegeben
|
||||||
|
const code = body.code || `CP-${objectId}-${Date.now().toString(36).toUpperCase()}`;
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
INSERT INTO object_checkpoints (object_id, name, location_description, code, code_type, sort_order, latitude, longitude)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
|
RETURNING *
|
||||||
|
`, [
|
||||||
|
objectId, body.name, body.location_description, code,
|
||||||
|
body.code_type || 'qr', body.sort_order || 0, body.latitude, body.longitude
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.response.status = 201;
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /:id/checkpoints/:checkpointId
|
||||||
|
objectsRouter.delete("/:id/checkpoints/:checkpointId", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const { id: objectId, checkpointId } = ctx.params;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await execute(`DELETE FROM object_checkpoints WHERE id = $1 AND object_id = $2`, [checkpointId, objectId]);
|
||||||
|
ctx.response.body = { success: true };
|
||||||
|
});
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// REQUIREMENTS (Qualifikations-Anforderungen)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// POST /:id/requirements - Anforderung hinzufügen
|
||||||
|
objectsRouter.post("/:id/requirements", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const objectId = ctx.params.id;
|
||||||
|
const body = await ctx.request.body().value;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await queryOne(`
|
||||||
|
INSERT INTO object_requirements (object_id, qualification_type_id, org_qualification_type_id, is_mandatory, notes)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
|
RETURNING *
|
||||||
|
`, [
|
||||||
|
objectId, body.qualification_type_id || null, body.org_qualification_type_id || null,
|
||||||
|
body.is_mandatory ?? true, body.notes
|
||||||
|
]);
|
||||||
|
|
||||||
|
ctx.response.status = 201;
|
||||||
|
ctx.response.body = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /:id/requirements/:requirementId
|
||||||
|
objectsRouter.delete("/:id/requirements/:requirementId", authMiddleware, requireDisponentOrHigher, async (ctx) => {
|
||||||
|
const { id: objectId, requirementId } = ctx.params;
|
||||||
|
const orgId = ctx.state.auth.user.org_id;
|
||||||
|
|
||||||
|
const obj = await queryOne(`SELECT id FROM objects WHERE id = $1 AND org_id = $2`, [objectId, orgId]);
|
||||||
|
if (!obj) {
|
||||||
|
throw new AppError("Objekt nicht gefunden", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await execute(`DELETE FROM object_requirements WHERE id = $1 AND object_id = $2`, [requirementId, objectId]);
|
||||||
|
ctx.response.body = { success: true };
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user