add linking between internal and external orders

This commit is contained in:
Matthias Hochmeister
2026-03-27 11:18:06 +01:00
parent 75e533c1fc
commit 90f691d607
8 changed files with 573 additions and 49 deletions

View File

@@ -507,6 +507,30 @@ class AusruestungsanfrageController {
}
}
async createOrders(req: Request, res: Response): Promise<void> {
try {
const anfrageId = Number(req.params.id);
const { orders } = req.body as {
orders: Array<{
lieferant_id: number;
bezeichnung: string;
positionen: Array<{ position_id: number; bezeichnung: string; menge: number; einheit?: string; notizen?: string }>;
}>;
};
if (!orders || orders.length === 0) {
res.status(400).json({ success: false, message: 'Mindestens eine Bestellung ist erforderlich' });
return;
}
const created = await ausruestungsanfrageService.createOrdersFromRequest(anfrageId, orders, req.user!.id);
res.status(201).json({ success: true, data: { created_bestellungen: created } });
} catch (error) {
logger.error('AusruestungsanfrageController.createOrders error', { error });
res.status(500).json({ success: false, message: 'Bestellungen konnten nicht erstellt werden' });
}
}
// -------------------------------------------------------------------------
// Widget overview (lightweight, for dashboard widget)
// -------------------------------------------------------------------------

View File

@@ -69,5 +69,6 @@ router.patch('/positionen/:positionId/geliefert', authenticate, requirePermissio
router.post('/requests/:id/link', authenticate, requirePermission('ausruestungsanfrage:link_orders'), ausruestungsanfrageController.linkToOrder.bind(ausruestungsanfrageController));
router.delete('/requests/:id/link/:bestellungId', authenticate, requirePermission('ausruestungsanfrage:link_orders'), ausruestungsanfrageController.unlinkFromOrder.bind(ausruestungsanfrageController));
router.post('/requests/:id/create-orders', authenticate, requirePermission('ausruestungsanfrage:link_orders'), ausruestungsanfrageController.createOrders.bind(ausruestungsanfrageController));
export default router;

View File

@@ -716,6 +716,73 @@ async function getLinkedOrders(anfrageId: number) {
return result.rows;
}
async function createOrdersFromRequest(
anfrageId: number,
orders: Array<{
lieferant_id: number;
bezeichnung: string;
positionen: Array<{ position_id: number; bezeichnung: string; menge: number; einheit?: string; notizen?: string }>;
}>,
userId: string,
) {
const client = await pool.connect();
try {
await client.query('BEGIN');
const createdBestellungen: Array<{ id: number; bezeichnung: string; lieferant_name: string }> = [];
for (const orderData of orders) {
const nrResult = await client.query(
`SELECT COALESCE(MAX(laufende_nummer), 0) + 1 AS next_nr
FROM bestellungen
WHERE EXTRACT(YEAR FROM erstellt_am) = EXTRACT(YEAR FROM NOW())`
);
const laufendeNummer = nrResult.rows[0].next_nr;
const bestellungResult = await client.query(
`INSERT INTO bestellungen (bezeichnung, lieferant_id, status, laufende_nummer, erstellt_von)
VALUES ($1, $2, 'wartet_auf_genehmigung', $3, $4)
RETURNING id, bezeichnung`,
[orderData.bezeichnung, orderData.lieferant_id, laufendeNummer, userId]
);
const bestellung = bestellungResult.rows[0];
for (const pos of orderData.positionen) {
await client.query(
`INSERT INTO bestellpositionen (bestellung_id, bezeichnung, menge, einheit, notizen)
VALUES ($1, $2, $3, $4, $5)`,
[bestellung.id, pos.bezeichnung, pos.menge, pos.einheit || 'Stk', pos.notizen || null]
);
}
await client.query(
`INSERT INTO ausruestung_anfrage_bestellung (anfrage_id, bestellung_id)
VALUES ($1, $2) ON CONFLICT DO NOTHING`,
[anfrageId, bestellung.id]
);
const vendorResult = await client.query('SELECT name FROM lieferanten WHERE id = $1', [orderData.lieferant_id]);
const lieferantName = vendorResult.rows[0]?.name ?? '';
createdBestellungen.push({ id: bestellung.id, bezeichnung: bestellung.bezeichnung, lieferant_name: lieferantName });
}
await client.query(
`UPDATE ausruestung_anfragen SET status = 'bestellt', aktualisiert_am = NOW() WHERE id = $1`,
[anfrageId]
);
await client.query('COMMIT');
return createdBestellungen;
} catch (error) {
await client.query('ROLLBACK');
logger.error('AusruestungsanfrageService.createOrdersFromRequest failed', { error });
throw new Error('Bestellungen konnten nicht erstellt werden');
} finally {
client.release();
}
}
// ---------------------------------------------------------------------------
// Overview (aggregated)
// ---------------------------------------------------------------------------
@@ -795,6 +862,7 @@ export default {
linkToOrder,
unlinkFromOrder,
getLinkedOrders,
createOrdersFromRequest,
getOverview,
getWidgetOverview,
};