rework internal order system

This commit is contained in:
Matthias Hochmeister
2026-03-24 13:28:46 +01:00
parent 50d963120a
commit 9a52e41372
9 changed files with 198 additions and 33 deletions

View File

@@ -407,6 +407,33 @@ class AusruestungsanfrageController {
}
}
// -------------------------------------------------------------------------
// Position delivery tracking
// -------------------------------------------------------------------------
async updatePositionGeliefert(req: Request, res: Response): Promise<void> {
try {
const positionId = Number(req.params.positionId);
const { geliefert } = req.body as { geliefert?: boolean };
if (typeof geliefert !== 'boolean') {
res.status(400).json({ success: false, message: 'geliefert (boolean) ist erforderlich' });
return;
}
const position = await ausruestungsanfrageService.updatePositionGeliefert(positionId, geliefert);
if (!position) {
res.status(404).json({ success: false, message: 'Position nicht gefunden' });
return;
}
res.status(200).json({ success: true, data: position });
} catch (error) {
logger.error('AusruestungsanfrageController.updatePositionGeliefert error', { error });
res.status(500).json({ success: false, message: 'Lieferstatus konnte nicht aktualisiert werden' });
}
}
// -------------------------------------------------------------------------
// Overview
// -------------------------------------------------------------------------

View File

@@ -0,0 +1,3 @@
-- Add per-item delivery tracking to request positions
ALTER TABLE ausruestung_anfrage_positionen
ADD COLUMN IF NOT EXISTS geliefert BOOLEAN NOT NULL DEFAULT false;

View File

@@ -51,6 +51,12 @@ router.patch('/requests/:id', authenticate, ausruestungsanfrageController.update
router.patch('/requests/:id/status', authenticate, requirePermission('ausruestungsanfrage:approve'), ausruestungsanfrageController.updateRequestStatus.bind(ausruestungsanfrageController));
router.delete('/requests/:id', authenticate, requirePermission('ausruestungsanfrage:approve'), ausruestungsanfrageController.deleteRequest.bind(ausruestungsanfrageController));
// ---------------------------------------------------------------------------
// Position delivery tracking
// ---------------------------------------------------------------------------
router.patch('/positionen/:positionId/geliefert', authenticate, requirePermission('ausruestungsanfrage:approve'), ausruestungsanfrageController.updatePositionGeliefert.bind(ausruestungsanfrageController));
// ---------------------------------------------------------------------------
// Linking requests to orders
// ---------------------------------------------------------------------------

View File

@@ -575,6 +575,33 @@ async function updateRequest(
}
}
async function updatePositionGeliefert(positionId: number, geliefert: boolean) {
const result = await pool.query(
`UPDATE ausruestung_anfrage_positionen SET geliefert = $1 WHERE id = $2 RETURNING *`,
[geliefert, positionId],
);
const position = result.rows[0];
if (!position) return null;
// Auto-complete: if all positions are geliefert, set anfrage status to 'erledigt'
if (geliefert) {
const check = await pool.query(
`SELECT COUNT(*) FILTER (WHERE NOT geliefert)::int AS remaining
FROM ausruestung_anfrage_positionen
WHERE anfrage_id = $1`,
[position.anfrage_id],
);
if (check.rows[0].remaining === 0) {
await pool.query(
`UPDATE ausruestung_anfragen SET status = 'erledigt', aktualisiert_am = NOW() WHERE id = $1`,
[position.anfrage_id],
);
}
}
return position;
}
async function updateRequestStatus(
id: number,
status: string,
@@ -582,6 +609,7 @@ async function updateRequestStatus(
bearbeitetVon?: string,
) {
// Use aktualisiert_am (always exists) + try bearbeitet_am (added in migration 050)
let updated;
try {
const result = await pool.query(
`UPDATE ausruestung_anfragen
@@ -594,7 +622,7 @@ async function updateRequestStatus(
RETURNING *`,
[status, adminNotizen || null, bearbeitetVon || null, id],
);
return result.rows[0] || null;
updated = result.rows[0] || null;
} catch {
// Fallback if bearbeitet_am column doesn't exist yet (migration 050 not run)
const result = await pool.query(
@@ -607,8 +635,20 @@ async function updateRequestStatus(
RETURNING *`,
[status, adminNotizen || null, bearbeitetVon || null, id],
);
return result.rows[0] || null;
updated = result.rows[0] || null;
}
// When status changes to 'erledigt', mark all positions as geliefert
if (status === 'erledigt') {
try {
await pool.query(
`UPDATE ausruestung_anfrage_positionen SET geliefert = true WHERE anfrage_id = $1`,
[id],
);
} catch { /* column may not exist yet */ }
}
return updated;
}
async function deleteRequest(id: number) {
@@ -716,6 +756,7 @@ export default {
getRequests,
getMyRequests,
getRequestById,
updatePositionGeliefert,
createRequest,
updateRequest,
updateRequestStatus,