feat(ausruestungsanfrage): add personal item tracking, catalog enforcement, and detail pages
This commit is contained in:
@@ -19,6 +19,7 @@ interface CreatePersonalEquipmentData {
|
||||
anschaffung_datum?: string;
|
||||
zustand?: string;
|
||||
notizen?: string;
|
||||
eigenschaften?: { eigenschaft_id?: number; name: string; wert: string }[];
|
||||
}
|
||||
|
||||
interface UpdatePersonalEquipmentData {
|
||||
@@ -33,6 +34,7 @@ interface UpdatePersonalEquipmentData {
|
||||
anschaffung_datum?: string | null;
|
||||
zustand?: string;
|
||||
notizen?: string | null;
|
||||
eigenschaften?: { eigenschaft_id?: number | null; name: string; wert: string }[] | null;
|
||||
}
|
||||
|
||||
const BASE_SELECT = `
|
||||
@@ -45,6 +47,23 @@ const BASE_SELECT = `
|
||||
WHERE pa.geloescht_am IS NULL
|
||||
`;
|
||||
|
||||
async function loadEigenschaften(ids: string[]) {
|
||||
if (ids.length === 0) return new Map<string, { id: number; persoenlich_id: string; eigenschaft_id: number | null; name: string; wert: string }[]>();
|
||||
const result = await pool.query(
|
||||
`SELECT pae.id, pae.persoenlich_id, pae.eigenschaft_id, pae.name, pae.wert
|
||||
FROM persoenliche_ausruestung_eigenschaften pae
|
||||
WHERE pae.persoenlich_id = ANY($1)`,
|
||||
[ids],
|
||||
);
|
||||
const map = new Map<string, typeof result.rows>();
|
||||
for (const row of result.rows) {
|
||||
const arr = map.get(row.persoenlich_id) || [];
|
||||
arr.push(row);
|
||||
map.set(row.persoenlich_id, arr);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
async function getAll(filters: PersonalEquipmentFilters = {}) {
|
||||
try {
|
||||
const conditions: string[] = [];
|
||||
@@ -68,6 +87,13 @@ async function getAll(filters: PersonalEquipmentFilters = {}) {
|
||||
`${BASE_SELECT}${where} ORDER BY pa.bezeichnung`,
|
||||
params,
|
||||
);
|
||||
|
||||
const ids = result.rows.map((r: { id: string }) => r.id);
|
||||
const eigMap = await loadEigenschaften(ids);
|
||||
for (const row of result.rows) {
|
||||
row.eigenschaften = eigMap.get(row.id) || [];
|
||||
}
|
||||
|
||||
return result.rows;
|
||||
} catch (error) {
|
||||
logger.error('personalEquipmentService.getAll failed', { error });
|
||||
@@ -81,6 +107,13 @@ async function getByUserId(userId: string) {
|
||||
`${BASE_SELECT} AND pa.user_id = $1 ORDER BY pa.bezeichnung`,
|
||||
[userId],
|
||||
);
|
||||
|
||||
const ids = result.rows.map((r: { id: string }) => r.id);
|
||||
const eigMap = await loadEigenschaften(ids);
|
||||
for (const row of result.rows) {
|
||||
row.eigenschaften = eigMap.get(row.id) || [];
|
||||
}
|
||||
|
||||
return result.rows;
|
||||
} catch (error) {
|
||||
logger.error('personalEquipmentService.getByUserId failed', { error, userId });
|
||||
@@ -94,7 +127,17 @@ async function getById(id: string) {
|
||||
`${BASE_SELECT} AND pa.id = $1`,
|
||||
[id],
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
if (!result.rows[0]) return null;
|
||||
|
||||
const eigResult = await pool.query(
|
||||
`SELECT pae.id, pae.persoenlich_id, pae.eigenschaft_id, pae.name, pae.wert
|
||||
FROM persoenliche_ausruestung_eigenschaften pae
|
||||
WHERE pae.persoenlich_id = $1`,
|
||||
[id],
|
||||
);
|
||||
result.rows[0].eigenschaften = eigResult.rows;
|
||||
|
||||
return result.rows[0];
|
||||
} catch (error) {
|
||||
logger.error('personalEquipmentService.getById failed', { error, id });
|
||||
throw new Error('Failed to fetch personal equipment item');
|
||||
@@ -125,8 +168,20 @@ async function create(data: CreatePersonalEquipmentData, requestingUserId: strin
|
||||
requestingUserId,
|
||||
],
|
||||
);
|
||||
logger.info('Personal equipment created', { id: result.rows[0].id, by: requestingUserId });
|
||||
return result.rows[0];
|
||||
const created = result.rows[0];
|
||||
logger.info('Personal equipment created', { id: created.id, by: requestingUserId });
|
||||
|
||||
if (data.eigenschaften && data.eigenschaften.length > 0) {
|
||||
for (const e of data.eigenschaften) {
|
||||
await pool.query(
|
||||
`INSERT INTO persoenliche_ausruestung_eigenschaften (persoenlich_id, eigenschaft_id, name, wert)
|
||||
VALUES ($1, $2, $3, $4)`,
|
||||
[created.id, e.eigenschaft_id ?? null, e.name, e.wert],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return created;
|
||||
} catch (error) {
|
||||
logger.error('personalEquipmentService.create failed', { error });
|
||||
throw new Error('Failed to create personal equipment');
|
||||
@@ -156,19 +211,46 @@ async function update(id: string, data: UpdatePersonalEquipmentData) {
|
||||
if (data.zustand !== undefined) addField('zustand', data.zustand);
|
||||
if (data.notizen !== undefined) addField('notizen', data.notizen);
|
||||
|
||||
if (fields.length === 0) {
|
||||
if (fields.length === 0 && data.eigenschaften === undefined) {
|
||||
throw new Error('No fields to update');
|
||||
}
|
||||
|
||||
values.push(id);
|
||||
const result = await pool.query(
|
||||
`UPDATE persoenliche_ausruestung SET ${fields.join(', ')} WHERE id = $${p} AND geloescht_am IS NULL RETURNING *`,
|
||||
values,
|
||||
);
|
||||
let updated = null;
|
||||
if (fields.length > 0) {
|
||||
values.push(id);
|
||||
const result = await pool.query(
|
||||
`UPDATE persoenliche_ausruestung SET ${fields.join(', ')} WHERE id = $${p} AND geloescht_am IS NULL RETURNING *`,
|
||||
values,
|
||||
);
|
||||
if (result.rows.length === 0) return null;
|
||||
updated = result.rows[0];
|
||||
}
|
||||
|
||||
if (data.eigenschaften !== undefined) {
|
||||
await pool.query('DELETE FROM persoenliche_ausruestung_eigenschaften WHERE persoenlich_id = $1', [id]);
|
||||
if (data.eigenschaften && data.eigenschaften.length > 0) {
|
||||
for (const e of data.eigenschaften) {
|
||||
await pool.query(
|
||||
`INSERT INTO persoenliche_ausruestung_eigenschaften (persoenlich_id, eigenschaft_id, name, wert)
|
||||
VALUES ($1, $2, $3, $4)`,
|
||||
[id, e.eigenschaft_id ?? null, e.name, e.wert],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!updated) {
|
||||
// Only eigenschaften were updated, fetch the row
|
||||
const result = await pool.query(
|
||||
`SELECT * FROM persoenliche_ausruestung WHERE id = $1 AND geloescht_am IS NULL`,
|
||||
[id],
|
||||
);
|
||||
if (result.rows.length === 0) return null;
|
||||
updated = result.rows[0];
|
||||
}
|
||||
|
||||
if (result.rows.length === 0) return null;
|
||||
logger.info('Personal equipment updated', { id });
|
||||
return result.rows[0];
|
||||
return updated;
|
||||
} catch (error) {
|
||||
logger.error('personalEquipmentService.update failed', { error, id });
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user