shared catalog in Bestellungen, catalog picker in line items, Ersatzbeschaffung flag, vendor detail flash fix
This commit is contained in:
@@ -422,7 +422,7 @@ async function getRequestById(id: number) {
|
||||
|
||||
async function createRequest(
|
||||
userId: string,
|
||||
items: { artikel_id?: number; bezeichnung: string; menge: number; notizen?: string; eigenschaften?: { eigenschaft_id: number; wert: string }[] }[],
|
||||
items: { artikel_id?: number; bezeichnung: string; menge: number; notizen?: string; ist_ersatz?: boolean; eigenschaften?: { eigenschaft_id: number; wert: string }[] }[],
|
||||
notizen?: string,
|
||||
bezeichnung?: string,
|
||||
fuerBenutzerName?: string,
|
||||
@@ -476,9 +476,9 @@ async function createRequest(
|
||||
}
|
||||
|
||||
await client.query(
|
||||
`INSERT INTO ausruestung_anfrage_positionen (anfrage_id, artikel_id, bezeichnung, menge, notizen)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[anfrage.id, item.artikel_id || null, itemBezeichnung, item.menge, item.notizen || null],
|
||||
`INSERT INTO ausruestung_anfrage_positionen (anfrage_id, artikel_id, bezeichnung, menge, notizen, ist_ersatz)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||
[anfrage.id, item.artikel_id || null, itemBezeichnung, item.menge, item.notizen || null, item.ist_ersatz ?? false],
|
||||
);
|
||||
|
||||
// NOTE: eigenschaft values are NOT saved in the transaction to avoid
|
||||
@@ -527,7 +527,7 @@ async function updateRequest(
|
||||
data: {
|
||||
bezeichnung?: string;
|
||||
notizen?: string;
|
||||
items?: { artikel_id?: number; bezeichnung: string; menge: number; notizen?: string; eigenschaften?: { eigenschaft_id: number; wert: string }[] }[];
|
||||
items?: { artikel_id?: number; bezeichnung: string; menge: number; notizen?: string; ist_ersatz?: boolean; eigenschaften?: { eigenschaft_id: number; wert: string }[] }[];
|
||||
},
|
||||
) {
|
||||
const client = await pool.connect();
|
||||
@@ -581,9 +581,9 @@ async function updateRequest(
|
||||
}
|
||||
const prevGeliefert = geliefertMap.get(`${item.artikel_id ?? 0}:${itemBezeichnung}`) ?? false;
|
||||
await client.query(
|
||||
`INSERT INTO ausruestung_anfrage_positionen (anfrage_id, artikel_id, bezeichnung, menge, notizen, geliefert)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||
[id, item.artikel_id || null, itemBezeichnung, item.menge, item.notizen || null, prevGeliefert],
|
||||
`INSERT INTO ausruestung_anfrage_positionen (anfrage_id, artikel_id, bezeichnung, menge, notizen, geliefert, ist_ersatz)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||
[id, item.artikel_id || null, itemBezeichnung, item.menge, item.notizen || null, prevGeliefert, item.ist_ersatz ?? false],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -652,6 +652,14 @@ async function updatePositionGeliefert(positionId: number, geliefert: boolean) {
|
||||
return position;
|
||||
}
|
||||
|
||||
async function updatePositionZurueckgegeben(positionId: number, zurueckgegeben: boolean) {
|
||||
const result = await pool.query(
|
||||
`UPDATE ausruestung_anfrage_positionen SET altes_geraet_zurueckgegeben = $1 WHERE id = $2 RETURNING *`,
|
||||
[zurueckgegeben, positionId],
|
||||
);
|
||||
return result.rows[0] || null;
|
||||
}
|
||||
|
||||
async function updateRequestStatus(
|
||||
id: number,
|
||||
status: string,
|
||||
@@ -768,10 +776,35 @@ async function createOrdersFromRequest(
|
||||
const bestellung = bestellungResult.rows[0];
|
||||
|
||||
for (const pos of orderData.positionen) {
|
||||
// Look up the anfrage position to get artikel_id and eigenschaften
|
||||
let artikelId: number | null = null;
|
||||
let spezifikationen: string[] = [];
|
||||
if (pos.position_id) {
|
||||
const posResult = await client.query(
|
||||
`SELECT p.artikel_id FROM ausruestung_anfrage_positionen p WHERE p.id = $1`,
|
||||
[pos.position_id]
|
||||
);
|
||||
if (posResult.rows.length > 0) {
|
||||
artikelId = posResult.rows[0].artikel_id || null;
|
||||
}
|
||||
// Load eigenschaften and map to spezifikationen strings
|
||||
try {
|
||||
const eigResult = await client.query(
|
||||
`SELECT ae.name AS eigenschaft_name, pe.wert
|
||||
FROM ausruestung_position_eigenschaften pe
|
||||
JOIN ausruestung_artikel_eigenschaften ae ON ae.id = pe.eigenschaft_id
|
||||
WHERE pe.position_id = $1
|
||||
ORDER BY ae.sort_order, ae.id`,
|
||||
[pos.position_id]
|
||||
);
|
||||
spezifikationen = eigResult.rows.map((e: { eigenschaft_name: string; wert: string }) => `${e.eigenschaft_name}: ${e.wert}`);
|
||||
} catch { /* table may not exist */ }
|
||||
}
|
||||
|
||||
await client.query(
|
||||
`INSERT INTO bestellpositionen (bestellung_id, bezeichnung, menge, einheit, notizen)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[bestellung.id, pos.bezeichnung, pos.menge, pos.einheit || 'Stk', pos.notizen || null]
|
||||
`INSERT INTO bestellpositionen (bestellung_id, bezeichnung, menge, einheit, notizen, artikel_id, spezifikationen)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)`,
|
||||
[bestellung.id, pos.bezeichnung, pos.menge, pos.einheit || 'Stk', pos.notizen || null, artikelId, JSON.stringify(spezifikationen)]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -875,6 +908,7 @@ export default {
|
||||
getMyRequests,
|
||||
getRequestById,
|
||||
updatePositionGeliefert,
|
||||
updatePositionZurueckgegeben,
|
||||
createRequest,
|
||||
updateRequest,
|
||||
updateRequestStatus,
|
||||
|
||||
@@ -7,6 +7,38 @@ import logger from '../utils/logger';
|
||||
import fs from 'fs';
|
||||
import notificationService from './notification.service';
|
||||
import { permissionService } from './permission.service';
|
||||
import ausruestungsanfrageService from './ausruestungsanfrage.service';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Catalog (shared ausruestung_artikel via ausruestungsanfrageService)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function getKatalogItems(filters?: { search?: string; kategorie?: string }) {
|
||||
try {
|
||||
return await ausruestungsanfrageService.getItems({ search: filters?.search, kategorie: filters?.kategorie, aktiv: true });
|
||||
} catch (error) {
|
||||
logger.error('BestellungService.getKatalogItems failed', { error });
|
||||
throw new Error('Katalogartikel konnten nicht geladen werden');
|
||||
}
|
||||
}
|
||||
|
||||
async function getKatalogItem(id: number) {
|
||||
try {
|
||||
return await ausruestungsanfrageService.getItemById(id);
|
||||
} catch (error) {
|
||||
logger.error('BestellungService.getKatalogItem failed', { error, id });
|
||||
throw new Error('Katalogartikel konnte nicht geladen werden');
|
||||
}
|
||||
}
|
||||
|
||||
async function getKatalogKategorien() {
|
||||
try {
|
||||
return await ausruestungsanfrageService.getKategorien();
|
||||
} catch (error) {
|
||||
logger.error('BestellungService.getKatalogKategorien failed', { error });
|
||||
throw new Error('Katalogkategorien konnten nicht geladen werden');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Vendors (Lieferanten)
|
||||
@@ -168,7 +200,13 @@ async function getOrderById(id: number) {
|
||||
if (orderResult.rows.length === 0) return null;
|
||||
|
||||
const [positionen, dateien, erinnerungen, historie] = await Promise.all([
|
||||
pool.query(`SELECT * FROM bestellpositionen WHERE bestellung_id = $1 ORDER BY id`, [id]),
|
||||
pool.query(
|
||||
`SELECT bp.*, aa.bezeichnung AS artikel_bezeichnung
|
||||
FROM bestellpositionen bp
|
||||
LEFT JOIN ausruestung_artikel aa ON aa.id = bp.artikel_id
|
||||
WHERE bp.bestellung_id = $1 ORDER BY bp.id`,
|
||||
[id]
|
||||
),
|
||||
pool.query(`SELECT * FROM bestellung_dateien WHERE bestellung_id = $1 ORDER BY hochgeladen_am DESC`, [id]),
|
||||
pool.query(`SELECT * FROM bestellung_erinnerungen WHERE bestellung_id = $1 ORDER BY faellig_am`, [id]),
|
||||
pool.query(`SELECT h.*, COALESCE(u.name, u.preferred_username, u.email) AS benutzer_name FROM bestellung_historie h LEFT JOIN users u ON u.id = h.erstellt_von WHERE h.bestellung_id = $1 ORDER BY h.erstellt_am DESC`, [id]),
|
||||
@@ -425,13 +463,13 @@ async function updateOrderStatus(id: number, status: string, userId: string, for
|
||||
// Line Items (Bestellpositionen)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function addLineItem(bestellungId: number, data: { bezeichnung: string; artikelnummer?: string; menge: number; einheit?: string; einzelpreis?: number; notizen?: string; spezifikationen?: string[] }, userId: string) {
|
||||
async function addLineItem(bestellungId: number, data: { bezeichnung: string; artikelnummer?: string; menge: number; einheit?: string; einzelpreis?: number; notizen?: string; spezifikationen?: string[]; artikel_id?: number }, userId: string) {
|
||||
try {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO bestellpositionen (bestellung_id, bezeichnung, artikelnummer, menge, einheit, einzelpreis, notizen, spezifikationen)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)
|
||||
`INSERT INTO bestellpositionen (bestellung_id, bezeichnung, artikelnummer, menge, einheit, einzelpreis, notizen, spezifikationen, artikel_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)
|
||||
RETURNING *`,
|
||||
[bestellungId, data.bezeichnung, data.artikelnummer || null, data.menge, data.einheit || 'Stk', data.einzelpreis || 0, data.notizen || null, JSON.stringify(data.spezifikationen || [])]
|
||||
[bestellungId, data.bezeichnung, data.artikelnummer || null, data.menge, data.einheit || 'Stk', data.einzelpreis || 0, data.notizen || null, JSON.stringify(data.spezifikationen || []), data.artikel_id || null]
|
||||
);
|
||||
await logAction(bestellungId, 'Position hinzugefügt', `"${data.bezeichnung}" x${data.menge}`, userId);
|
||||
return result.rows[0];
|
||||
@@ -693,6 +731,10 @@ async function getHistory(bestellungId: number) {
|
||||
}
|
||||
|
||||
export default {
|
||||
// Catalog
|
||||
getKatalogItems,
|
||||
getKatalogItem,
|
||||
getKatalogKategorien,
|
||||
// Vendors
|
||||
getVendors,
|
||||
getVendorById,
|
||||
|
||||
Reference in New Issue
Block a user