feat: user data purge, breadcrumbs, first-login dialog, widget consolidation, bookkeeping cascade
- Admin can purge all personal data for a user (POST /api/admin/users/:userId/purge-data) while keeping the account; clears profile, notifications, bookings, ical tokens, preferences - Add isNewUser flag to auth callback response; first-login dialog prompts for Standesbuchnummer - Add PageBreadcrumbs component and apply to 18 sub-pages across the app - Cascade budget_typ changes from parent pot to all children recursively, converting amounts (detailliert→einfach: sum into budget_gesamt; einfach→detailliert: zero all for redistribution) - Migrate NextcloudTalkWidget to use shared WidgetCard template for consistent header styling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -653,6 +653,36 @@ async function createKonto(
|
||||
}
|
||||
}
|
||||
|
||||
async function cascadeBudgetTypToChildren(parentId: number, newType: string): Promise<void> {
|
||||
const children = await pool.query(
|
||||
'SELECT id, budget_typ, budget_gwg, budget_anlagen, budget_instandhaltung, budget_gesamt FROM buchhaltung_konten WHERE parent_id = $1',
|
||||
[parentId]
|
||||
);
|
||||
|
||||
for (const child of children.rows) {
|
||||
const oldType = child.budget_typ || 'detailliert';
|
||||
if (oldType === newType) continue;
|
||||
|
||||
let gwg = 0, anlagen = 0, instandhaltung = 0, gesamt = 0;
|
||||
|
||||
if (oldType === 'detailliert' && newType === 'einfach') {
|
||||
// Sum detail fields into gesamt
|
||||
gesamt = (parseFloat(child.budget_gwg) || 0) + (parseFloat(child.budget_anlagen) || 0) + (parseFloat(child.budget_instandhaltung) || 0);
|
||||
}
|
||||
// einfach → detailliert: zero everything (user redistributes manually)
|
||||
|
||||
await pool.query(
|
||||
`UPDATE buchhaltung_konten
|
||||
SET budget_typ = $1, budget_gwg = $2, budget_anlagen = $3, budget_instandhaltung = $4, budget_gesamt = $5
|
||||
WHERE id = $6`,
|
||||
[newType, gwg, anlagen, instandhaltung, gesamt, child.id]
|
||||
);
|
||||
|
||||
// Recurse into grandchildren
|
||||
await cascadeBudgetTypToChildren(child.id, newType);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateKonto(
|
||||
id: number,
|
||||
data: { konto_typ_id?: number; kontonummer?: string; bezeichnung?: string; parent_id?: number | null; budget_gwg?: number; budget_anlagen?: number; budget_instandhaltung?: number; notizen?: string; kategorie_id?: number | null; budget_typ?: string; budget_gesamt?: number }
|
||||
@@ -711,6 +741,15 @@ async function updateKonto(
|
||||
`UPDATE buchhaltung_konten SET ${fields.join(', ')} WHERE id = $${idx} RETURNING *`,
|
||||
values
|
||||
);
|
||||
|
||||
// Cascade budget_typ change to all children recursively
|
||||
if (data.budget_typ !== undefined && result.rows[0]) {
|
||||
const newBudgetTyp = result.rows[0].budget_typ;
|
||||
// Fetch what the old type was before the update (the RETURNING row has the new value)
|
||||
// We know a change happened if the user sent budget_typ, so cascade unconditionally
|
||||
await cascadeBudgetTypToChildren(id, newBudgetTyp);
|
||||
}
|
||||
|
||||
return result.rows[0] || null;
|
||||
} catch (error: any) {
|
||||
logger.error('BuchhaltungService.updateKonto failed', { error, id });
|
||||
|
||||
Reference in New Issue
Block a user