1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 08:29:36 +01:00

fix(tui,seed): Seed-Batch-Nummern korrigieren und Produktionsergebnis anzeigen

Seed-Daten: BW-260223-01 → P-2026-02-23-001, LW-260222-01 → P-2026-02-22-001,
damit die Chargennummern dem BatchNumber-VO-Format entsprechen.

ProductionOrderDetailScreen: Rezeptname statt ID anzeigen, Batch-Daten
(Soll-/Ist-Menge, Ausschuss, Bemerkungen) bei verknüpfter Charge laden.
This commit is contained in:
Sebastian Frick 2026-02-26 08:49:19 +01:00
parent 417f8fcdae
commit 26adf21162
3 changed files with 72 additions and 12 deletions

View file

@ -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'),

View file

@ -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<Mode>('view');
const [menuIndex, setMenuIndex] = useState(0);
const [batchId, setBatchId] = useState('');
const [newDate, setNewDate] = useState('');
const [success, setSuccess] = useState<string | null>(null);
const [batch, setBatch] = useState<BatchDTO | null>(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() {
<Box flexDirection="column" borderStyle="round" borderColor="gray" paddingX={1}>
<Box><Text color="gray">ID: </Text><Text>{productionOrder.id}</Text></Box>
<Box><Text color="gray">Rezept-ID: </Text><Text>{productionOrder.recipeId}</Text></Box>
<Box><Text color="gray">Rezept: </Text><Text>{recipeName(productionOrder.recipeId ?? '') ?? productionOrder.recipeId}</Text><Text color="gray" dimColor> ({productionOrder.recipeId})</Text></Box>
<Box><Text color="gray">Status: </Text><Text color={statusColor}>{statusLabel}</Text></Box>
<Box><Text color="gray">Menge: </Text><Text>{productionOrder.plannedQuantity} {productionOrder.plannedQuantityUnit}</Text></Box>
<Box><Text color="gray">Geplant am: </Text><Text>{productionOrder.plannedDate}</Text></Box>
<Box><Text color="gray">Priorität: </Text><Text>{prioLabel}</Text></Box>
{productionOrder.batchId && (
<Box><Text color="gray">Chargen-ID: </Text><Text>{productionOrder.batchId}</Text></Box>
<Box><Text color="gray">Chargen-Nr: </Text><Text>{batch?.batchNumber ?? productionOrder.batchId}</Text></Box>
)}
{productionOrder.notes && (
<Box><Text color="gray">Notizen: </Text><Text>{productionOrder.notes}</Text></Box>
@ -169,6 +187,25 @@ export function ProductionOrderDetailScreen() {
<Box><Text color="gray">Aktualisiert:</Text><Text> {updatedAt}</Text></Box>
</Box>
{batch && productionOrder.batchId && (
<Box flexDirection="column" borderStyle="round" borderColor="gray" paddingX={1}>
<Text color="cyan" bold>Produktionsergebnis</Text>
<Box><Text color="gray">Soll-Menge: </Text><Text>{batch.plannedQuantity} {batch.plannedQuantityUnit}</Text></Box>
{batch.actualQuantity && (
<Box><Text color="gray">Ist-Menge: </Text><Text color="green">{batch.actualQuantity} {batch.actualQuantityUnit}</Text></Box>
)}
{batch.waste && (
<Box><Text color="gray">Ausschuss: </Text><Text color="red">{batch.waste} {batch.wasteUnit}</Text></Box>
)}
{batch.remarks && (
<Box><Text color="gray">Bemerkungen: </Text><Text>{batch.remarks}</Text></Box>
)}
{batch.completedAt && (
<Box><Text color="gray">Abgeschl. am:</Text><Text> {new Date(batch.completedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' })}</Text></Box>
)}
</Box>
)}
{mode === 'menu' && (
<Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
<Text color="cyan" bold>Aktionen</Text>

View file

@ -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<string, string>();
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 };
}