refactor(mitglieder): replace legacy status values (passiv/anwärter/ausgetreten/…) with aktiv/kind/jugend/reserve across backend, frontend, and sync
This commit is contained in:
@@ -47,7 +47,7 @@ class MemberController {
|
||||
pageSize,
|
||||
} = req.query as Record<string, string | undefined>;
|
||||
|
||||
// Arrays can be sent as ?status[]=aktiv&status[]=passiv or CSV
|
||||
// Arrays can be sent as ?status[]=aktiv&status[]=jugend or CSV
|
||||
const statusParam = req.query['status'] as string | string[] | undefined;
|
||||
const dienstgradParam = req.query['dienstgrad'] as string | string[] | undefined;
|
||||
|
||||
|
||||
16
backend/src/database/migrations/090_update_status_values.sql
Normal file
16
backend/src/database/migrations/090_update_status_values.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- Migration: 090_update_status_values
|
||||
-- Replace old status values with FDISK-aligned values: aktiv, kind, jugend, reserve.
|
||||
-- Old values passiv, ehrenmitglied, jugendfeuerwehr, anwärter, ausgetreten are removed.
|
||||
-- Idempotent: safe to run multiple times.
|
||||
|
||||
-- 1. Drop existing CHECK constraint
|
||||
ALTER TABLE mitglieder_profile DROP CONSTRAINT IF EXISTS mitglieder_profile_status_check;
|
||||
|
||||
-- 2. Migrate existing data
|
||||
UPDATE mitglieder_profile SET status = 'jugend' WHERE status = 'jugendfeuerwehr';
|
||||
UPDATE mitglieder_profile SET status = NULL
|
||||
WHERE status IN ('passiv', 'ehrenmitglied', 'anwärter', 'ausgetreten');
|
||||
|
||||
-- 3. Re-add CHECK with new allowed values (NULL still allowed for profiles without FDISK sync)
|
||||
ALTER TABLE mitglieder_profile ADD CONSTRAINT mitglieder_profile_status_check
|
||||
CHECK (status IS NULL OR status IN ('aktiv', 'kind', 'jugend', 'reserve'));
|
||||
@@ -29,11 +29,9 @@ export const DIENSTGRAD_VALUES = [
|
||||
|
||||
export const STATUS_VALUES = [
|
||||
'aktiv',
|
||||
'passiv',
|
||||
'ehrenmitglied',
|
||||
'jugendfeuerwehr',
|
||||
'anwärter',
|
||||
'ausgetreten',
|
||||
'kind',
|
||||
'jugend',
|
||||
'reserve',
|
||||
] as const;
|
||||
|
||||
export const FUNKTION_VALUES = [
|
||||
@@ -232,9 +230,7 @@ export type SelfUpdateMemberProfileData = z.infer<typeof SelfUpdateMemberProfile
|
||||
export interface MemberStats {
|
||||
total: number;
|
||||
aktiv: number;
|
||||
passiv: number;
|
||||
ehrenmitglied: number;
|
||||
jugendfeuerwehr: number;
|
||||
anwärter: number;
|
||||
ausgetreten: number;
|
||||
kind: number;
|
||||
jugend: number;
|
||||
reserve: number;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class AtemschutzService {
|
||||
result = await pool.query(`
|
||||
SELECT *
|
||||
FROM atemschutz_uebersicht
|
||||
WHERE mitglied_status IS NULL OR mitglied_status IN ('aktiv', 'anwärter')
|
||||
WHERE mitglied_status IS NULL OR mitglied_status IN ('aktiv')
|
||||
ORDER BY user_family_name, user_given_name
|
||||
`);
|
||||
} else {
|
||||
@@ -301,7 +301,7 @@ class AtemschutzService {
|
||||
) AS leistungstest_bald_faellig,
|
||||
COUNT(*) FILTER (WHERE einsatzbereit = TRUE) AS einsatzbereit
|
||||
FROM atemschutz_uebersicht
|
||||
WHERE mitglied_status IS NULL OR mitglied_status IN ('aktiv', 'anwärter')
|
||||
WHERE mitglied_status IS NULL OR mitglied_status IN ('aktiv')
|
||||
`);
|
||||
|
||||
const row = result.rows[0] ?? {};
|
||||
|
||||
@@ -630,24 +630,20 @@ class MemberService {
|
||||
try {
|
||||
const result = await pool.query(`
|
||||
SELECT
|
||||
COUNT(*)::INTEGER AS total,
|
||||
COUNT(*) FILTER (WHERE status = 'aktiv')::INTEGER AS aktiv,
|
||||
COUNT(*) FILTER (WHERE status = 'passiv')::INTEGER AS passiv,
|
||||
COUNT(*) FILTER (WHERE status = 'ehrenmitglied')::INTEGER AS ehrenmitglied,
|
||||
COUNT(*) FILTER (WHERE status = 'jugendfeuerwehr')::INTEGER AS jugendfeuerwehr,
|
||||
COUNT(*) FILTER (WHERE status = 'anwärter')::INTEGER AS "anwärter",
|
||||
COUNT(*) FILTER (WHERE status = 'ausgetreten')::INTEGER AS ausgetreten
|
||||
COUNT(*)::INTEGER AS total,
|
||||
COUNT(*) FILTER (WHERE status = 'aktiv')::INTEGER AS aktiv,
|
||||
COUNT(*) FILTER (WHERE status = 'kind')::INTEGER AS kind,
|
||||
COUNT(*) FILTER (WHERE status = 'jugend')::INTEGER AS jugend,
|
||||
COUNT(*) FILTER (WHERE status = 'reserve')::INTEGER AS reserve
|
||||
FROM mitglieder_profile
|
||||
`);
|
||||
|
||||
return (result.rows[0] as MemberStats) ?? {
|
||||
total: 0,
|
||||
aktiv: 0,
|
||||
passiv: 0,
|
||||
ehrenmitglied: 0,
|
||||
jugendfeuerwehr: 0,
|
||||
'anwärter': 0,
|
||||
ausgetreten: 0,
|
||||
kind: 0,
|
||||
jugend: 0,
|
||||
reserve: 0,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error fetching member stats', { error });
|
||||
|
||||
32
backend/src/types/multer.d.ts
vendored
32
backend/src/types/multer.d.ts
vendored
@@ -30,22 +30,22 @@ declare module 'multer' {
|
||||
}
|
||||
|
||||
declare namespace Express {
|
||||
namespace Multer {
|
||||
interface File {
|
||||
fieldname: string;
|
||||
originalname: string;
|
||||
encoding: string;
|
||||
mimetype: string;
|
||||
size: number;
|
||||
destination: string;
|
||||
filename: string;
|
||||
path: string;
|
||||
buffer: Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
interface Request {
|
||||
file?: {
|
||||
fieldname: string;
|
||||
originalname: string;
|
||||
encoding: string;
|
||||
mimetype: string;
|
||||
size: number;
|
||||
buffer: Buffer;
|
||||
};
|
||||
files?: {
|
||||
fieldname: string;
|
||||
originalname: string;
|
||||
encoding: string;
|
||||
mimetype: string;
|
||||
size: number;
|
||||
buffer: Buffer;
|
||||
}[];
|
||||
file?: Multer.File;
|
||||
files?: Multer.File[];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user