feat: checklist multi-type assignments, tab layouts for Fahrzeuge/Ausruestung, admin cleanup
- Migration 074: convert checklist vorlage single FK fields to junction tables (vorlage_fahrzeug_typen, vorlage_fahrzeuge, vorlage_ausruestung_typen, vorlage_ausruestungen) - Backend checklist service: multi-type create/update/query with array fields - Backend cleanup service: add checklist-history and reset-checklist-history targets - Frontend types/service: singular FK fields replaced with arrays (fahrzeug_typ_ids, etc.) - Frontend Checklisten.tsx: multi-select Autocomplete pickers for all assignment types - Fahrzeuge.tsx/Ausruestung.tsx: add tab layout (Uebersicht + Einstellungen), inline type CRUD - FahrzeugEinstellungen/AusruestungEinstellungen: replaced with redirects to tab URLs - Sidebar: add Uebersicht sub-items, update Einstellungen paths to tab URLs - DataManagementTab: add checklist-history cleanup and reset sections Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,26 +32,85 @@ function calculateNextDueDate(intervall: string | null, intervall_tage: number |
|
||||
}
|
||||
}
|
||||
|
||||
// Subquery fragments for junction table arrays
|
||||
const JUNCTION_SUBQUERIES = `
|
||||
ARRAY(SELECT fahrzeug_typ_id FROM checklist_vorlage_fahrzeug_typen WHERE vorlage_id = v.id) AS fahrzeug_typ_ids,
|
||||
ARRAY(SELECT fahrzeug_id::text FROM checklist_vorlage_fahrzeuge WHERE vorlage_id = v.id) AS fahrzeug_ids,
|
||||
ARRAY(SELECT ausruestung_typ_id FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = v.id) AS ausruestung_typ_ids,
|
||||
ARRAY(SELECT ausruestung_id::text FROM checklist_vorlage_ausruestung WHERE vorlage_id = v.id) AS ausruestung_ids,
|
||||
ARRAY(SELECT ft.name FROM checklist_vorlage_fahrzeug_typen cvft JOIN fahrzeug_typen ft ON ft.id = cvft.fahrzeug_typ_id WHERE cvft.vorlage_id = v.id) AS fahrzeug_typ_names,
|
||||
ARRAY(SELECT f.bezeichnung FROM checklist_vorlage_fahrzeuge cvf JOIN fahrzeuge f ON f.id = cvf.fahrzeug_id WHERE cvf.vorlage_id = v.id) AS fahrzeug_names,
|
||||
ARRAY(SELECT at2.name FROM checklist_vorlage_ausruestung_typen cvat JOIN ausruestung_typen at2 ON at2.id = cvat.ausruestung_typ_id WHERE cvat.vorlage_id = v.id) AS ausruestung_typ_names,
|
||||
ARRAY(SELECT a.bezeichnung FROM checklist_vorlage_ausruestung cva JOIN ausruestung a ON a.id = cva.ausruestung_id WHERE cva.vorlage_id = v.id) AS ausruestung_names
|
||||
`;
|
||||
|
||||
// Helper: insert rows into junction tables within a transaction client
|
||||
async function insertJunctionRows(client: any, vorlageId: number, data: {
|
||||
fahrzeug_typ_ids?: number[];
|
||||
fahrzeug_ids?: string[];
|
||||
ausruestung_typ_ids?: number[];
|
||||
ausruestung_ids?: string[];
|
||||
}) {
|
||||
if (data.fahrzeug_typ_ids?.length) {
|
||||
for (const id of data.fahrzeug_typ_ids) {
|
||||
await client.query(
|
||||
`INSERT INTO checklist_vorlage_fahrzeug_typen (vorlage_id, fahrzeug_typ_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
|
||||
[vorlageId, id]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.fahrzeug_ids?.length) {
|
||||
for (const id of data.fahrzeug_ids) {
|
||||
await client.query(
|
||||
`INSERT INTO checklist_vorlage_fahrzeuge (vorlage_id, fahrzeug_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
|
||||
[vorlageId, id]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.ausruestung_typ_ids?.length) {
|
||||
for (const id of data.ausruestung_typ_ids) {
|
||||
await client.query(
|
||||
`INSERT INTO checklist_vorlage_ausruestung_typen (vorlage_id, ausruestung_typ_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
|
||||
[vorlageId, id]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.ausruestung_ids?.length) {
|
||||
for (const id of data.ausruestung_ids) {
|
||||
await client.query(
|
||||
`INSERT INTO checklist_vorlage_ausruestung (vorlage_id, ausruestung_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
|
||||
[vorlageId, id]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: delete all junction rows for a vorlage
|
||||
async function deleteJunctionRows(client: any, vorlageId: number) {
|
||||
await client.query(`DELETE FROM checklist_vorlage_fahrzeug_typen WHERE vorlage_id = $1`, [vorlageId]);
|
||||
await client.query(`DELETE FROM checklist_vorlage_fahrzeuge WHERE vorlage_id = $1`, [vorlageId]);
|
||||
await client.query(`DELETE FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = $1`, [vorlageId]);
|
||||
await client.query(`DELETE FROM checklist_vorlage_ausruestung WHERE vorlage_id = $1`, [vorlageId]);
|
||||
}
|
||||
|
||||
// "Global" template condition: no rows in any junction table
|
||||
const GLOBAL_TEMPLATE_CONDITION = `
|
||||
NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeug_typen WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeuge WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung WHERE vorlage_id = v.id)
|
||||
`;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Vorlagen (Templates)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function getVorlagen(filter?: { fahrzeug_typ_id?: number; ausruestung_typ_id?: number; aktiv?: boolean }) {
|
||||
async function getVorlagen(filter?: { aktiv?: boolean }) {
|
||||
try {
|
||||
const conditions: string[] = [];
|
||||
const values: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
if (filter?.fahrzeug_typ_id !== undefined) {
|
||||
conditions.push(`v.fahrzeug_typ_id = $${idx}`);
|
||||
values.push(filter.fahrzeug_typ_id);
|
||||
idx++;
|
||||
}
|
||||
if (filter?.ausruestung_typ_id !== undefined) {
|
||||
conditions.push(`v.ausruestung_typ_id = $${idx}`);
|
||||
values.push(filter.ausruestung_typ_id);
|
||||
idx++;
|
||||
}
|
||||
if (filter?.aktiv !== undefined) {
|
||||
conditions.push(`v.aktiv = $${idx}`);
|
||||
values.push(filter.aktiv);
|
||||
@@ -61,11 +120,8 @@ async function getVorlagen(filter?: { fahrzeug_typ_id?: number; ausruestung_typ_
|
||||
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
const result = await pool.query(
|
||||
`SELECT v.*,
|
||||
ft.name AS fahrzeug_typ_name,
|
||||
at.name AS ausruestung_typ_name
|
||||
${JUNCTION_SUBQUERIES}
|
||||
FROM checklist_vorlagen v
|
||||
LEFT JOIN fahrzeug_typen ft ON ft.id = v.fahrzeug_typ_id
|
||||
LEFT JOIN ausruestung_typen at ON at.id = v.ausruestung_typ_id
|
||||
${where}
|
||||
ORDER BY v.name ASC`,
|
||||
values
|
||||
@@ -81,11 +137,8 @@ async function getVorlageById(id: number) {
|
||||
try {
|
||||
const vorlageResult = await pool.query(
|
||||
`SELECT v.*,
|
||||
ft.name AS fahrzeug_typ_name,
|
||||
at.name AS ausruestung_typ_name
|
||||
${JUNCTION_SUBQUERIES}
|
||||
FROM checklist_vorlagen v
|
||||
LEFT JOIN fahrzeug_typen ft ON ft.id = v.fahrzeug_typ_id
|
||||
LEFT JOIN ausruestung_typen at ON at.id = v.ausruestung_typ_id
|
||||
WHERE v.id = $1`,
|
||||
[id]
|
||||
);
|
||||
@@ -106,74 +159,93 @@ async function getVorlageById(id: number) {
|
||||
|
||||
async function createVorlage(data: {
|
||||
name: string;
|
||||
fahrzeug_typ_id?: number | null;
|
||||
fahrzeug_id?: string | null;
|
||||
ausruestung_typ_id?: number | null;
|
||||
ausruestung_id?: string | null;
|
||||
fahrzeug_typ_ids?: number[];
|
||||
fahrzeug_ids?: string[];
|
||||
ausruestung_typ_ids?: number[];
|
||||
ausruestung_ids?: string[];
|
||||
intervall?: string | null;
|
||||
intervall_tage?: number | null;
|
||||
beschreibung?: string | null;
|
||||
}) {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO checklist_vorlagen (name, fahrzeug_typ_id, fahrzeug_id, ausruestung_typ_id, ausruestung_id, intervall, intervall_tage, beschreibung)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
await client.query('BEGIN');
|
||||
const result = await client.query(
|
||||
`INSERT INTO checklist_vorlagen (name, intervall, intervall_tage, beschreibung)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING *`,
|
||||
[
|
||||
data.name,
|
||||
data.fahrzeug_typ_id ?? null,
|
||||
data.fahrzeug_id ?? null,
|
||||
data.ausruestung_typ_id ?? null,
|
||||
data.ausruestung_id ?? null,
|
||||
data.intervall ?? null,
|
||||
data.intervall_tage ?? null,
|
||||
data.beschreibung ?? null,
|
||||
]
|
||||
[data.name, data.intervall ?? null, data.intervall_tage ?? null, data.beschreibung ?? null]
|
||||
);
|
||||
return result.rows[0];
|
||||
const vorlage = result.rows[0];
|
||||
|
||||
await insertJunctionRows(client, vorlage.id, data);
|
||||
|
||||
await client.query('COMMIT');
|
||||
return getVorlageById(vorlage.id);
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK').catch(() => {});
|
||||
logger.error('ChecklistService.createVorlage failed', { error });
|
||||
throw new Error('Vorlage konnte nicht erstellt werden');
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
async function updateVorlage(id: number, data: {
|
||||
name?: string;
|
||||
fahrzeug_typ_id?: number | null;
|
||||
fahrzeug_id?: string | null;
|
||||
ausruestung_typ_id?: number | null;
|
||||
ausruestung_id?: string | null;
|
||||
fahrzeug_typ_ids?: number[];
|
||||
fahrzeug_ids?: string[];
|
||||
ausruestung_typ_ids?: number[];
|
||||
ausruestung_ids?: string[];
|
||||
intervall?: string | null;
|
||||
intervall_tage?: number | null;
|
||||
beschreibung?: string | null;
|
||||
aktiv?: boolean;
|
||||
}) {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
// Build SET clauses for scalar fields only
|
||||
const setClauses: string[] = [];
|
||||
const values: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
if (data.name !== undefined) { setClauses.push(`name = $${idx}`); values.push(data.name); idx++; }
|
||||
if ('fahrzeug_typ_id' in data) { setClauses.push(`fahrzeug_typ_id = $${idx}`); values.push(data.fahrzeug_typ_id); idx++; }
|
||||
if ('fahrzeug_id' in data) { setClauses.push(`fahrzeug_id = $${idx}`); values.push(data.fahrzeug_id); idx++; }
|
||||
if ('ausruestung_typ_id' in data) { setClauses.push(`ausruestung_typ_id = $${idx}`); values.push(data.ausruestung_typ_id); idx++; }
|
||||
if ('ausruestung_id' in data) { setClauses.push(`ausruestung_id = $${idx}`); values.push(data.ausruestung_id); idx++; }
|
||||
if ('intervall' in data) { setClauses.push(`intervall = $${idx}`); values.push(data.intervall); idx++; }
|
||||
if ('intervall_tage' in data) { setClauses.push(`intervall_tage = $${idx}`); values.push(data.intervall_tage); idx++; }
|
||||
if ('beschreibung' in data) { setClauses.push(`beschreibung = $${idx}`); values.push(data.beschreibung); idx++; }
|
||||
if (data.aktiv !== undefined) { setClauses.push(`aktiv = $${idx}`); values.push(data.aktiv); idx++; }
|
||||
|
||||
if (setClauses.length === 0) return getVorlageById(id);
|
||||
if (setClauses.length > 0) {
|
||||
values.push(id);
|
||||
await client.query(
|
||||
`UPDATE checklist_vorlagen SET ${setClauses.join(', ')} WHERE id = $${idx}`,
|
||||
values
|
||||
);
|
||||
}
|
||||
|
||||
values.push(id);
|
||||
const result = await pool.query(
|
||||
`UPDATE checklist_vorlagen SET ${setClauses.join(', ')} WHERE id = $${idx} RETURNING *`,
|
||||
values
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
// Replace junction rows if any array key is present
|
||||
const hasJunctionData = 'fahrzeug_typ_ids' in data || 'fahrzeug_ids' in data
|
||||
|| 'ausruestung_typ_ids' in data || 'ausruestung_ids' in data;
|
||||
if (hasJunctionData) {
|
||||
await deleteJunctionRows(client, id);
|
||||
await insertJunctionRows(client, id, {
|
||||
fahrzeug_typ_ids: data.fahrzeug_typ_ids ?? [],
|
||||
fahrzeug_ids: data.fahrzeug_ids ?? [],
|
||||
ausruestung_typ_ids: data.ausruestung_typ_ids ?? [],
|
||||
ausruestung_ids: data.ausruestung_ids ?? [],
|
||||
});
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
return getVorlageById(id);
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK').catch(() => {});
|
||||
logger.error('ChecklistService.updateVorlage failed', { error, id });
|
||||
throw new Error('Vorlage konnte nicht aktualisiert werden');
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,25 +517,31 @@ async function deleteEquipmentItem(id: number) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Templates for a specific vehicle (via type junction)
|
||||
// Templates for a specific vehicle (via junction tables)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function getTemplatesForVehicle(fahrzeugId: string) {
|
||||
try {
|
||||
// Templates that match the vehicle directly, by type, or global (no assignment)
|
||||
const result = await pool.query(
|
||||
`SELECT DISTINCT v.*, ft.name AS fahrzeug_typ_name
|
||||
`SELECT DISTINCT v.*,
|
||||
${JUNCTION_SUBQUERIES}
|
||||
FROM checklist_vorlagen v
|
||||
LEFT JOIN fahrzeug_typen ft ON ft.id = v.fahrzeug_typ_id
|
||||
WHERE v.aktiv = true
|
||||
AND v.ausruestung_id IS NULL
|
||||
AND v.ausruestung_typ_id IS NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung WHERE vorlage_id = v.id)
|
||||
AND (
|
||||
v.fahrzeug_id = $1
|
||||
OR v.fahrzeug_typ_id IN (
|
||||
SELECT fahrzeug_typ_id FROM fahrzeug_fahrzeug_typen WHERE fahrzeug_id = $1
|
||||
EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeuge cvf WHERE cvf.vorlage_id = v.id AND cvf.fahrzeug_id = $1)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM checklist_vorlage_fahrzeug_typen cvft
|
||||
WHERE cvft.vorlage_id = v.id
|
||||
AND cvft.fahrzeug_typ_id IN (SELECT fahrzeug_typ_id FROM fahrzeug_fahrzeug_typen WHERE fahrzeug_id = $1)
|
||||
)
|
||||
OR (
|
||||
NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeug_typen WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeuge WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = v.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung WHERE vorlage_id = v.id)
|
||||
)
|
||||
OR (v.fahrzeug_typ_id IS NULL AND v.fahrzeug_id IS NULL)
|
||||
)
|
||||
ORDER BY v.name ASC`,
|
||||
[fahrzeugId]
|
||||
@@ -486,22 +564,26 @@ async function getTemplatesForVehicle(fahrzeugId: string) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Templates for a specific equipment item (via type junction)
|
||||
// Templates for a specific equipment item (via junction tables)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function getTemplatesForEquipment(ausruestungId: string) {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
`SELECT DISTINCT v.*, at.name AS ausruestung_typ_name
|
||||
`SELECT DISTINCT v.*,
|
||||
${JUNCTION_SUBQUERIES}
|
||||
FROM checklist_vorlagen v
|
||||
LEFT JOIN ausruestung_typen at ON at.id = v.ausruestung_typ_id
|
||||
WHERE v.aktiv = true
|
||||
AND (
|
||||
v.ausruestung_id = $1
|
||||
OR v.ausruestung_typ_id IN (
|
||||
SELECT ausruestung_typ_id FROM ausruestung_ausruestung_typen WHERE ausruestung_id = $1
|
||||
EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung cva WHERE cva.vorlage_id = v.id AND cva.ausruestung_id = $1)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM checklist_vorlage_ausruestung_typen cvat
|
||||
WHERE cvat.vorlage_id = v.id
|
||||
AND cvat.ausruestung_typ_id IN (SELECT ausruestung_typ_id FROM ausruestung_ausruestung_typen WHERE ausruestung_id = $1)
|
||||
)
|
||||
OR (
|
||||
${GLOBAL_TEMPLATE_CONDITION}
|
||||
)
|
||||
OR (v.fahrzeug_typ_id IS NULL AND v.fahrzeug_id IS NULL AND v.ausruestung_typ_id IS NULL AND v.ausruestung_id IS NULL)
|
||||
)
|
||||
ORDER BY v.name ASC`,
|
||||
[ausruestungId]
|
||||
@@ -530,7 +612,6 @@ async function getTemplatesForEquipment(ausruestungId: string) {
|
||||
async function getOverviewItems() {
|
||||
try {
|
||||
// All vehicles with their assigned templates (direct, by type, or global)
|
||||
// LEFT JOIN faelligkeit so unexecuted templates still appear
|
||||
const vehiclesResult = await pool.query(`
|
||||
SELECT f.id, COALESCE(f.bezeichnung, f.kurzname) AS name,
|
||||
json_agg(DISTINCT jsonb_build_object(
|
||||
@@ -541,14 +622,21 @@ async function getOverviewItems() {
|
||||
)) AS checklists
|
||||
FROM fahrzeuge f
|
||||
JOIN checklist_vorlagen cv ON cv.aktiv = true
|
||||
AND cv.ausruestung_id IS NULL
|
||||
AND cv.ausruestung_typ_id IS NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung WHERE vorlage_id = cv.id)
|
||||
AND (
|
||||
cv.fahrzeug_id = f.id
|
||||
OR cv.fahrzeug_typ_id IN (
|
||||
SELECT fahrzeug_typ_id FROM fahrzeug_fahrzeug_typen WHERE fahrzeug_id = f.id
|
||||
EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeuge cvf WHERE cvf.vorlage_id = cv.id AND cvf.fahrzeug_id = f.id)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM checklist_vorlage_fahrzeug_typen cvft
|
||||
WHERE cvft.vorlage_id = cv.id
|
||||
AND cvft.fahrzeug_typ_id IN (SELECT fahrzeug_typ_id FROM fahrzeug_fahrzeug_typen WHERE fahrzeug_id = f.id)
|
||||
)
|
||||
OR (
|
||||
NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeug_typen WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeuge WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung WHERE vorlage_id = cv.id)
|
||||
)
|
||||
OR (cv.fahrzeug_id IS NULL AND cv.fahrzeug_typ_id IS NULL)
|
||||
)
|
||||
LEFT JOIN checklist_faelligkeit cf ON cf.vorlage_id = cv.id AND cf.fahrzeug_id = f.id
|
||||
WHERE f.deleted_at IS NULL
|
||||
@@ -567,14 +655,19 @@ async function getOverviewItems() {
|
||||
)) AS checklists
|
||||
FROM ausruestung a
|
||||
JOIN checklist_vorlagen cv ON cv.aktiv = true
|
||||
AND cv.fahrzeug_id IS NULL
|
||||
AND cv.fahrzeug_typ_id IS NULL
|
||||
AND (
|
||||
cv.ausruestung_id = a.id
|
||||
OR cv.ausruestung_typ_id IN (
|
||||
SELECT ausruestung_typ_id FROM ausruestung_ausruestung_typen WHERE ausruestung_id = a.id
|
||||
EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung cva WHERE cva.vorlage_id = cv.id AND cva.ausruestung_id = a.id)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM checklist_vorlage_ausruestung_typen cvat
|
||||
WHERE cvat.vorlage_id = cv.id
|
||||
AND cvat.ausruestung_typ_id IN (SELECT ausruestung_typ_id FROM ausruestung_ausruestung_typen WHERE ausruestung_id = a.id)
|
||||
)
|
||||
OR (
|
||||
NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeug_typen WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_fahrzeuge WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung_typen WHERE vorlage_id = cv.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM checklist_vorlage_ausruestung WHERE vorlage_id = cv.id)
|
||||
)
|
||||
OR (cv.ausruestung_id IS NULL AND cv.ausruestung_typ_id IS NULL)
|
||||
)
|
||||
LEFT JOIN checklist_faelligkeit cf ON cf.vorlage_id = cv.id AND cf.ausruestung_id = a.id
|
||||
WHERE a.deleted_at IS NULL
|
||||
|
||||
@@ -157,6 +157,34 @@ class CleanupService {
|
||||
return { count, deleted: true };
|
||||
}
|
||||
|
||||
async cleanupChecklistHistory(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
|
||||
const cutoff = `${olderThanDays} days`;
|
||||
if (!confirm) {
|
||||
const { rows } = await pool.query(
|
||||
`SELECT COUNT(*)::int AS count FROM checklist_ausfuehrungen WHERE ausgefuehrt_am < NOW() - $1::interval`,
|
||||
[cutoff]
|
||||
);
|
||||
return { count: rows[0].count, deleted: false };
|
||||
}
|
||||
const { rowCount } = await pool.query(
|
||||
`DELETE FROM checklist_ausfuehrungen WHERE ausgefuehrt_am < NOW() - $1::interval`,
|
||||
[cutoff]
|
||||
);
|
||||
logger.info(`Cleanup: deleted ${rowCount} checklist executions older than ${olderThanDays} days`);
|
||||
return { count: rowCount ?? 0, deleted: true };
|
||||
}
|
||||
|
||||
async resetChecklistHistory(confirm: boolean): Promise<CleanupResult> {
|
||||
if (!confirm) {
|
||||
const { rows } = await pool.query(`SELECT COUNT(*)::int AS count FROM checklist_ausfuehrungen`);
|
||||
return { count: rows[0].count, deleted: false };
|
||||
}
|
||||
await pool.query(`TRUNCATE checklist_ausfuehrungen CASCADE`);
|
||||
await pool.query(`TRUNCATE checklist_faelligkeit CASCADE`);
|
||||
logger.info('Cleanup: reset all checklist history');
|
||||
return { count: 0, deleted: true };
|
||||
}
|
||||
|
||||
async resetIssuesSequence(confirm: boolean): Promise<CleanupResult> {
|
||||
if (!confirm) {
|
||||
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM issues');
|
||||
|
||||
Reference in New Issue
Block a user