diff --git a/backend/src/main/resources/db/changelog/changes/099-seed-testdata.sql b/backend/src/main/resources/db/changelog/changes/099-seed-testdata.sql index efdf175..dcadc89 100644 --- a/backend/src/main/resources/db/changelog/changes/099-seed-testdata.sql +++ b/backend/src/main/resources/db/changelog/changes/099-seed-testdata.sql @@ -312,9 +312,9 @@ INSERT INTO stock_batches (id, stock_id, batch_id, batch_type, quantity_amount, -- Kräutermischung Leberwurst ('A0000000-0000-0000-0000-000000000009', '90000000-0000-0000-0000-000000000008', 'GK-2026-0115-001', 'PURCHASED', 3.500000, 'KILOGRAM', '2026-09-30', 'AVAILABLE', '2026-01-25 09:00:00+01'), -- Bratwurst in Theke (produziert) - ('A0000000-0000-0000-0000-000000000010', '90000000-0000-0000-0000-000000000009', 'BW-260223-01', 'PRODUCED', 8.500000, 'KILOGRAM', '2026-02-28', 'AVAILABLE', '2026-02-23 14:00:00+01'), + ('A0000000-0000-0000-0000-000000000010', '90000000-0000-0000-0000-000000000009', 'P-2026-02-23-001', 'PRODUCED', 8.500000, 'KILOGRAM', '2026-02-28', 'AVAILABLE', '2026-02-23 14:00:00+01'), -- Leberwurst in Theke (produziert) - ('A0000000-0000-0000-0000-000000000011', '90000000-0000-0000-0000-000000000010', 'LW-260222-01', 'PRODUCED', 4.200000, 'KILOGRAM', '2026-03-01', 'AVAILABLE', '2026-02-22 15:00:00+01'), + ('A0000000-0000-0000-0000-000000000011', '90000000-0000-0000-0000-000000000010', 'P-2026-02-22-001', 'PRODUCED', 4.200000, 'KILOGRAM', '2026-03-01', 'AVAILABLE', '2026-02-22 15:00:00+01'), -- Schinkenspeck in Theke (produziert) ('A0000000-0000-0000-0000-000000000012', '90000000-0000-0000-0000-000000000011', 'SS-260215-01', 'PRODUCED', 3.800000, 'KILOGRAM', '2026-03-15', 'AVAILABLE', '2026-02-15 16:00:00+01') ON CONFLICT (id) DO NOTHING; @@ -418,7 +418,7 @@ INSERT INTO batches (id, batch_number, recipe_id, status, created_at, updated_at, version) VALUES -- Bratwurst-Charge: abgeschlossen ('C0000000-0000-0000-0000-000000000001', - 'BW-260223-01', 'B0000000-0000-0000-0000-000000000001', 'COMPLETED', + 'P-2026-02-23-001', 'B0000000-0000-0000-0000-000000000001', 'COMPLETED', 10.000000, 'KILOGRAM', '2026-02-23', '2026-02-28', 9.200000, 'KILOGRAM', @@ -428,7 +428,7 @@ INSERT INTO batches (id, batch_number, recipe_id, status, -- Leberwurst-Charge: in Produktion ('C0000000-0000-0000-0000-000000000002', - 'LW-260222-01', 'B0000000-0000-0000-0000-000000000002', 'IN_PRODUCTION', + 'P-2026-02-22-001', 'B0000000-0000-0000-0000-000000000002', 'IN_PRODUCTION', 8.000000, 'KILOGRAM', '2026-02-22', '2026-03-01', NULL, NULL, @@ -519,7 +519,7 @@ INSERT INTO stock_movements (id, stock_id, article_id, stock_batch_id, batch_id, '90000000-0000-0000-0000-000000000001', '40000000-0000-0000-0000-000000000001', 'A0000000-0000-0000-0000-000000000001', 'BF-2026-0215-001', 'PURCHASED', 'PRODUCTION_CONSUMPTION', 'OUT', 7.000000, 'KILOGRAM', - 'Verbrauch für Charge BW-260223-01', 'BW-260223-01', + 'Verbrauch für Charge P-2026-02-23-001', 'P-2026-02-23-001', '10000000-0000-0000-0000-000000000001', '2026-02-23 07:00:00+01'), -- Produktionsverbrauch Bratwurst (Schweinebauch) @@ -527,21 +527,21 @@ INSERT INTO stock_movements (id, stock_id, article_id, stock_batch_id, batch_id, '90000000-0000-0000-0000-000000000002', '40000000-0000-0000-0000-000000000002', 'A0000000-0000-0000-0000-000000000003', 'BF-2026-0218-001', 'PURCHASED', 'PRODUCTION_CONSUMPTION', 'OUT', 3.000000, 'KILOGRAM', - 'Verbrauch für Charge BW-260223-01', 'BW-260223-01', + 'Verbrauch für Charge P-2026-02-23-001', 'P-2026-02-23-001', '10000000-0000-0000-0000-000000000001', '2026-02-23 07:00:00+01'), -- Produktionsoutput Bratwurst ('E0000000-0000-0000-0000-000000000007', '90000000-0000-0000-0000-000000000009', '40000000-0000-0000-0000-000000000006', - 'A0000000-0000-0000-0000-000000000010', 'BW-260223-01', 'PRODUCED', + 'A0000000-0000-0000-0000-000000000010', 'P-2026-02-23-001', 'PRODUCED', 'PRODUCTION_OUTPUT', 'IN', 9.200000, 'KILOGRAM', - 'Produktion Hausmacher Bratwurst, Charge BW-260223-01', 'BW-260223-01', + 'Produktion Hausmacher Bratwurst, Charge P-2026-02-23-001', 'P-2026-02-23-001', '10000000-0000-0000-0000-000000000001', '2026-02-23 14:00:00+01'), -- Verkauf Bratwurst aus Theke ('E0000000-0000-0000-0000-000000000008', '90000000-0000-0000-0000-000000000009', '40000000-0000-0000-0000-000000000006', - 'A0000000-0000-0000-0000-000000000010', 'BW-260223-01', 'PRODUCED', + 'A0000000-0000-0000-0000-000000000010', 'P-2026-02-23-001', 'PRODUCED', 'SALE', 'OUT', 0.700000, 'KILOGRAM', 'Thekenverkauf', NULL, '10000000-0000-0000-0000-000000000006', '2026-02-23 16:30:00+01'), diff --git a/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx b/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx index 2528919..4970021 100644 --- a/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx +++ b/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx @@ -1,8 +1,11 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { Box, Text, useInput } from 'ink'; import TextInput from 'ink-text-input'; import { useNavigation } from '../../state/navigation-context.js'; import { useProductionOrders } from '../../hooks/useProductionOrders.js'; +import { useRecipeNameLookup } from '../../hooks/useRecipeNameLookup.js'; +import { client } from '../../utils/api-client.js'; +import type { BatchDTO } from '@effigenix/api-client'; import { LoadingSpinner } from '../shared/LoadingSpinner.js'; import { ErrorDisplay } from '../shared/ErrorDisplay.js'; import { SuccessDisplay } from '../shared/SuccessDisplay.js'; @@ -25,11 +28,13 @@ export function ProductionOrderDetailScreen() { productionOrder, loading, error, fetchProductionOrder, releaseProductionOrder, rescheduleProductionOrder, startProductionOrder, clearError, } = useProductionOrders(); + const { recipeName } = useRecipeNameLookup(); const [mode, setMode] = useState('view'); const [menuIndex, setMenuIndex] = useState(0); const [batchId, setBatchId] = useState(''); const [newDate, setNewDate] = useState(''); const [success, setSuccess] = useState(null); + const [batch, setBatch] = useState(null); const orderId = params.orderId ?? ''; @@ -37,6 +42,19 @@ export function ProductionOrderDetailScreen() { if (orderId) void fetchProductionOrder(orderId); }, [fetchProductionOrder, orderId]); + const loadBatch = useCallback(async (id: string) => { + try { + const b = await client.batches.getById(id); + setBatch(b); + } catch (err) { + setBatch(null); + } + }, []); + + useEffect(() => { + if (productionOrder?.batchId) void loadBatch(productionOrder.batchId); + }, [loadBatch, productionOrder?.batchId]); + const getMenuItems = () => { const items: { label: string; action: string }[] = []; const status = productionOrder?.status; @@ -154,13 +172,13 @@ export function ProductionOrderDetailScreen() { ID: {productionOrder.id} - Rezept-ID: {productionOrder.recipeId} + Rezept: {recipeName(productionOrder.recipeId ?? '') ?? productionOrder.recipeId} ({productionOrder.recipeId}) Status: {statusLabel} Menge: {productionOrder.plannedQuantity} {productionOrder.plannedQuantityUnit} Geplant am: {productionOrder.plannedDate} Priorität: {prioLabel} {productionOrder.batchId && ( - Chargen-ID: {productionOrder.batchId} + Chargen-Nr: {batch?.batchNumber ?? productionOrder.batchId} )} {productionOrder.notes && ( Notizen: {productionOrder.notes} @@ -169,6 +187,25 @@ export function ProductionOrderDetailScreen() { Aktualisiert: {updatedAt} + {batch && productionOrder.batchId && ( + + Produktionsergebnis + Soll-Menge: {batch.plannedQuantity} {batch.plannedQuantityUnit} + {batch.actualQuantity && ( + Ist-Menge: {batch.actualQuantity} {batch.actualQuantityUnit} + )} + {batch.waste && ( + Ausschuss: {batch.waste} {batch.wasteUnit} + )} + {batch.remarks && ( + Bemerkungen: {batch.remarks} + )} + {batch.completedAt && ( + Abgeschl. am: {new Date(batch.completedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' })} + )} + + )} + {mode === 'menu' && ( Aktionen diff --git a/frontend/apps/cli/src/hooks/useRecipeNameLookup.ts b/frontend/apps/cli/src/hooks/useRecipeNameLookup.ts new file mode 100644 index 0000000..498aa15 --- /dev/null +++ b/frontend/apps/cli/src/hooks/useRecipeNameLookup.ts @@ -0,0 +1,23 @@ +import { useEffect, useMemo, useCallback } from 'react'; +import { useRecipes } from './useRecipes.js'; + +export function useRecipeNameLookup() { + const { recipes, fetchRecipes } = useRecipes(); + + useEffect(() => { + void fetchRecipes(); + }, [fetchRecipes]); + + const recipeNames = useMemo(() => { + const map = new Map(); + for (const r of recipes) map.set(r.id, r.name); + return map; + }, [recipes]); + + const recipeName = useCallback( + (id: string) => recipeNames.get(id) ?? null, + [recipeNames], + ); + + return { recipeName }; +}