feat: add hierarchical subitems to checklist templates and executions

This commit is contained in:
Matthias Hochmeister
2026-03-28 18:37:36 +01:00
parent 51be3b54f6
commit 893fbe43a0
5 changed files with 209 additions and 94 deletions

View File

@@ -212,13 +212,14 @@ async function addVorlageItem(vorlageId: number, data: {
beschreibung?: string | null;
pflicht?: boolean;
sort_order?: number;
parent_item_id?: number | null;
}) {
try {
const result = await pool.query(
`INSERT INTO checklist_vorlage_items (vorlage_id, bezeichnung, beschreibung, pflicht, sort_order)
VALUES ($1, $2, $3, $4, $5)
`INSERT INTO checklist_vorlage_items (vorlage_id, bezeichnung, beschreibung, pflicht, sort_order, parent_item_id)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *`,
[vorlageId, data.bezeichnung, data.beschreibung ?? null, data.pflicht ?? true, data.sort_order ?? 0]
[vorlageId, data.bezeichnung, data.beschreibung ?? null, data.pflicht ?? true, data.sort_order ?? 0, data.parent_item_id ?? null]
);
return result.rows[0];
} catch (error) {
@@ -609,17 +610,34 @@ async function startExecution(fahrzeugId: string | null, vorlageId: number, user
);
const execution = execResult.rows[0];
// Copy template items into execution items
// Copy template items into execution items (two-pass to preserve parent-child hierarchy)
const vorlageItems = await client.query(
`SELECT * FROM checklist_vorlage_items WHERE vorlage_id = $1 ORDER BY sort_order ASC, id ASC`,
[vorlageId]
);
// First pass: insert all items, record the vorlage_item_id → ausfuehrung_item_id mapping
const vorlageToAusfuehrungId = new Map<number, number>();
for (const item of vorlageItems.rows) {
await client.query(
const inserted = await client.query(
`INSERT INTO checklist_ausfuehrung_items (ausfuehrung_id, vorlage_item_id, bezeichnung)
VALUES ($1, $2, $3)`,
VALUES ($1, $2, $3)
RETURNING id`,
[execution.id, item.id, item.bezeichnung]
);
vorlageToAusfuehrungId.set(item.id, inserted.rows[0].id);
}
// Second pass: set parent_ausfuehrung_item_id for items that have a parent
for (const item of vorlageItems.rows) {
if (item.parent_item_id != null) {
const parentAusfuehrungId = vorlageToAusfuehrungId.get(item.parent_item_id);
const childAusfuehrungId = vorlageToAusfuehrungId.get(item.id);
if (parentAusfuehrungId && childAusfuehrungId) {
await client.query(
`UPDATE checklist_ausfuehrung_items SET parent_ausfuehrung_item_id = $1 WHERE id = $2`,
[parentAusfuehrungId, childAusfuehrungId]
);
}
}
}
// Copy entity-specific items (vehicle or equipment)
@@ -714,6 +732,7 @@ async function submitExecution(
}
// Check if all pflicht items have ergebnis = 'ok'
// Exclude parent items (those that have subitems) — their children carry the pflicht flag
const pflichtCheck = await client.query(
`SELECT ai.id, ai.ergebnis, ai.vorlage_item_id, ai.fahrzeug_item_id, ai.ausruestung_item_id
FROM checklist_ausfuehrung_items ai
@@ -721,7 +740,11 @@ async function submitExecution(
LEFT JOIN fahrzeug_checklist_items fi ON fi.id = ai.fahrzeug_item_id
LEFT JOIN ausruestung_checklist_items aci ON aci.id = ai.ausruestung_item_id
WHERE ai.ausfuehrung_id = $1
AND (COALESCE(vi.pflicht, fi.pflicht, aci.pflicht, true) = true)`,
AND (COALESCE(vi.pflicht, fi.pflicht, aci.pflicht, true) = true)
AND NOT EXISTS (
SELECT 1 FROM checklist_ausfuehrung_items child
WHERE child.parent_ausfuehrung_item_id = ai.id
)`,
[id]
);