diff --git a/frontend/apps/cli/src/App.tsx b/frontend/apps/cli/src/App.tsx
index e48e0a8..4ca23d2 100644
--- a/frontend/apps/cli/src/App.tsx
+++ b/frontend/apps/cli/src/App.tsx
@@ -39,6 +39,9 @@ import { StorageLocationDetailScreen } from './components/inventory/StorageLocat
import { StorageLocationEditScreen } from './components/inventory/StorageLocationEditScreen.js';
import { StockBatchEntryScreen } from './components/inventory/StockBatchEntryScreen.js';
import { AddBatchScreen } from './components/inventory/AddBatchScreen.js';
+import { StockMovementListScreen } from './components/inventory/StockMovementListScreen.js';
+import { StockMovementDetailScreen } from './components/inventory/StockMovementDetailScreen.js';
+import { StockMovementRecordScreen } from './components/inventory/StockMovementRecordScreen.js';
// Produktion
import { ProductionMenu } from './components/production/ProductionMenu.js';
import { RecipeListScreen } from './components/production/RecipeListScreen.js';
@@ -51,7 +54,9 @@ import { BatchDetailScreen } from './components/production/BatchDetailScreen.js'
import { BatchPlanScreen } from './components/production/BatchPlanScreen.js';
import { RecordConsumptionScreen } from './components/production/RecordConsumptionScreen.js';
import { CompleteBatchScreen } from './components/production/CompleteBatchScreen.js';
+import { ProductionOrderListScreen } from './components/production/ProductionOrderListScreen.js';
import { ProductionOrderCreateScreen } from './components/production/ProductionOrderCreateScreen.js';
+import { ProductionOrderDetailScreen } from './components/production/ProductionOrderDetailScreen.js';
import { StockListScreen } from './components/inventory/StockListScreen.js';
import { StockDetailScreen } from './components/inventory/StockDetailScreen.js';
import { StockCreateScreen } from './components/inventory/StockCreateScreen.js';
@@ -124,6 +129,9 @@ function ScreenRouter() {
{current === 'stock-detail' && }
{current === 'stock-create' && }
{current === 'stock-reserve' && }
+ {current === 'stock-movement-list' && }
+ {current === 'stock-movement-detail' && }
+ {current === 'stock-movement-record' && }
{/* Produktion */}
{current === 'production-menu' && }
{current === 'recipe-list' && }
@@ -136,7 +144,9 @@ function ScreenRouter() {
{current === 'batch-plan' && }
{current === 'batch-record-consumption' && }
{current === 'batch-complete' && }
+ {current === 'production-order-list' && }
{current === 'production-order-create' && }
+ {current === 'production-order-detail' && }
);
}
diff --git a/frontend/apps/cli/src/components/inventory/InventoryMenu.tsx b/frontend/apps/cli/src/components/inventory/InventoryMenu.tsx
index 4f72e55..23ac13f 100644
--- a/frontend/apps/cli/src/components/inventory/InventoryMenu.tsx
+++ b/frontend/apps/cli/src/components/inventory/InventoryMenu.tsx
@@ -12,6 +12,7 @@ interface MenuItem {
const MENU_ITEMS: MenuItem[] = [
{ label: 'Lagerorte', screen: 'storage-location-list', description: 'Lagerorte verwalten (Kühlräume, Trockenlager, …)' },
{ label: 'Bestände', screen: 'stock-list', description: 'Bestände einsehen, anlegen und Chargen verwalten' },
+ { label: 'Bestandsbewegungen', screen: 'stock-movement-list', description: 'Wareneingänge, Verbräuche, Korrekturen und Umlagerungen' },
{ label: 'Charge einbuchen', screen: 'stock-batch-entry', description: 'Neue Charge in einen Bestand einbuchen' },
];
diff --git a/frontend/apps/cli/src/components/inventory/StockMovementDetailScreen.tsx b/frontend/apps/cli/src/components/inventory/StockMovementDetailScreen.tsx
new file mode 100644
index 0000000..2d0c5db
--- /dev/null
+++ b/frontend/apps/cli/src/components/inventory/StockMovementDetailScreen.tsx
@@ -0,0 +1,81 @@
+import React, { useEffect } from 'react';
+import { Box, Text, useInput } from 'ink';
+import { useNavigation } from '../../state/navigation-context.js';
+import { useStockMovements } from '../../hooks/useStockMovements.js';
+import { LoadingSpinner } from '../shared/LoadingSpinner.js';
+import { ErrorDisplay } from '../shared/ErrorDisplay.js';
+import { MOVEMENT_TYPE_LABELS, MOVEMENT_DIRECTION_LABELS } from '@effigenix/api-client';
+import type { MovementType, MovementDirection } from '@effigenix/api-client';
+
+const DIRECTION_COLORS: Record = {
+ IN: 'green',
+ OUT: 'red',
+};
+
+export function StockMovementDetailScreen() {
+ const { params, back } = useNavigation();
+ const { movement, loading, error, fetchMovement, clearError } = useStockMovements();
+
+ const movementId = params.movementId ?? '';
+
+ useEffect(() => {
+ if (movementId) void fetchMovement(movementId);
+ }, [fetchMovement, movementId]);
+
+ useInput((_input, key) => {
+ if (key.backspace || key.escape) back();
+ });
+
+ if (loading && !movement) return ;
+
+ if (!movement) {
+ return (
+
+ {error && }
+ Bewegung nicht gefunden.
+
+ );
+ }
+
+ const typeLabel = MOVEMENT_TYPE_LABELS[movement.movementType as MovementType] ?? movement.movementType;
+ const dirLabel = MOVEMENT_DIRECTION_LABELS[movement.direction as MovementDirection] ?? movement.direction;
+ const dirColor = DIRECTION_COLORS[movement.direction] ?? 'white';
+ const dateStr = movement.performedAt
+ ? new Date(movement.performedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'medium' })
+ : '';
+
+ return (
+
+
+ Bestandsbewegung
+ {loading && (aktualisiere...)}
+
+
+ {error && }
+
+
+ ID: {movement.id}
+ Typ: {typeLabel}
+ Richtung: {dirLabel}
+ Menge: {movement.quantityAmount} {movement.quantityUnit}
+ Stock-ID: {movement.stockId}
+ Artikel-ID: {movement.articleId}
+ Chargen-Batch: {movement.stockBatchId}
+ Chargen-Nr.: {movement.batchId}
+ Chargen-Typ: {movement.batchType}
+ {movement.reason && (
+ Grund: {movement.reason}
+ )}
+ {movement.referenceDocumentId && (
+ Referenz-Dok.: {movement.referenceDocumentId}
+ )}
+ Durchgeführt von: {movement.performedBy}
+ Zeitpunkt: {dateStr}
+
+
+
+ Backspace Zurück
+
+
+ );
+}
diff --git a/frontend/apps/cli/src/components/inventory/StockMovementListScreen.tsx b/frontend/apps/cli/src/components/inventory/StockMovementListScreen.tsx
new file mode 100644
index 0000000..a90a979
--- /dev/null
+++ b/frontend/apps/cli/src/components/inventory/StockMovementListScreen.tsx
@@ -0,0 +1,127 @@
+import React, { useEffect, useState } from 'react';
+import { Box, Text, useInput } from 'ink';
+import { useNavigation } from '../../state/navigation-context.js';
+import { useStockMovements } from '../../hooks/useStockMovements.js';
+import { LoadingSpinner } from '../shared/LoadingSpinner.js';
+import { ErrorDisplay } from '../shared/ErrorDisplay.js';
+import { MOVEMENT_TYPE_LABELS, MOVEMENT_DIRECTION_LABELS } from '@effigenix/api-client';
+import type { MovementType, MovementDirection, StockMovementFilter } from '@effigenix/api-client';
+
+const DIRECTION_COLORS: Record = {
+ IN: 'green',
+ OUT: 'red',
+};
+
+const TYPE_FILTER_OPTIONS: { key: string; label: string; value: string | undefined }[] = [
+ { key: 'a', label: 'ALLE', value: undefined },
+ { key: '1', label: 'Wareneingang', value: 'GOODS_RECEIPT' },
+ { key: '2', label: 'Prod.-Ausstoß', value: 'PRODUCTION_OUTPUT' },
+ { key: '3', label: 'Prod.-Verbrauch', value: 'PRODUCTION_CONSUMPTION' },
+ { key: '4', label: 'Verkauf', value: 'SALE' },
+ { key: '5', label: 'Retoure', value: 'RETURN' },
+ { key: '6', label: 'Ausschuss', value: 'WASTE' },
+ { key: '7', label: 'Korrektur', value: 'ADJUSTMENT' },
+ { key: '8', label: 'Umlagerung', value: 'INTER_BRANCH_TRANSFER' },
+];
+
+export function StockMovementListScreen() {
+ const { navigate, back, params } = useNavigation();
+ const { movements, loading, error, fetchMovements, clearError } = useStockMovements();
+ const [selectedIndex, setSelectedIndex] = useState(0);
+ const [typeFilter, setTypeFilter] = useState(undefined);
+
+ const stockId = params.stockId;
+
+ useEffect(() => {
+ const filter: StockMovementFilter = {};
+ if (stockId) filter.stockId = stockId;
+ if (typeFilter) filter.movementType = typeFilter;
+ void fetchMovements(filter);
+ }, [fetchMovements, stockId, typeFilter]);
+
+ useInput((input, key) => {
+ if (loading) return;
+
+ if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
+ if (key.downArrow) setSelectedIndex((i) => Math.min(movements.length - 1, i + 1));
+
+ if (key.return && movements.length > 0) {
+ const movement = movements[selectedIndex];
+ if (movement) navigate('stock-movement-detail', { movementId: movement.id });
+ }
+ if (input === 'n') navigate('stock-movement-record', stockId ? { stockId } : {});
+ if (input === 'r') {
+ const filter: StockMovementFilter = {};
+ if (stockId) filter.stockId = stockId;
+ if (typeFilter) filter.movementType = typeFilter;
+ void fetchMovements(filter);
+ }
+ if (key.backspace || key.escape) back();
+
+ for (const opt of TYPE_FILTER_OPTIONS) {
+ if (input === opt.key) {
+ setTypeFilter(opt.value);
+ setSelectedIndex(0);
+ break;
+ }
+ }
+ });
+
+ const activeFilterLabel = TYPE_FILTER_OPTIONS.find((f) => f.value === typeFilter)?.label ?? 'ALLE';
+
+ return (
+
+
+ Bestandsbewegungen
+ ({movements.length})
+ Filter:
+ {activeFilterLabel}
+
+
+ {loading && }
+ {error && !loading && }
+
+ {!loading && !error && (
+
+
+ {' Typ'.padEnd(22)}
+ {'Richtung'.padEnd(10)}
+ {'Menge'.padEnd(16)}
+ {'Charge'.padEnd(16)}
+ Zeitpunkt
+
+ {movements.length === 0 && (
+
+ Keine Bewegungen gefunden.
+
+ )}
+ {movements.map((m, index) => {
+ const isSelected = index === selectedIndex;
+ const textColor = isSelected ? 'cyan' : 'white';
+ const typeLabel = MOVEMENT_TYPE_LABELS[m.movementType as MovementType] ?? m.movementType;
+ const dirLabel = MOVEMENT_DIRECTION_LABELS[m.direction as MovementDirection] ?? m.direction;
+ const dirColor = isSelected ? 'cyan' : (DIRECTION_COLORS[m.direction] ?? 'white');
+ const qty = `${m.quantityAmount} ${m.quantityUnit}`;
+ const dateStr = m.performedAt ? new Date(m.performedAt).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) : '';
+ return (
+
+ {isSelected ? '▶ ' : ' '}
+ {typeLabel.substring(0, 18).padEnd(19)}
+ {dirLabel.padEnd(10)}
+ {qty.substring(0, 14).padEnd(16)}
+ {(m.batchId ?? '').substring(0, 14).padEnd(16)}
+ {dateStr}
+
+ );
+ })}
+
+ )}
+
+
+
+ ↑↓ nav · Enter Details · [n] Neu · [r] Aktualisieren · [a] Alle [1-8] Typ-Filter · Backspace Zurück
+
+
+
+ );
+}
diff --git a/frontend/apps/cli/src/components/inventory/StockMovementRecordScreen.tsx b/frontend/apps/cli/src/components/inventory/StockMovementRecordScreen.tsx
new file mode 100644
index 0000000..fdac9f6
--- /dev/null
+++ b/frontend/apps/cli/src/components/inventory/StockMovementRecordScreen.tsx
@@ -0,0 +1,325 @@
+import React, { useEffect, useState } from 'react';
+import { Box, Text, useInput } from 'ink';
+import { useNavigation } from '../../state/navigation-context.js';
+import { useStockMovements } from '../../hooks/useStockMovements.js';
+import { useStocks } from '../../hooks/useStocks.js';
+import { FormInput } from '../shared/FormInput.js';
+import { LoadingSpinner } from '../shared/LoadingSpinner.js';
+import { ErrorDisplay } from '../shared/ErrorDisplay.js';
+import { MOVEMENT_TYPE_LABELS, UOM_VALUES, UOM_LABELS } from '@effigenix/api-client';
+import type { MovementType, UoM, StockDTO, StockBatchDTO, RecordStockMovementRequest } from '@effigenix/api-client';
+
+const MOVEMENT_TYPES: MovementType[] = [
+ 'GOODS_RECEIPT', 'PRODUCTION_OUTPUT', 'PRODUCTION_CONSUMPTION',
+ 'SALE', 'RETURN', 'WASTE', 'ADJUSTMENT', 'INTER_BRANCH_TRANSFER',
+];
+
+type Field = 'stock' | 'batch' | 'movementType' | 'direction' | 'quantity' | 'unit' | 'reason' | 'referenceDocumentId';
+
+const FIELD_LABELS: Record = {
+ stock: 'Bestand (↑↓ auswählen)',
+ batch: 'Charge (↑↓ auswählen)',
+ movementType: 'Bewegungstyp (←→ wechseln)',
+ direction: 'Richtung (←→ wechseln)',
+ quantity: 'Menge *',
+ unit: 'Einheit (←→ wechseln)',
+ reason: 'Grund (bei Ausschuss/Korrektur)',
+ referenceDocumentId: 'Referenz-Dok. (bei Umlagerung)',
+};
+
+const DIRECTIONS = ['IN', 'OUT'] as const;
+const DIRECTION_LABELS: Record = { IN: 'Eingang', OUT: 'Ausgang' };
+
+function needsDirection(type: MovementType): boolean {
+ return type === 'ADJUSTMENT';
+}
+
+function needsReason(type: MovementType): boolean {
+ return type === 'WASTE' || type === 'ADJUSTMENT';
+}
+
+function needsReference(type: MovementType): boolean {
+ return type === 'INTER_BRANCH_TRANSFER';
+}
+
+export function StockMovementRecordScreen() {
+ const { back, params } = useNavigation();
+ const { recordMovement, loading, error, clearError } = useStockMovements();
+ const { stocks, fetchStocks } = useStocks();
+
+ const preselectedStockId = params.stockId;
+
+ const [stockIdx, setStockIdx] = useState(0);
+ const [batchIdx, setBatchIdx] = useState(0);
+ const [typeIdx, setTypeIdx] = useState(0);
+ const [dirIdx, setDirIdx] = useState(0);
+ const [quantity, setQuantity] = useState('');
+ const [uomIdx, setUomIdx] = useState(0);
+ const [reason, setReason] = useState('');
+ const [refDocId, setRefDocId] = useState('');
+ const [activeField, setActiveField] = useState(preselectedStockId ? 'batch' : 'stock');
+ const [fieldErrors, setFieldErrors] = useState>>({});
+ const [success, setSuccess] = useState(false);
+
+ useEffect(() => {
+ void fetchStocks();
+ }, [fetchStocks]);
+
+ useEffect(() => {
+ if (preselectedStockId && stocks.length > 0) {
+ const idx = stocks.findIndex((s) => s.id === preselectedStockId);
+ if (idx >= 0) setStockIdx(idx);
+ }
+ }, [preselectedStockId, stocks]);
+
+ const selectedStock: StockDTO | undefined = stocks[stockIdx];
+ const batches: StockBatchDTO[] = selectedStock?.batches ?? [];
+ const selectedBatch: StockBatchDTO | undefined = batches[batchIdx];
+ const selectedType = MOVEMENT_TYPES[typeIdx]!;
+
+ const getActiveFields = (): Field[] => {
+ const fields: Field[] = ['stock', 'batch', 'movementType'];
+ if (needsDirection(selectedType)) fields.push('direction');
+ fields.push('quantity', 'unit');
+ if (needsReason(selectedType)) fields.push('reason');
+ if (needsReference(selectedType)) fields.push('referenceDocumentId');
+ return fields;
+ };
+
+ const handleSubmit = async () => {
+ const errors: Partial> = {};
+ if (!selectedStock) errors.stock = 'Bestand auswählen.';
+ if (!selectedBatch) errors.batch = 'Charge auswählen.';
+ if (!quantity.trim()) errors.quantity = 'Menge ist erforderlich.';
+ if (needsDirection(selectedType) && !DIRECTIONS[dirIdx]) errors.direction = 'Richtung auswählen.';
+ if (needsReason(selectedType) && !reason.trim()) errors.reason = 'Grund ist erforderlich.';
+ if (needsReference(selectedType) && !refDocId.trim()) errors.referenceDocumentId = 'Referenz ist erforderlich.';
+ setFieldErrors(errors);
+ if (Object.keys(errors).length > 0) return;
+
+ const request: Record = {
+ stockId: selectedStock!.id,
+ articleId: selectedStock!.articleId,
+ stockBatchId: selectedBatch!.id!,
+ batchId: selectedBatch!.batchId!,
+ batchType: selectedBatch!.batchType!,
+ movementType: selectedType,
+ quantityAmount: quantity.trim(),
+ quantityUnit: UOM_VALUES[uomIdx] as string,
+ };
+ if (needsDirection(selectedType)) request.direction = DIRECTIONS[dirIdx];
+ if (needsReason(selectedType) && reason.trim()) request.reason = reason.trim();
+ if (needsReference(selectedType) && refDocId.trim()) request.referenceDocumentId = refDocId.trim();
+
+ const result = await recordMovement(request as unknown as RecordStockMovementRequest);
+ if (result) setSuccess(true);
+ };
+
+ const activeFields = getActiveFields();
+
+ const goNextField = () => {
+ const idx = activeFields.indexOf(activeField);
+ if (idx < activeFields.length - 1) {
+ setActiveField(activeFields[idx + 1]!);
+ } else {
+ void handleSubmit();
+ }
+ };
+
+ const goPrevField = () => {
+ const idx = activeFields.indexOf(activeField);
+ if (idx > 0) setActiveField(activeFields[idx - 1]!);
+ };
+
+ useInput((_input, key) => {
+ if (loading) return;
+
+ if (success) {
+ if (key.return || key.escape) back();
+ return;
+ }
+
+ if (activeField === 'stock') {
+ if (key.upArrow) setStockIdx((i) => Math.max(0, i - 1));
+ if (key.downArrow) setStockIdx((i) => Math.min(stocks.length - 1, i + 1));
+ if (key.return || key.tab) { setBatchIdx(0); goNextField(); }
+ if (key.escape) back();
+ return;
+ }
+
+ if (activeField === 'batch') {
+ if (key.upArrow) setBatchIdx((i) => Math.max(0, i - 1));
+ if (key.downArrow) setBatchIdx((i) => Math.min(batches.length - 1, i + 1));
+ if (key.return || key.tab) goNextField();
+ if (key.escape) goPrevField();
+ return;
+ }
+
+ if (activeField === 'movementType') {
+ if (key.leftArrow || key.rightArrow) {
+ const dir = key.rightArrow ? 1 : -1;
+ setTypeIdx((i) => (i + dir + MOVEMENT_TYPES.length) % MOVEMENT_TYPES.length);
+ return;
+ }
+ if (key.return || key.tab) goNextField();
+ if (key.escape) goPrevField();
+ return;
+ }
+
+ if (activeField === 'direction') {
+ if (key.leftArrow || key.rightArrow) {
+ setDirIdx((i) => (i + 1) % DIRECTIONS.length);
+ return;
+ }
+ if (key.return || key.tab) goNextField();
+ if (key.escape) goPrevField();
+ return;
+ }
+
+ if (activeField === 'unit') {
+ if (key.leftArrow || key.rightArrow) {
+ const dir = key.rightArrow ? 1 : -1;
+ setUomIdx((i) => (i + dir + UOM_VALUES.length) % UOM_VALUES.length);
+ return;
+ }
+ if (key.return || key.tab) goNextField();
+ if (key.escape) goPrevField();
+ return;
+ }
+
+ // Text-Felder
+ if (key.tab || key.downArrow) goNextField();
+ if (key.upArrow) goPrevField();
+ if (key.escape) goPrevField();
+ });
+
+ if (loading) return ;
+
+ if (success) {
+ return (
+
+ Bestandsbewegung erfolgreich erfasst!
+ Enter/Escape zum Zurückkehren
+
+ );
+ }
+
+ const typeLabel = MOVEMENT_TYPE_LABELS[selectedType];
+ const uomLabel = UOM_LABELS[UOM_VALUES[uomIdx] as UoM] ?? UOM_VALUES[uomIdx];
+ const dirLabel = DIRECTION_LABELS[DIRECTIONS[dirIdx]] ?? DIRECTIONS[dirIdx];
+
+ return (
+
+ Bestandsbewegung erfassen
+ {error && }
+
+
+ {/* Stock selector */}
+
+ {FIELD_LABELS.stock}
+ {fieldErrors.stock && {fieldErrors.stock}}
+
+ {stocks.length === 0 ? (
+ Keine Bestände gefunden.
+ ) : (
+ stocks.slice(Math.max(0, stockIdx - 3), stockIdx + 4).map((s, i) => {
+ const actualIdx = Math.max(0, stockIdx - 3) + i;
+ const isSelected = actualIdx === stockIdx;
+ return (
+
+ {isSelected ? '▶ ' : ' '}{s.articleId.substring(0, 12)} @ {s.storageLocationId.substring(0, 12)}
+
+ );
+ })
+ )}
+
+
+
+ {/* Batch selector */}
+
+ {FIELD_LABELS.batch}
+ {fieldErrors.batch && {fieldErrors.batch}}
+
+ {batches.length === 0 ? (
+ Keine Chargen in diesem Bestand.
+ ) : (
+ batches.slice(Math.max(0, batchIdx - 3), batchIdx + 4).map((b, i) => {
+ const actualIdx = Math.max(0, batchIdx - 3) + i;
+ const isSelected = actualIdx === batchIdx;
+ return (
+
+ {isSelected ? '▶ ' : ' '}{b.batchId} ({b.batchType}) – {b.quantityAmount} {b.quantityUnit}
+
+ );
+ })
+ )}
+
+
+
+ {/* Movement Type */}
+
+
+ {FIELD_LABELS.movementType}: {activeField === 'movementType' ? `< ${typeLabel} >` : typeLabel}
+
+
+
+ {/* Direction (only for ADJUSTMENT) */}
+ {needsDirection(selectedType) && (
+
+
+ {FIELD_LABELS.direction}: {activeField === 'direction' ? `< ${dirLabel} >` : dirLabel}
+
+ {fieldErrors.direction && {fieldErrors.direction}}
+
+ )}
+
+ {/* Quantity */}
+ goNextField()}
+ focus={activeField === 'quantity'}
+ {...(fieldErrors.quantity ? { error: fieldErrors.quantity } : {})}
+ />
+
+ {/* Unit */}
+
+
+ {FIELD_LABELS.unit}: {activeField === 'unit' ? `< ${uomLabel} >` : uomLabel}
+
+
+
+ {/* Reason (for WASTE/ADJUSTMENT) */}
+ {needsReason(selectedType) && (
+ goNextField()}
+ focus={activeField === 'reason'}
+ {...(fieldErrors.reason ? { error: fieldErrors.reason } : {})}
+ />
+ )}
+
+ {/* Reference Document ID (for INTER_BRANCH_TRANSFER) */}
+ {needsReference(selectedType) && (
+ void handleSubmit()}
+ focus={activeField === 'referenceDocumentId'}
+ {...(fieldErrors.referenceDocumentId ? { error: fieldErrors.referenceDocumentId } : {})}
+ />
+ )}
+
+
+
+
+ Tab/↑↓ Feld wechseln · ←→ Typ/Einheit/Richtung · Enter bestätigen/speichern · Escape Zurück
+
+
+
+ );
+}
diff --git a/frontend/apps/cli/src/components/production/ProductionMenu.tsx b/frontend/apps/cli/src/components/production/ProductionMenu.tsx
index 4d76a48..4b96656 100644
--- a/frontend/apps/cli/src/components/production/ProductionMenu.tsx
+++ b/frontend/apps/cli/src/components/production/ProductionMenu.tsx
@@ -12,7 +12,7 @@ interface MenuItem {
const MENU_ITEMS: MenuItem[] = [
{ label: 'Rezepte', screen: 'recipe-list', description: 'Rezepte anlegen und verwalten' },
{ label: 'Chargen', screen: 'batch-list', description: 'Produktionschargen planen, starten und abschließen' },
- { label: 'Produktionsaufträge', screen: 'production-order-create', description: 'Produktionsauftrag anlegen' },
+ { label: 'Produktionsaufträge', screen: 'production-order-list', description: 'Produktionsaufträge verwalten, freigeben und starten' },
];
export function ProductionMenu() {
diff --git a/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx b/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx
new file mode 100644
index 0000000..a0434c0
--- /dev/null
+++ b/frontend/apps/cli/src/components/production/ProductionOrderDetailScreen.tsx
@@ -0,0 +1,185 @@
+import React, { useEffect, useState } 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 { LoadingSpinner } from '../shared/LoadingSpinner.js';
+import { ErrorDisplay } from '../shared/ErrorDisplay.js';
+import { SuccessDisplay } from '../shared/SuccessDisplay.js';
+import { PRODUCTION_ORDER_STATUS_LABELS, PRIORITY_LABELS } from '@effigenix/api-client';
+import type { ProductionOrderStatus, Priority } from '@effigenix/api-client';
+
+const STATUS_COLORS: Record = {
+ CREATED: 'gray',
+ RELEASED: 'yellow',
+ IN_PRODUCTION: 'blue',
+ COMPLETED: 'green',
+ CANCELLED: 'red',
+};
+
+type Mode = 'view' | 'menu' | 'start-batch-input';
+
+export function ProductionOrderDetailScreen() {
+ const { params, back } = useNavigation();
+ const {
+ productionOrder, loading, error,
+ fetchProductionOrder, releaseProductionOrder, startProductionOrder, clearError,
+ } = useProductionOrders();
+ const [mode, setMode] = useState('view');
+ const [menuIndex, setMenuIndex] = useState(0);
+ const [batchId, setBatchId] = useState('');
+ const [success, setSuccess] = useState(null);
+
+ const orderId = params.orderId ?? '';
+
+ useEffect(() => {
+ if (orderId) void fetchProductionOrder(orderId);
+ }, [fetchProductionOrder, orderId]);
+
+ const getMenuItems = () => {
+ const items: { label: string; action: string }[] = [];
+ const status = productionOrder?.status;
+ if (status === 'CREATED') {
+ items.push({ label: 'Freigeben', action: 'release' });
+ }
+ if (status === 'RELEASED') {
+ items.push({ label: 'Produktion starten', action: 'start' });
+ }
+ return items;
+ };
+
+ const menuItems = getMenuItems();
+
+ const handleRelease = async () => {
+ const result = await releaseProductionOrder(orderId);
+ if (result) {
+ setSuccess('Produktionsauftrag freigegeben.');
+ setMode('view');
+ }
+ };
+
+ const handleStart = async () => {
+ if (!batchId.trim()) return;
+ const result = await startProductionOrder(orderId, { batchId: batchId.trim() });
+ if (result) {
+ setSuccess('Produktion gestartet.');
+ setMode('view');
+ setBatchId('');
+ }
+ };
+
+ useInput((_input, key) => {
+ if (loading) return;
+
+ if (mode === 'start-batch-input') {
+ if (key.escape) setMode('menu');
+ return;
+ }
+
+ if (mode === 'menu') {
+ if (key.upArrow) setMenuIndex((i) => Math.max(0, i - 1));
+ if (key.downArrow) setMenuIndex((i) => Math.min(menuItems.length - 1, i + 1));
+ if (key.return && menuItems[menuIndex]) {
+ const action = menuItems[menuIndex].action;
+ if (action === 'release') void handleRelease();
+ if (action === 'start') {
+ setMode('start-batch-input');
+ setBatchId('');
+ }
+ }
+ if (key.escape) setMode('view');
+ return;
+ }
+
+ // view mode
+ if (_input === 'm' && menuItems.length > 0) {
+ setMode('menu');
+ setMenuIndex(0);
+ }
+ if (key.backspace || key.escape) back();
+ });
+
+ if (loading && !productionOrder) return ;
+
+ if (!productionOrder) {
+ return (
+
+ {error && }
+ Produktionsauftrag nicht gefunden.
+
+ );
+ }
+
+ const statusLabel = PRODUCTION_ORDER_STATUS_LABELS[(productionOrder.status ?? '') as ProductionOrderStatus] ?? productionOrder.status;
+ const statusColor = STATUS_COLORS[productionOrder.status ?? ''] ?? 'white';
+ const prioLabel = PRIORITY_LABELS[(productionOrder.priority ?? '') as Priority] ?? productionOrder.priority;
+ const createdAt = productionOrder.createdAt
+ ? new Date(productionOrder.createdAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' })
+ : '';
+ const updatedAt = productionOrder.updatedAt
+ ? new Date(productionOrder.updatedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' })
+ : '';
+
+ return (
+
+
+ Produktionsauftrag
+ {loading && (aktualisiere...)}
+
+
+ {error && }
+ {success && setSuccess(null)} />}
+
+
+ ID: {productionOrder.id}
+ Rezept-ID: {productionOrder.recipeId}
+ Status: {statusLabel}
+ Menge: {productionOrder.plannedQuantity} {productionOrder.plannedQuantityUnit}
+ Geplant am: {productionOrder.plannedDate}
+ Priorität: {prioLabel}
+ {productionOrder.batchId && (
+ Chargen-ID: {productionOrder.batchId}
+ )}
+ {productionOrder.notes && (
+ Notizen: {productionOrder.notes}
+ )}
+ Erstellt: {createdAt}
+ Aktualisiert: {updatedAt}
+
+
+ {mode === 'menu' && (
+
+ Aktionen
+ {menuItems.map((item, i) => (
+
+ {i === menuIndex ? '▶ ' : ' '}{item.label}
+
+ ))}
+ ↑↓ nav · Enter ausführen · Escape zurück
+
+ )}
+
+ {mode === 'start-batch-input' && (
+
+ Chargen-ID eingeben:
+
+ ›
+ void handleStart()}
+ focus={true}
+ />
+
+ Enter bestätigen · Escape abbrechen
+
+ )}
+
+
+
+ {menuItems.length > 0 ? '[m] Aktionsmenü · ' : ''}Backspace Zurück
+
+
+
+ );
+}
diff --git a/frontend/apps/cli/src/components/production/ProductionOrderListScreen.tsx b/frontend/apps/cli/src/components/production/ProductionOrderListScreen.tsx
new file mode 100644
index 0000000..cb2f0f3
--- /dev/null
+++ b/frontend/apps/cli/src/components/production/ProductionOrderListScreen.tsx
@@ -0,0 +1,94 @@
+import React, { useEffect, useState } from 'react';
+import { Box, Text, useInput } from 'ink';
+import { useNavigation } from '../../state/navigation-context.js';
+import { useProductionOrders } from '../../hooks/useProductionOrders.js';
+import { LoadingSpinner } from '../shared/LoadingSpinner.js';
+import { ErrorDisplay } from '../shared/ErrorDisplay.js';
+import { PRODUCTION_ORDER_STATUS_LABELS, PRIORITY_LABELS } from '@effigenix/api-client';
+import type { ProductionOrderStatus, Priority } from '@effigenix/api-client';
+
+const STATUS_COLORS: Record = {
+ CREATED: 'gray',
+ RELEASED: 'yellow',
+ IN_PRODUCTION: 'blue',
+ COMPLETED: 'green',
+ CANCELLED: 'red',
+};
+
+export function ProductionOrderListScreen() {
+ const { navigate, back } = useNavigation();
+ const { productionOrders, loading, error, fetchProductionOrders, clearError } = useProductionOrders();
+ const [selectedIndex, setSelectedIndex] = useState(0);
+
+ useEffect(() => {
+ void fetchProductionOrders();
+ }, [fetchProductionOrders]);
+
+ useInput((input, key) => {
+ if (loading) return;
+
+ if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
+ if (key.downArrow) setSelectedIndex((i) => Math.min(productionOrders.length - 1, i + 1));
+
+ if (key.return && productionOrders.length > 0) {
+ const order = productionOrders[selectedIndex];
+ if (order?.id) navigate('production-order-detail', { orderId: order.id });
+ }
+ if (input === 'n') navigate('production-order-create');
+ if (input === 'r') void fetchProductionOrders();
+ if (key.backspace || key.escape) back();
+ });
+
+ return (
+
+
+ Produktionsaufträge
+ ({productionOrders.length})
+
+
+ {loading && }
+ {error && !loading && }
+
+ {!loading && !error && (
+
+
+ {' Rezept-ID'.padEnd(16)}
+ {'Menge'.padEnd(16)}
+ {'Datum'.padEnd(14)}
+ {'Priorität'.padEnd(12)}
+ Status
+
+ {productionOrders.length === 0 && (
+
+ Keine Produktionsaufträge gefunden.
+
+ )}
+ {productionOrders.map((order, index) => {
+ const isSelected = index === selectedIndex;
+ const textColor = isSelected ? 'cyan' : 'white';
+ const statusColor = STATUS_COLORS[order.status ?? ''] ?? 'white';
+ const statusLabel = PRODUCTION_ORDER_STATUS_LABELS[(order.status ?? '') as ProductionOrderStatus] ?? order.status;
+ const prioLabel = PRIORITY_LABELS[(order.priority ?? '') as Priority] ?? order.priority;
+ const qty = `${order.plannedQuantity ?? ''} ${order.plannedQuantityUnit ?? ''}`;
+ return (
+
+ {isSelected ? '▶ ' : ' '}
+ {(order.recipeId ?? '').substring(0, 12).padEnd(13)}
+ {qty.substring(0, 14).padEnd(16)}
+ {(order.plannedDate ?? '').padEnd(14)}
+ {(prioLabel ?? '').padEnd(12)}
+ {statusLabel}
+
+ );
+ })}
+
+ )}
+
+
+
+ ↑↓ nav · Enter Details · [n] Neu · [r] Aktualisieren · Backspace Zurück
+
+
+
+ );
+}
diff --git a/frontend/apps/cli/src/hooks/useProductionOrders.ts b/frontend/apps/cli/src/hooks/useProductionOrders.ts
index a47c18d..dedc834 100644
--- a/frontend/apps/cli/src/hooks/useProductionOrders.ts
+++ b/frontend/apps/cli/src/hooks/useProductionOrders.ts
@@ -1,8 +1,9 @@
import { useState, useCallback } from 'react';
-import type { ProductionOrderDTO, CreateProductionOrderRequest } from '@effigenix/api-client';
+import type { ProductionOrderDTO, CreateProductionOrderRequest, StartProductionOrderRequest } from '@effigenix/api-client';
import { client } from '../utils/api-client.js';
interface ProductionOrdersState {
+ productionOrders: ProductionOrderDTO[];
productionOrder: ProductionOrderDTO | null;
loading: boolean;
error: string | null;
@@ -14,16 +15,37 @@ function errorMessage(err: unknown): string {
export function useProductionOrders() {
const [state, setState] = useState({
+ productionOrders: [],
productionOrder: null,
loading: false,
error: null,
});
+ const fetchProductionOrders = useCallback(async () => {
+ setState((s) => ({ ...s, loading: true, error: null }));
+ try {
+ const productionOrders = await client.productionOrders.list();
+ setState((s) => ({ ...s, productionOrders, loading: false, error: null }));
+ } catch (err) {
+ setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
+ }
+ }, []);
+
+ const fetchProductionOrder = useCallback(async (id: string) => {
+ setState((s) => ({ ...s, loading: true, error: null }));
+ try {
+ const productionOrder = await client.productionOrders.getById(id);
+ setState((s) => ({ ...s, productionOrder, loading: false, error: null }));
+ } catch (err) {
+ setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
+ }
+ }, []);
+
const createProductionOrder = useCallback(async (request: CreateProductionOrderRequest) => {
setState((s) => ({ ...s, loading: true, error: null }));
try {
const productionOrder = await client.productionOrders.create(request);
- setState({ productionOrder, loading: false, error: null });
+ setState((s) => ({ ...s, productionOrder, loading: false, error: null }));
return productionOrder;
} catch (err) {
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
@@ -35,7 +57,19 @@ export function useProductionOrders() {
setState((s) => ({ ...s, loading: true, error: null }));
try {
const productionOrder = await client.productionOrders.release(id);
- setState({ productionOrder, loading: false, error: null });
+ setState((s) => ({ ...s, productionOrder, loading: false, error: null }));
+ return productionOrder;
+ } catch (err) {
+ setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
+ return null;
+ }
+ }, []);
+
+ const startProductionOrder = useCallback(async (id: string, request: StartProductionOrderRequest) => {
+ setState((s) => ({ ...s, loading: true, error: null }));
+ try {
+ const productionOrder = await client.productionOrders.start(id, request);
+ setState((s) => ({ ...s, productionOrder, loading: false, error: null }));
return productionOrder;
} catch (err) {
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
@@ -49,8 +83,11 @@ export function useProductionOrders() {
return {
...state,
+ fetchProductionOrders,
+ fetchProductionOrder,
createProductionOrder,
releaseProductionOrder,
+ startProductionOrder,
clearError,
};
}
diff --git a/frontend/apps/cli/src/hooks/useStockMovements.ts b/frontend/apps/cli/src/hooks/useStockMovements.ts
new file mode 100644
index 0000000..ef24b0e
--- /dev/null
+++ b/frontend/apps/cli/src/hooks/useStockMovements.ts
@@ -0,0 +1,67 @@
+import { useState, useCallback } from 'react';
+import type { StockMovementDTO, RecordStockMovementRequest, StockMovementFilter } from '@effigenix/api-client';
+import { client } from '../utils/api-client.js';
+
+interface StockMovementsState {
+ movements: StockMovementDTO[];
+ movement: StockMovementDTO | null;
+ loading: boolean;
+ error: string | null;
+}
+
+function errorMessage(err: unknown): string {
+ return err instanceof Error ? err.message : 'Unbekannter Fehler';
+}
+
+export function useStockMovements() {
+ const [state, setState] = useState({
+ movements: [],
+ movement: null,
+ loading: false,
+ error: null,
+ });
+
+ const fetchMovements = useCallback(async (filter?: StockMovementFilter) => {
+ setState((s) => ({ ...s, loading: true, error: null }));
+ try {
+ const movements = await client.stockMovements.list(filter);
+ setState((s) => ({ ...s, movements, loading: false, error: null }));
+ } catch (err) {
+ setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
+ }
+ }, []);
+
+ const fetchMovement = useCallback(async (id: string) => {
+ setState((s) => ({ ...s, loading: true, error: null }));
+ try {
+ const movement = await client.stockMovements.getById(id);
+ setState((s) => ({ ...s, movement, loading: false, error: null }));
+ } catch (err) {
+ setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
+ }
+ }, []);
+
+ const recordMovement = useCallback(async (request: RecordStockMovementRequest) => {
+ setState((s) => ({ ...s, loading: true, error: null }));
+ try {
+ const movement = await client.stockMovements.record(request);
+ setState((s) => ({ ...s, movement, loading: false, error: null }));
+ return movement;
+ } catch (err) {
+ setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
+ return null;
+ }
+ }, []);
+
+ const clearError = useCallback(() => {
+ setState((s) => ({ ...s, error: null }));
+ }, []);
+
+ return {
+ ...state,
+ fetchMovements,
+ fetchMovement,
+ recordMovement,
+ clearError,
+ };
+}
diff --git a/frontend/apps/cli/src/state/navigation-context.tsx b/frontend/apps/cli/src/state/navigation-context.tsx
index 974863b..ba8783e 100644
--- a/frontend/apps/cli/src/state/navigation-context.tsx
+++ b/frontend/apps/cli/src/state/navigation-context.tsx
@@ -39,6 +39,9 @@ export type Screen =
| 'stock-list'
| 'stock-detail'
| 'stock-create'
+ | 'stock-movement-list'
+ | 'stock-movement-detail'
+ | 'stock-movement-record'
// Produktion
| 'production-menu'
| 'recipe-list'
@@ -51,7 +54,9 @@ export type Screen =
| 'batch-plan'
| 'batch-record-consumption'
| 'batch-complete'
+ | 'production-order-list'
| 'production-order-create'
+ | 'production-order-detail'
| 'stock-reserve';
interface NavigationState {
diff --git a/frontend/openapi.json b/frontend/openapi.json
index 3d243e6..87d050e 100644
--- a/frontend/openapi.json
+++ b/frontend/openapi.json
@@ -1 +1 @@
-{"openapi":"3.0.1","info":{"title":"Effigenix Fleischerei ERP API","description":"RESTful API for Effigenix Fleischerei ERP System.\n\n## Authentication\n\nAll endpoints (except /api/auth/login and /api/auth/refresh) require JWT authentication.\n\n1. Login via POST /api/auth/login with username and password\n2. Copy the returned access token\n3. Click \"Authorize\" button (top right)\n4. Enter: Bearer \n5. Click \"Authorize\"\n\n## User Management\n\n- **Authentication**: Login, logout, refresh token\n- **User Management**: Create, update, list users (ADMIN only)\n- **Role Management**: Assign roles, lock/unlock users (ADMIN only)\n- **Password Management**: Change password (requires current password)\n\n## Error Handling\n\nAll errors return a consistent error response format:\n\n```json\n{\n \"code\": \"USER_NOT_FOUND\",\n \"message\": \"User with ID 'user-123' not found\",\n \"status\": 404,\n \"timestamp\": \"2026-02-17T12:00:00\",\n \"path\": \"/api/users/user-123\",\n \"validationErrors\": null\n}\n```\n\n## Architecture\n\nBuilt with:\n- Domain-Driven Design (DDD)\n- Clean Architecture (Hexagonal Architecture)\n- Spring Boot 3.2\n- Java 21\n- PostgreSQL\n","contact":{"name":"Effigenix Development Team","url":"https://effigenix.com","email":"dev@effigenix.com"},"license":{"name":"Proprietary","url":"https://effigenix.com/license"},"version":"0.1.0"},"servers":[{"url":"http://localhost:8080","description":"Local Development Server"},{"url":"https://api.effigenix.com","description":"Production Server"}],"tags":[{"name":"Storage Locations","description":"Storage location management endpoints"},{"name":"Product Categories","description":"Product category management endpoints"},{"name":"User Management","description":"User management endpoints (requires authentication)"},{"name":"Articles","description":"Article management endpoints"},{"name":"Recipes","description":"Recipe management endpoints"},{"name":"Batches","description":"Production batch management endpoints"},{"name":"Role Management","description":"Role management endpoints (ADMIN only)"},{"name":"Stocks","description":"Stock management endpoints"},{"name":"Customers","description":"Customer management endpoints"},{"name":"Suppliers","description":"Supplier management endpoints"},{"name":"Authentication","description":"Authentication and session management endpoints"},{"name":"Production Orders","description":"Production order management endpoints"}],"paths":{"/api/users/{id}":{"get":{"tags":["User Management"],"summary":"Get user by ID","description":"Retrieve a single user by their ID.","operationId":"getUserById","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"responses":{"401":{"description":"Authentication required","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"200":{"description":"User retrieved successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["User Management"],"summary":"Update user","description":"Update user details (email, branchId).","operationId":"updateUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}},"required":true},"responses":{"200":{"description":"User updated successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Email already exists","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/password":{"put":{"tags":["User Management"],"summary":"Change password","description":"Change user password. Requires current password for verification.","operationId":"changePassword","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}},"required":true},"responses":{"400":{"description":"Invalid password"},"401":{"description":"Invalid current password"},"404":{"description":"User not found"},"204":{"description":"Password changed successfully"}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}":{"get":{"tags":["Suppliers"],"operationId":"getSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Suppliers"],"operationId":"updateSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations/{id}":{"get":{"tags":["Storage Locations"],"operationId":"getStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Storage Locations"],"operationId":"updateStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStorageLocationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{id}":{"get":{"tags":["Stocks"],"operationId":"getStock","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Stocks"],"operationId":"updateStock","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStockRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}":{"get":{"tags":["Customers"],"operationId":"getCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Customers"],"operationId":"updateCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/preferences":{"put":{"tags":["Customers"],"operationId":"setPreferences","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPreferencesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/frame-contract":{"put":{"tags":["Customers"],"operationId":"setFrameContract","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetFrameContractRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"delete":{"tags":["Customers"],"operationId":"removeFrameContract","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/categories/{id}":{"put":{"tags":["Product Categories"],"operationId":"updateCategory","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductCategoryRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"delete":{"tags":["Product Categories"],"operationId":"deleteCategory","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}":{"get":{"tags":["Articles"],"operationId":"getArticle","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Articles"],"operationId":"updateArticle","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateArticleRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/sales-units/{suId}/price":{"put":{"tags":["Articles"],"operationId":"updateSalesUnitPrice","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"suId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesUnitPriceRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users":{"get":{"tags":["User Management"],"summary":"List all users","description":"Get a list of all users in the system.","operationId":"listUsers","responses":{"200":{"description":"Users retrieved successfully","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDTO"}}}}},"401":{"description":"Authentication required","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDTO"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["User Management"],"summary":"Create user (ADMIN only)","description":"Create a new user account with specified roles.","operationId":"createUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}},"required":true},"responses":{"400":{"description":"Validation error or invalid password","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Username or email already exists","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"201":{"description":"User created successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/unlock":{"post":{"tags":["User Management"],"summary":"Unlock user (ADMIN only)","description":"Unlock a user account (allows login).","operationId":"unlockUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"responses":{"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"200":{"description":"User unlocked successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Invalid status transition","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/roles":{"post":{"tags":["User Management"],"summary":"Assign role (ADMIN only)","description":"Assign a role to a user.","operationId":"assignRole","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRoleRequest"}}},"required":true},"responses":{"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User or role not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"200":{"description":"Role assigned successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/lock":{"post":{"tags":["User Management"],"summary":"Lock user (ADMIN only)","description":"Lock a user account (prevents login).","operationId":"lockUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"responses":{"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"200":{"description":"User locked successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Invalid status transition","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers":{"get":{"tags":["Suppliers"],"operationId":"listSuppliers","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","INACTIVE"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupplierResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Suppliers"],"operationId":"createSupplier","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/rating":{"post":{"tags":["Suppliers"],"operationId":"rateSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/deactivate":{"post":{"tags":["Suppliers"],"operationId":"deactivate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/certificates":{"post":{"tags":["Suppliers"],"operationId":"addCertificate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCertificateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"delete":{"tags":["Suppliers"],"operationId":"removeCertificate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveCertificateRequest"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/activate":{"post":{"tags":["Suppliers"],"operationId":"activate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes":{"get":{"tags":["Recipes"],"operationId":"listRecipes","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecipeSummaryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Recipes"],"operationId":"createRecipe","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRecipeRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/steps":{"post":{"tags":["Recipes"],"operationId":"addProductionStep","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddProductionStepRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/ingredients":{"post":{"tags":["Recipes"],"operationId":"addIngredient","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddRecipeIngredientRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/archive":{"post":{"tags":["Recipes"],"operationId":"archiveRecipe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/activate":{"post":{"tags":["Recipes"],"operationId":"activateRecipe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/production-orders":{"post":{"tags":["Production Orders"],"operationId":"createProductionOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductionOrderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductionOrderResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/production-orders/{id}/release":{"post":{"tags":["Production Orders"],"operationId":"releaseProductionOrder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductionOrderResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches":{"get":{"tags":["Batches"],"operationId":"listBatches","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"productionDate","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"articleId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BatchSummaryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Batches"],"operationId":"planBatch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/start":{"post":{"tags":["Batches"],"operationId":"startBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/consumptions":{"post":{"tags":["Batches"],"operationId":"recordConsumption","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordConsumptionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ConsumptionResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/complete":{"post":{"tags":["Batches"],"operationId":"completeBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/cancel":{"post":{"tags":["Batches"],"operationId":"cancelBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations":{"get":{"tags":["Storage Locations"],"operationId":"listStorageLocations","parameters":[{"name":"storageType","in":"query","required":false,"schema":{"type":"string"}},{"name":"active","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Storage Locations"],"operationId":"createStorageLocation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStorageLocationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks":{"get":{"tags":["Stocks"],"operationId":"listStocks","parameters":[{"name":"storageLocationId","in":"query","required":false,"schema":{"type":"string"}},{"name":"articleId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Stocks"],"operationId":"createStock","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStockRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateStockResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/reservations":{"post":{"tags":["Stocks"],"operationId":"reserveStock","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReserveStockRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReservationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches":{"post":{"tags":["Stocks"],"operationId":"addBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddStockBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockBatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches/{batchId}/unblock":{"post":{"tags":["Stocks"],"operationId":"unblockBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches/{batchId}/remove":{"post":{"tags":["Stocks"],"operationId":"removeBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveStockBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches/{batchId}/block":{"post":{"tags":["Stocks"],"operationId":"blockBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockStockBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/customers":{"get":{"tags":["Customers"],"operationId":"listCustomers","parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["B2C","B2B"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","INACTIVE"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomerResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Customers"],"operationId":"createCustomer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/delivery-addresses":{"post":{"tags":["Customers"],"operationId":"addDeliveryAddress","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddDeliveryAddressRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/deactivate":{"post":{"tags":["Customers"],"operationId":"deactivate_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/activate":{"post":{"tags":["Customers"],"operationId":"activate_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/categories":{"get":{"tags":["Product Categories"],"operationId":"listCategories","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProductCategoryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Product Categories"],"operationId":"createCategory","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductCategoryRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/auth/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh access token","description":"Refresh an expired access token using a valid refresh token.","operationId":"refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}},"required":true},"responses":{"401":{"description":"Invalid or expired refresh token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"200":{"description":"Token refresh successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}}}}},"/api/auth/logout":{"post":{"tags":["Authentication"],"summary":"User logout","description":"Invalidate current JWT token.","operationId":"logout","responses":{"401":{"description":"Invalid or missing authentication token"},"204":{"description":"Logout successful"}}}},"/api/auth/login":{"post":{"tags":["Authentication"],"summary":"User login","description":"Authenticate user with username and password. Returns JWT tokens.","operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"429":{"description":"Too many login attempts","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"401":{"description":"Invalid credentials, user locked, or user inactive","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"200":{"description":"Login successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}}}}},"/api/articles":{"get":{"tags":["Articles"],"operationId":"listArticles","parameters":[{"name":"categoryId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","INACTIVE"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ArticleResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Articles"],"operationId":"createArticle","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateArticleRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/suppliers":{"post":{"tags":["Articles"],"operationId":"assignSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/sales-units":{"post":{"tags":["Articles"],"operationId":"addSalesUnit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSalesUnitRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/deactivate":{"post":{"tags":["Articles"],"operationId":"deactivate_2","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/activate":{"post":{"tags":["Articles"],"operationId":"activate_2","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations/{id}/deactivate":{"patch":{"tags":["Storage Locations"],"operationId":"deactivateStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations/{id}/activate":{"patch":{"tags":["Storage Locations"],"operationId":"activateStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/roles":{"get":{"tags":["Role Management"],"summary":"List all roles (ADMIN only)","description":"Get a list of all available roles in the system. Requires USER_MANAGEMENT permission.","operationId":"listRoles","responses":{"403":{"description":"Missing USER_MANAGEMENT permission","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleDTO"}}}}},"401":{"description":"Authentication required","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleDTO"}}}}},"200":{"description":"Roles retrieved successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoleDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}":{"get":{"tags":["Recipes"],"operationId":"getRecipe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}":{"get":{"tags":["Batches"],"operationId":"getBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/by-number/{batchNumber}":{"get":{"tags":["Batches"],"operationId":"findByNumber","parameters":[{"name":"batchNumber","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/below-minimum":{"get":{"tags":["Stocks"],"operationId":"listStocksBelowMinimum","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockResponse"}}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/roles/{roleName}":{"delete":{"tags":["User Management"],"summary":"Remove role (ADMIN only)","description":"Remove a role from a user.","operationId":"removeRole","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}},{"name":"roleName","in":"path","description":"Role name","required":true,"schema":{"type":"string","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]}}],"responses":{"403":{"description":"Missing permission"},"404":{"description":"User or role not found"},"204":{"description":"Role removed successfully"}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/steps/{stepNumber}":{"delete":{"tags":["Recipes"],"operationId":"removeProductionStep","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"stepNumber","in":"path","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/ingredients/{ingredientId}":{"delete":{"tags":["Recipes"],"operationId":"removeIngredient","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"ingredientId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/reservations/{reservationId}":{"delete":{"tags":["Stocks"],"operationId":"releaseReservation","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"reservationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/delivery-addresses/{label}":{"delete":{"tags":["Customers"],"operationId":"removeDeliveryAddress","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"label","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/suppliers/{supplierId}":{"delete":{"tags":["Articles"],"operationId":"removeSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/sales-units/{suId}":{"delete":{"tags":["Articles"],"operationId":"removeSalesUnit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"suId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}}},"components":{"schemas":{"UpdateUserRequest":{"type":"object","properties":{"email":{"type":"string","description":"New email address","example":"newemail@example.com"},"branchId":{"type":"string","description":"New branch ID","example":"BRANCH-002"}},"description":"Request to update user details"},"RoleDTO":{"required":["description","id","name","permissions"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]},"permissions":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["RECIPE_READ","RECIPE_WRITE","RECIPE_DELETE","BATCH_READ","BATCH_WRITE","BATCH_COMPLETE","BATCH_CANCEL","BATCH_DELETE","PRODUCTION_ORDER_READ","PRODUCTION_ORDER_WRITE","PRODUCTION_ORDER_DELETE","HACCP_READ","HACCP_WRITE","TEMPERATURE_LOG_READ","TEMPERATURE_LOG_WRITE","CLEANING_RECORD_READ","CLEANING_RECORD_WRITE","GOODS_INSPECTION_READ","GOODS_INSPECTION_WRITE","STOCK_READ","STOCK_WRITE","STOCK_MOVEMENT_READ","STOCK_MOVEMENT_WRITE","INVENTORY_COUNT_READ","INVENTORY_COUNT_WRITE","PURCHASE_ORDER_READ","PURCHASE_ORDER_WRITE","PURCHASE_ORDER_DELETE","GOODS_RECEIPT_READ","GOODS_RECEIPT_WRITE","SUPPLIER_READ","SUPPLIER_WRITE","SUPPLIER_DELETE","ORDER_READ","ORDER_WRITE","ORDER_DELETE","INVOICE_READ","INVOICE_WRITE","INVOICE_DELETE","CUSTOMER_READ","CUSTOMER_WRITE","CUSTOMER_DELETE","LABEL_READ","LABEL_WRITE","LABEL_PRINT","MASTERDATA_READ","MASTERDATA_WRITE","BRANCH_READ","BRANCH_WRITE","BRANCH_DELETE","USER_READ","USER_WRITE","USER_DELETE","USER_LOCK","USER_UNLOCK","ROLE_READ","ROLE_WRITE","ROLE_ASSIGN","ROLE_REMOVE","REPORT_READ","REPORT_GENERATE","NOTIFICATION_READ","NOTIFICATION_SEND","AUDIT_LOG_READ","SYSTEM_SETTINGS_READ","SYSTEM_SETTINGS_WRITE"]}},"description":{"type":"string"}}},"UserDTO":{"required":["createdAt","email","id","roles","status","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"email":{"type":"string"},"roles":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/RoleDTO"}},"branchId":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","INACTIVE","LOCKED"]},"createdAt":{"type":"string","format":"date-time"},"lastLogin":{"type":"string","format":"date-time"}}},"ChangePasswordRequest":{"required":["currentPassword","newPassword"],"type":"object","properties":{"currentPassword":{"type":"string","description":"Current password","example":"OldPass123"},"newPassword":{"maxLength":2147483647,"minLength":8,"type":"string","description":"New password (min 8 characters)","example":"NewSecurePass456"}},"description":"Request to change user password"},"UpdateSupplierRequest":{"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"AddressResponse":{"required":["city","country","houseNumber","postalCode","street"],"type":"object","properties":{"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}},"nullable":true},"ContactInfoResponse":{"required":["contactPerson","email","phone"],"type":"object","properties":{"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"}}},"PaymentTermsResponse":{"required":["paymentDescription","paymentDueDays"],"type":"object","properties":{"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}},"nullable":true},"QualityCertificateResponse":{"required":["certificateType","issuer","validFrom","validUntil"],"type":"object","properties":{"certificateType":{"type":"string"},"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"}}},"SupplierRatingResponse":{"required":["deliveryScore","priceScore","qualityScore"],"type":"object","properties":{"qualityScore":{"type":"integer","format":"int32"},"deliveryScore":{"type":"integer","format":"int32"},"priceScore":{"type":"integer","format":"int32"}},"nullable":true},"SupplierResponse":{"required":["certificates","contactInfo","createdAt","id","name","status","updatedAt"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"address":{"$ref":"#/components/schemas/AddressResponse"},"contactInfo":{"$ref":"#/components/schemas/ContactInfoResponse"},"paymentTerms":{"$ref":"#/components/schemas/PaymentTermsResponse"},"certificates":{"type":"array","items":{"$ref":"#/components/schemas/QualityCertificateResponse"}},"rating":{"$ref":"#/components/schemas/SupplierRatingResponse"},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"UpdateStorageLocationRequest":{"type":"object","properties":{"name":{"type":"string"},"minTemperature":{"type":"string"},"maxTemperature":{"type":"string"}}},"StorageLocationResponse":{"required":["active","id","name","storageType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"storageType":{"type":"string"},"temperatureRange":{"$ref":"#/components/schemas/TemperatureRangeResponse"},"active":{"type":"boolean"}}},"TemperatureRangeResponse":{"required":["maxTemperature","minTemperature"],"type":"object","properties":{"minTemperature":{"type":"number"},"maxTemperature":{"type":"number"}},"nullable":true},"UpdateStockRequest":{"type":"object","properties":{"minimumLevelAmount":{"type":"string"},"minimumLevelUnit":{"type":"string"},"minimumShelfLifeDays":{"type":"integer","format":"int32"}}},"MinimumLevelResponse":{"required":["amount","unit"],"type":"object","properties":{"amount":{"type":"number"},"unit":{"type":"string"}},"nullable":true},"ReservationResponse":{"type":"object","properties":{"id":{"type":"string"},"referenceType":{"type":"string"},"referenceId":{"type":"string"},"quantityAmount":{"type":"number"},"quantityUnit":{"type":"string"},"priority":{"type":"string"},"reservedAt":{"type":"string","format":"date-time"},"allocations":{"type":"array","items":{"$ref":"#/components/schemas/StockBatchAllocationResponse"}}}},"StockBatchAllocationResponse":{"type":"object","properties":{"stockBatchId":{"type":"string"},"allocatedQuantityAmount":{"type":"number"},"allocatedQuantityUnit":{"type":"string"}}},"StockBatchResponse":{"type":"object","properties":{"id":{"type":"string"},"batchId":{"type":"string"},"batchType":{"type":"string"},"quantityAmount":{"type":"number"},"quantityUnit":{"type":"string"},"expiryDate":{"type":"string","format":"date"},"status":{"type":"string"},"receivedAt":{"type":"string","format":"date-time"}}},"StockResponse":{"required":["articleId","availableQuantity","batches","id","reservations","storageLocationId","totalQuantity"],"type":"object","properties":{"id":{"type":"string"},"articleId":{"type":"string"},"storageLocationId":{"type":"string"},"minimumLevel":{"$ref":"#/components/schemas/MinimumLevelResponse"},"minimumShelfLifeDays":{"type":"integer","format":"int32","nullable":true},"batches":{"type":"array","items":{"$ref":"#/components/schemas/StockBatchResponse"}},"totalQuantity":{"type":"number"},"quantityUnit":{"type":"string","nullable":true},"availableQuantity":{"type":"number"},"reservations":{"type":"array","items":{"$ref":"#/components/schemas/ReservationResponse"}}}},"UpdateCustomerRequest":{"type":"object","properties":{"name":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"ContractLineItemResponse":{"required":["agreedPrice","agreedQuantity","articleId","unit"],"type":"object","properties":{"articleId":{"type":"string"},"agreedPrice":{"type":"number"},"agreedQuantity":{"type":"number"},"unit":{"type":"string"}}},"CustomerResponse":{"required":["billingAddress","contactInfo","createdAt","deliveryAddresses","id","name","preferences","status","type","updatedAt"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"billingAddress":{"$ref":"#/components/schemas/AddressResponse"},"contactInfo":{"$ref":"#/components/schemas/ContactInfoResponse"},"paymentTerms":{"$ref":"#/components/schemas/PaymentTermsResponse"},"deliveryAddresses":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryAddressResponse"}},"frameContract":{"$ref":"#/components/schemas/FrameContractResponse"},"preferences":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"DeliveryAddressResponse":{"required":["address","contactPerson","deliveryNotes","label"],"type":"object","properties":{"label":{"type":"string"},"address":{"$ref":"#/components/schemas/AddressResponse"},"contactPerson":{"type":"string"},"deliveryNotes":{"type":"string"}}},"FrameContractResponse":{"required":["deliveryRhythm","id","lineItems","validFrom","validUntil"],"type":"object","properties":{"id":{"type":"string"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"},"deliveryRhythm":{"type":"string"},"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/ContractLineItemResponse"}}},"nullable":true},"SetPreferencesRequest":{"required":["preferences"],"type":"object","properties":{"preferences":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["BIO","REGIONAL","TIERWOHL","HALAL","KOSHER","GLUTENFREI","LAKTOSEFREI"]}}}},"LineItem":{"required":["agreedPrice","articleId"],"type":"object","properties":{"articleId":{"type":"string"},"agreedPrice":{"type":"number"},"agreedQuantity":{"type":"number"},"unit":{"type":"string","enum":["PIECE_FIXED","KG","HUNDRED_GRAM","PIECE_VARIABLE"]}}},"SetFrameContractRequest":{"required":["lineItems","rhythm"],"type":"object","properties":{"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"},"rhythm":{"type":"string","enum":["DAILY","WEEKLY","BIWEEKLY","MONTHLY","ON_DEMAND"]},"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/LineItem"}}}},"UpdateProductCategoryRequest":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}},"ProductCategoryResponse":{"required":["description","id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}},"UpdateArticleRequest":{"type":"object","properties":{"name":{"type":"string"},"categoryId":{"type":"string"}}},"ArticleResponse":{"required":["articleNumber","categoryId","createdAt","id","name","salesUnits","status","supplierIds","updatedAt"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"articleNumber":{"type":"string"},"categoryId":{"type":"string"},"salesUnits":{"type":"array","items":{"$ref":"#/components/schemas/SalesUnitResponse"}},"status":{"type":"string"},"supplierIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SalesUnitResponse":{"required":["id","price","priceModel","unit"],"type":"object","properties":{"id":{"type":"string"},"unit":{"type":"string"},"priceModel":{"type":"string"},"price":{"type":"number"}}},"UpdateSalesUnitPriceRequest":{"required":["price"],"type":"object","properties":{"price":{"type":"number"}}},"CreateUserRequest":{"required":["email","password","roleNames","username"],"type":"object","properties":{"username":{"maxLength":50,"minLength":3,"type":"string","description":"Username (unique)","example":"john.doe"},"email":{"type":"string","description":"Email address (unique)","example":"john.doe@example.com"},"password":{"maxLength":2147483647,"minLength":8,"type":"string","description":"Password (min 8 characters)","example":"SecurePass123"},"roleNames":{"uniqueItems":true,"type":"array","description":"Role names to assign","example":["USER","MANAGER"],"items":{"type":"string","description":"Role names to assign","example":"[\"USER\",\"MANAGER\"]","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]}},"branchId":{"type":"string","description":"Branch ID (optional)","example":"BRANCH-001"}},"description":"Request to create a new user"},"AssignRoleRequest":{"required":["roleName"],"type":"object","properties":{"roleName":{"type":"string","description":"Role name to assign","example":"MANAGER","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]}},"description":"Request to assign a role to a user"},"CreateSupplierRequest":{"required":["name","phone"],"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"RateSupplierRequest":{"type":"object","properties":{"qualityScore":{"maximum":5,"minimum":1,"type":"integer","format":"int32"},"deliveryScore":{"maximum":5,"minimum":1,"type":"integer","format":"int32"},"priceScore":{"maximum":5,"minimum":1,"type":"integer","format":"int32"}}},"AddCertificateRequest":{"required":["certificateType"],"type":"object","properties":{"certificateType":{"type":"string"},"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"}}},"CreateRecipeRequest":{"required":["articleId","name","outputQuantity","outputUom","type"],"type":"object","properties":{"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"type":{"type":"string","enum":["RAW_MATERIAL","INTERMEDIATE","FINISHED_PRODUCT"]},"description":{"type":"string"},"yieldPercentage":{"type":"integer","format":"int32"},"shelfLifeDays":{"type":"integer","format":"int32"},"outputQuantity":{"type":"string"},"outputUom":{"type":"string"},"articleId":{"type":"string"}}},"IngredientResponse":{"required":["articleId","id","position","quantity","substitutable","uom"],"type":"object","properties":{"id":{"type":"string"},"position":{"type":"integer","format":"int32"},"articleId":{"type":"string"},"quantity":{"type":"string"},"uom":{"type":"string"},"subRecipeId":{"type":"string","nullable":true},"substitutable":{"type":"boolean"}}},"ProductionStepResponse":{"required":["description","id","stepNumber"],"type":"object","properties":{"id":{"type":"string"},"stepNumber":{"type":"integer","format":"int32"},"description":{"type":"string"},"durationMinutes":{"type":"integer","format":"int32","nullable":true},"temperatureCelsius":{"type":"integer","format":"int32","nullable":true}}},"RecipeResponse":{"required":["articleId","createdAt","description","id","ingredients","name","outputQuantity","outputUom","productionSteps","status","type","updatedAt","version","yieldPercentage"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"type":{"type":"string"},"description":{"type":"string"},"yieldPercentage":{"type":"integer","format":"int32"},"shelfLifeDays":{"type":"integer","format":"int32","nullable":true},"outputQuantity":{"type":"string"},"outputUom":{"type":"string"},"articleId":{"type":"string"},"status":{"type":"string"},"ingredients":{"type":"array","items":{"$ref":"#/components/schemas/IngredientResponse"}},"productionSteps":{"type":"array","items":{"$ref":"#/components/schemas/ProductionStepResponse"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"AddProductionStepRequest":{"required":["description"],"type":"object","properties":{"stepNumber":{"minimum":1,"type":"integer","format":"int32"},"description":{"maxLength":500,"minLength":0,"type":"string"},"durationMinutes":{"minimum":1,"type":"integer","format":"int32"},"temperatureCelsius":{"maximum":1000,"minimum":-273,"type":"integer","format":"int32"}}},"AddRecipeIngredientRequest":{"required":["articleId","quantity","uom"],"type":"object","properties":{"position":{"minimum":1,"type":"integer","format":"int32"},"articleId":{"type":"string"},"quantity":{"type":"string"},"uom":{"type":"string"},"subRecipeId":{"type":"string"},"substitutable":{"type":"boolean"}}},"CreateProductionOrderRequest":{"required":["plannedDate","plannedQuantity","plannedQuantityUnit","priority","recipeId"],"type":"object","properties":{"recipeId":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"plannedDate":{"type":"string","format":"date"},"priority":{"type":"string"},"notes":{"type":"string"}}},"ProductionOrderResponse":{"type":"object","properties":{"id":{"type":"string"},"recipeId":{"type":"string"},"status":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"plannedDate":{"type":"string","format":"date"},"priority":{"type":"string"},"notes":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"PlanBatchRequest":{"required":["bestBeforeDate","plannedQuantity","plannedQuantityUnit","productionDate","recipeId"],"type":"object","properties":{"recipeId":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"productionDate":{"type":"string","format":"date"},"bestBeforeDate":{"type":"string","format":"date"}}},"BatchResponse":{"type":"object","properties":{"id":{"type":"string"},"batchNumber":{"type":"string"},"recipeId":{"type":"string"},"status":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"actualQuantity":{"type":"string"},"actualQuantityUnit":{"type":"string"},"waste":{"type":"string"},"wasteUnit":{"type":"string"},"remarks":{"type":"string"},"productionDate":{"type":"string","format":"date"},"bestBeforeDate":{"type":"string","format":"date"},"consumptions":{"type":"array","items":{"$ref":"#/components/schemas/ConsumptionResponse"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"cancellationReason":{"type":"string"},"cancelledAt":{"type":"string","format":"date-time"}}},"ConsumptionResponse":{"type":"object","properties":{"id":{"type":"string"},"inputBatchId":{"type":"string"},"articleId":{"type":"string"},"quantityUsed":{"type":"string"},"quantityUsedUnit":{"type":"string"},"consumedAt":{"type":"string","format":"date-time"}}},"RecordConsumptionRequest":{"required":["articleId","inputBatchId","quantityUnit","quantityUsed"],"type":"object","properties":{"inputBatchId":{"type":"string"},"articleId":{"type":"string"},"quantityUsed":{"type":"string"},"quantityUnit":{"type":"string"}}},"CompleteBatchRequest":{"required":["actualQuantity","actualQuantityUnit","waste","wasteUnit"],"type":"object","properties":{"actualQuantity":{"type":"string"},"actualQuantityUnit":{"type":"string"},"waste":{"type":"string"},"wasteUnit":{"type":"string"},"remarks":{"type":"string"}}},"CancelBatchRequest":{"required":["reason"],"type":"object","properties":{"reason":{"type":"string"}}},"CreateStorageLocationRequest":{"required":["name","storageType"],"type":"object","properties":{"name":{"type":"string"},"storageType":{"type":"string"},"minTemperature":{"type":"string"},"maxTemperature":{"type":"string"}}},"CreateStockRequest":{"required":["articleId","storageLocationId"],"type":"object","properties":{"articleId":{"type":"string"},"storageLocationId":{"type":"string"},"minimumLevelAmount":{"type":"string"},"minimumLevelUnit":{"type":"string"},"minimumShelfLifeDays":{"type":"integer","format":"int32"}}},"CreateStockResponse":{"required":["articleId","id","storageLocationId"],"type":"object","properties":{"id":{"type":"string"},"articleId":{"type":"string"},"storageLocationId":{"type":"string"},"minimumLevel":{"$ref":"#/components/schemas/MinimumLevelResponse"},"minimumShelfLifeDays":{"type":"integer","format":"int32","nullable":true}}},"ReserveStockRequest":{"required":["priority","quantityAmount","quantityUnit","referenceId","referenceType"],"type":"object","properties":{"referenceType":{"type":"string"},"referenceId":{"type":"string"},"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"},"priority":{"type":"string"}}},"AddStockBatchRequest":{"required":["batchId","batchType","expiryDate","quantityAmount","quantityUnit"],"type":"object","properties":{"batchId":{"type":"string"},"batchType":{"type":"string"},"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"},"expiryDate":{"type":"string"}}},"RemoveStockBatchRequest":{"required":["quantityAmount","quantityUnit"],"type":"object","properties":{"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"}}},"BlockStockBatchRequest":{"required":["reason"],"type":"object","properties":{"reason":{"type":"string"}}},"CreateCustomerRequest":{"required":["city","country","name","phone","postalCode","street","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["B2C","B2B"]},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"AddDeliveryAddressRequest":{"required":["city","country","postalCode","street"],"type":"object","properties":{"label":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"contactPerson":{"type":"string"},"deliveryNotes":{"type":"string"}}},"CreateProductCategoryRequest":{"required":["name"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}},"RefreshTokenRequest":{"required":["refreshToken"],"type":"object","properties":{"refreshToken":{"type":"string","description":"Refresh token"}},"description":"Refresh token request"},"LoginResponse":{"required":["accessToken","expiresAt","expiresIn","refreshToken","tokenType"],"type":"object","properties":{"accessToken":{"type":"string","description":"JWT access token","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"tokenType":{"type":"string","description":"Token type","example":"Bearer"},"expiresIn":{"type":"integer","description":"Token expiration time in seconds","format":"int64","example":3600},"expiresAt":{"type":"string","description":"Token expiration timestamp","format":"date-time"},"refreshToken":{"type":"string","description":"Refresh token for obtaining new access token"}},"description":"Login response with JWT tokens"},"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string","description":"Username","example":"admin"},"password":{"type":"string","description":"Password","example":"admin123"}},"description":"Login request with username and password"},"CreateArticleRequest":{"required":["articleNumber","categoryId","name","price","priceModel","unit"],"type":"object","properties":{"name":{"type":"string"},"articleNumber":{"type":"string"},"categoryId":{"type":"string"},"unit":{"type":"string","enum":["PIECE_FIXED","KG","HUNDRED_GRAM","PIECE_VARIABLE"]},"priceModel":{"type":"string","enum":["FIXED","WEIGHT_BASED"]},"price":{"type":"number"}}},"AssignSupplierRequest":{"required":["supplierId"],"type":"object","properties":{"supplierId":{"type":"string"}}},"AddSalesUnitRequest":{"required":["price","priceModel","unit"],"type":"object","properties":{"unit":{"type":"string","enum":["PIECE_FIXED","KG","HUNDRED_GRAM","PIECE_VARIABLE"]},"priceModel":{"type":"string","enum":["FIXED","WEIGHT_BASED"]},"price":{"type":"number"}}},"RecipeSummaryResponse":{"required":["articleId","createdAt","description","id","ingredientCount","name","outputQuantity","outputUom","status","stepCount","type","updatedAt","version","yieldPercentage"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"type":{"type":"string"},"description":{"type":"string"},"yieldPercentage":{"type":"integer","format":"int32"},"shelfLifeDays":{"type":"integer","format":"int32","nullable":true},"outputQuantity":{"type":"string"},"outputUom":{"type":"string"},"articleId":{"type":"string"},"status":{"type":"string"},"ingredientCount":{"type":"integer","format":"int32"},"stepCount":{"type":"integer","format":"int32"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"BatchSummaryResponse":{"type":"object","properties":{"id":{"type":"string"},"batchNumber":{"type":"string"},"recipeId":{"type":"string"},"status":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"productionDate":{"type":"string","format":"date"},"bestBeforeDate":{"type":"string","format":"date"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"RemoveCertificateRequest":{"required":["certificateType"],"type":"object","properties":{"certificateType":{"type":"string"},"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date"}}}},"securitySchemes":{"Bearer Authentication":{"type":"http","description":"JWT authentication token obtained from POST /api/auth/login.\n\nFormat: Bearer \n\nExample:\nBearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n","scheme":"bearer","bearerFormat":"JWT"}}}}
\ No newline at end of file
+{"openapi":"3.0.1","info":{"title":"Effigenix Fleischerei ERP API","description":"RESTful API for Effigenix Fleischerei ERP System.\n\n## Authentication\n\nAll endpoints (except /api/auth/login and /api/auth/refresh) require JWT authentication.\n\n1. Login via POST /api/auth/login with username and password\n2. Copy the returned access token\n3. Click \"Authorize\" button (top right)\n4. Enter: Bearer \n5. Click \"Authorize\"\n\n## User Management\n\n- **Authentication**: Login, logout, refresh token\n- **User Management**: Create, update, list users (ADMIN only)\n- **Role Management**: Assign roles, lock/unlock users (ADMIN only)\n- **Password Management**: Change password (requires current password)\n\n## Error Handling\n\nAll errors return a consistent error response format:\n\n```json\n{\n \"code\": \"USER_NOT_FOUND\",\n \"message\": \"User with ID 'user-123' not found\",\n \"status\": 404,\n \"timestamp\": \"2026-02-17T12:00:00\",\n \"path\": \"/api/users/user-123\",\n \"validationErrors\": null\n}\n```\n\n## Architecture\n\nBuilt with:\n- Domain-Driven Design (DDD)\n- Clean Architecture (Hexagonal Architecture)\n- Spring Boot 3.2\n- Java 21\n- PostgreSQL\n","contact":{"name":"Effigenix Development Team","url":"https://effigenix.com","email":"dev@effigenix.com"},"license":{"name":"Proprietary","url":"https://effigenix.com/license"},"version":"0.1.0"},"servers":[{"url":"http://localhost:8080","description":"Local Development Server"},{"url":"https://api.effigenix.com","description":"Production Server"}],"tags":[{"name":"Storage Locations","description":"Storage location management endpoints"},{"name":"User Management","description":"User management endpoints (requires authentication)"},{"name":"Countries","description":"ISO 3166-1 country reference data"},{"name":"Recipes","description":"Recipe management endpoints"},{"name":"Batches","description":"Production batch management endpoints"},{"name":"Role Management","description":"Role management endpoints (ADMIN only)"},{"name":"Suppliers","description":"Supplier management endpoints"},{"name":"Stock Movements","description":"Stock movement tracking endpoints"},{"name":"Product Categories","description":"Product category management endpoints"},{"name":"Articles","description":"Article management endpoints"},{"name":"Stocks","description":"Stock management endpoints"},{"name":"Customers","description":"Customer management endpoints"},{"name":"Authentication","description":"Authentication and session management endpoints"},{"name":"Production Orders","description":"Production order management endpoints"}],"paths":{"/api/users/{id}":{"get":{"tags":["User Management"],"summary":"Get user by ID","description":"Retrieve a single user by their ID.","operationId":"getUserById","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User retrieved successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"401":{"description":"Authentication required","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["User Management"],"summary":"Update user","description":"Update user details (email, branchId).","operationId":"updateUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}},"required":true},"responses":{"200":{"description":"User updated successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Email already exists","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/password":{"put":{"tags":["User Management"],"summary":"Change password","description":"Change user password. Requires current password for verification.","operationId":"changePassword","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}},"required":true},"responses":{"400":{"description":"Invalid password"},"401":{"description":"Invalid current password"},"404":{"description":"User not found"},"204":{"description":"Password changed successfully"}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}":{"get":{"tags":["Suppliers"],"operationId":"getSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Suppliers"],"operationId":"updateSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations/{id}":{"get":{"tags":["Storage Locations"],"operationId":"getStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Storage Locations"],"operationId":"updateStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStorageLocationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{id}":{"get":{"tags":["Stocks"],"operationId":"getStock","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Stocks"],"operationId":"updateStock","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStockRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}":{"get":{"tags":["Customers"],"operationId":"getCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Customers"],"operationId":"updateCustomer","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomerRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/preferences":{"put":{"tags":["Customers"],"operationId":"setPreferences","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPreferencesRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/frame-contract":{"put":{"tags":["Customers"],"operationId":"setFrameContract","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetFrameContractRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"delete":{"tags":["Customers"],"operationId":"removeFrameContract","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/categories/{id}":{"put":{"tags":["Product Categories"],"operationId":"updateCategory","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductCategoryRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"delete":{"tags":["Product Categories"],"operationId":"deleteCategory","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}":{"get":{"tags":["Articles"],"operationId":"getArticle","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"put":{"tags":["Articles"],"operationId":"updateArticle","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateArticleRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/sales-units/{suId}/price":{"put":{"tags":["Articles"],"operationId":"updateSalesUnitPrice","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"suId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSalesUnitPriceRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users":{"get":{"tags":["User Management"],"summary":"List all users","description":"Get a list of all users in the system.","operationId":"listUsers","responses":{"200":{"description":"Users retrieved successfully","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDTO"}}}}},"401":{"description":"Authentication required","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDTO"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["User Management"],"summary":"Create user (ADMIN only)","description":"Create a new user account with specified roles.","operationId":"createUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}},"required":true},"responses":{"400":{"description":"Validation error or invalid password","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"201":{"description":"User created successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Username or email already exists","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/unlock":{"post":{"tags":["User Management"],"summary":"Unlock user (ADMIN only)","description":"Unlock a user account (allows login).","operationId":"unlockUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User unlocked successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Invalid status transition","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/roles":{"post":{"tags":["User Management"],"summary":"Assign role (ADMIN only)","description":"Assign a role to a user.","operationId":"assignRole","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRoleRequest"}}},"required":true},"responses":{"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User or role not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"200":{"description":"Role assigned successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/lock":{"post":{"tags":["User Management"],"summary":"Lock user (ADMIN only)","description":"Lock a user account (prevents login).","operationId":"lockUser","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}}],"responses":{"403":{"description":"Missing permission","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"200":{"description":"User locked successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}},"409":{"description":"Invalid status transition","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers":{"get":{"tags":["Suppliers"],"operationId":"listSuppliers","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","INACTIVE"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SupplierResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Suppliers"],"operationId":"createSupplier","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/rating":{"post":{"tags":["Suppliers"],"operationId":"rateSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/deactivate":{"post":{"tags":["Suppliers"],"operationId":"deactivate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/certificates":{"post":{"tags":["Suppliers"],"operationId":"addCertificate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCertificateRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]},"delete":{"tags":["Suppliers"],"operationId":"removeCertificate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveCertificateRequest"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/suppliers/{id}/activate":{"post":{"tags":["Suppliers"],"operationId":"activate","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SupplierResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes":{"get":{"tags":["Recipes"],"operationId":"listRecipes","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecipeSummaryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Recipes"],"operationId":"createRecipe","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRecipeRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/steps":{"post":{"tags":["Recipes"],"operationId":"addProductionStep","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddProductionStepRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/ingredients":{"post":{"tags":["Recipes"],"operationId":"addIngredient","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddRecipeIngredientRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/archive":{"post":{"tags":["Recipes"],"operationId":"archiveRecipe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/activate":{"post":{"tags":["Recipes"],"operationId":"activateRecipe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/production-orders":{"post":{"tags":["Production Orders"],"operationId":"createProductionOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductionOrderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductionOrderResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/production-orders/{id}/start":{"post":{"tags":["Production Orders"],"operationId":"startProductionOrder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartProductionOrderRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductionOrderResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/production-orders/{id}/release":{"post":{"tags":["Production Orders"],"operationId":"releaseProductionOrder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductionOrderResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches":{"get":{"tags":["Batches"],"operationId":"listBatches","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"productionDate","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"articleId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BatchSummaryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Batches"],"operationId":"planBatch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/start":{"post":{"tags":["Batches"],"operationId":"startBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/consumptions":{"post":{"tags":["Batches"],"operationId":"recordConsumption","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordConsumptionRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ConsumptionResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/complete":{"post":{"tags":["Batches"],"operationId":"completeBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}/cancel":{"post":{"tags":["Batches"],"operationId":"cancelBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations":{"get":{"tags":["Storage Locations"],"operationId":"listStorageLocations","parameters":[{"name":"storageType","in":"query","required":false,"schema":{"type":"string"}},{"name":"active","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Storage Locations"],"operationId":"createStorageLocation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStorageLocationRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks":{"get":{"tags":["Stocks"],"operationId":"listStocks","parameters":[{"name":"storageLocationId","in":"query","required":false,"schema":{"type":"string"}},{"name":"articleId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Stocks"],"operationId":"createStock","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStockRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CreateStockResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/reservations":{"post":{"tags":["Stocks"],"operationId":"reserveStock","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReserveStockRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ReservationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches":{"post":{"tags":["Stocks"],"operationId":"addBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddStockBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockBatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches/{batchId}/unblock":{"post":{"tags":["Stocks"],"operationId":"unblockBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches/{batchId}/remove":{"post":{"tags":["Stocks"],"operationId":"removeBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoveStockBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/batches/{batchId}/block":{"post":{"tags":["Stocks"],"operationId":"blockBatch","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockStockBatchRequest"}}},"required":true},"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stock-movements":{"get":{"tags":["Stock Movements"],"summary":"List stock movements","description":"Filter priority (only one filter applied): stockId > articleId > batchReference > movementType > from/to","operationId":"listMovements","parameters":[{"name":"stockId","in":"query","required":false,"schema":{"type":"string"}},{"name":"articleId","in":"query","required":false,"schema":{"type":"string"}},{"name":"movementType","in":"query","required":false,"schema":{"type":"string"}},{"name":"batchReference","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockMovementResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Stock Movements"],"operationId":"recordMovement","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordStockMovementRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockMovementResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers":{"get":{"tags":["Customers"],"operationId":"listCustomers","parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["B2C","B2B"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","INACTIVE"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomerResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Customers"],"operationId":"createCustomer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/delivery-addresses":{"post":{"tags":["Customers"],"operationId":"addDeliveryAddress","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddDeliveryAddressRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/deactivate":{"post":{"tags":["Customers"],"operationId":"deactivate_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/activate":{"post":{"tags":["Customers"],"operationId":"activate_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/categories":{"get":{"tags":["Product Categories"],"operationId":"listCategories","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProductCategoryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Product Categories"],"operationId":"createCategory","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductCategoryRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProductCategoryResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/auth/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh access token","description":"Refresh an expired access token using a valid refresh token.","operationId":"refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}},"required":true},"responses":{"401":{"description":"Invalid or expired refresh token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"200":{"description":"Token refresh successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}}}}},"/api/auth/logout":{"post":{"tags":["Authentication"],"summary":"User logout","description":"Invalidate current JWT token.","operationId":"logout","responses":{"401":{"description":"Invalid or missing authentication token"},"204":{"description":"Logout successful"}}}},"/api/auth/login":{"post":{"tags":["Authentication"],"summary":"User login","description":"Authenticate user with username and password. Returns JWT tokens.","operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"429":{"description":"Too many login attempts","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"401":{"description":"Invalid credentials, user locked, or user inactive","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"200":{"description":"Login successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}}}}},"/api/articles":{"get":{"tags":["Articles"],"operationId":"listArticles","parameters":[{"name":"categoryId","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ACTIVE","INACTIVE"]}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ArticleResponse"}}}}}},"security":[{"Bearer Authentication":[]}]},"post":{"tags":["Articles"],"operationId":"createArticle","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateArticleRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/suppliers":{"post":{"tags":["Articles"],"operationId":"assignSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignSupplierRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/sales-units":{"post":{"tags":["Articles"],"operationId":"addSalesUnit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSalesUnitRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/deactivate":{"post":{"tags":["Articles"],"operationId":"deactivate_2","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/activate":{"post":{"tags":["Articles"],"operationId":"activate_2","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ArticleResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations/{id}/deactivate":{"patch":{"tags":["Storage Locations"],"operationId":"deactivateStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/storage-locations/{id}/activate":{"patch":{"tags":["Storage Locations"],"operationId":"activateStorageLocation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StorageLocationResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/roles":{"get":{"tags":["Role Management"],"summary":"List all roles (ADMIN only)","description":"Get a list of all available roles in the system. Requires USER_MANAGEMENT permission.","operationId":"listRoles","responses":{"403":{"description":"Missing USER_MANAGEMENT permission","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleDTO"}}}}},"401":{"description":"Authentication required","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleDTO"}}}}},"200":{"description":"Roles retrieved successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoleDTO"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}":{"get":{"tags":["Recipes"],"operationId":"getRecipe","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RecipeResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/{id}":{"get":{"tags":["Batches"],"operationId":"getBatch","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/production/batches/by-number/{batchNumber}":{"get":{"tags":["Batches"],"operationId":"findByNumber","parameters":[{"name":"batchNumber","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/below-minimum":{"get":{"tags":["Stocks"],"operationId":"listStocksBelowMinimum","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StockResponse"}}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stock-movements/{id}":{"get":{"tags":["Stock Movements"],"operationId":"getMovement","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StockMovementResponse"}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/countries":{"get":{"tags":["Countries"],"operationId":"search","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":""}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CountryResponse"}}}}}},"security":[{"Bearer Authentication":[]}]}},"/api/users/{id}/roles/{roleName}":{"delete":{"tags":["User Management"],"summary":"Remove role (ADMIN only)","description":"Remove a role from a user.","operationId":"removeRole","parameters":[{"name":"id","in":"path","description":"User ID","required":true,"schema":{"type":"string"}},{"name":"roleName","in":"path","description":"Role name","required":true,"schema":{"type":"string","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]}}],"responses":{"403":{"description":"Missing permission"},"404":{"description":"User or role not found"},"204":{"description":"Role removed successfully"}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/steps/{stepNumber}":{"delete":{"tags":["Recipes"],"operationId":"removeProductionStep","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"stepNumber","in":"path","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/recipes/{id}/ingredients/{ingredientId}":{"delete":{"tags":["Recipes"],"operationId":"removeIngredient","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"ingredientId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/inventory/stocks/{stockId}/reservations/{reservationId}":{"delete":{"tags":["Stocks"],"operationId":"releaseReservation","parameters":[{"name":"stockId","in":"path","required":true,"schema":{"type":"string"}},{"name":"reservationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/customers/{id}/delivery-addresses/{label}":{"delete":{"tags":["Customers"],"operationId":"removeDeliveryAddress","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"label","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/suppliers/{supplierId}":{"delete":{"tags":["Articles"],"operationId":"removeSupplier","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"supplierId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}},"/api/articles/{id}/sales-units/{suId}":{"delete":{"tags":["Articles"],"operationId":"removeSalesUnit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"suId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}},"security":[{"Bearer Authentication":[]}]}}},"components":{"schemas":{"UpdateUserRequest":{"type":"object","properties":{"email":{"type":"string","description":"New email address","example":"newemail@example.com"},"branchId":{"type":"string","description":"New branch ID","example":"BRANCH-002"}},"description":"Request to update user details"},"RoleDTO":{"required":["description","id","name","permissions"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]},"permissions":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["RECIPE_READ","RECIPE_WRITE","RECIPE_DELETE","BATCH_READ","BATCH_WRITE","BATCH_COMPLETE","BATCH_CANCEL","BATCH_DELETE","PRODUCTION_ORDER_READ","PRODUCTION_ORDER_WRITE","PRODUCTION_ORDER_DELETE","HACCP_READ","HACCP_WRITE","TEMPERATURE_LOG_READ","TEMPERATURE_LOG_WRITE","CLEANING_RECORD_READ","CLEANING_RECORD_WRITE","GOODS_INSPECTION_READ","GOODS_INSPECTION_WRITE","STOCK_READ","STOCK_WRITE","STOCK_MOVEMENT_READ","STOCK_MOVEMENT_WRITE","INVENTORY_COUNT_READ","INVENTORY_COUNT_WRITE","PURCHASE_ORDER_READ","PURCHASE_ORDER_WRITE","PURCHASE_ORDER_DELETE","GOODS_RECEIPT_READ","GOODS_RECEIPT_WRITE","SUPPLIER_READ","SUPPLIER_WRITE","SUPPLIER_DELETE","ORDER_READ","ORDER_WRITE","ORDER_DELETE","INVOICE_READ","INVOICE_WRITE","INVOICE_DELETE","CUSTOMER_READ","CUSTOMER_WRITE","CUSTOMER_DELETE","LABEL_READ","LABEL_WRITE","LABEL_PRINT","MASTERDATA_READ","MASTERDATA_WRITE","BRANCH_READ","BRANCH_WRITE","BRANCH_DELETE","USER_READ","USER_WRITE","USER_DELETE","USER_LOCK","USER_UNLOCK","ROLE_READ","ROLE_WRITE","ROLE_ASSIGN","ROLE_REMOVE","REPORT_READ","REPORT_GENERATE","NOTIFICATION_READ","NOTIFICATION_SEND","AUDIT_LOG_READ","SYSTEM_SETTINGS_READ","SYSTEM_SETTINGS_WRITE"]}},"description":{"type":"string"}}},"UserDTO":{"required":["createdAt","email","id","roles","status","username"],"type":"object","properties":{"id":{"type":"string"},"username":{"type":"string"},"email":{"type":"string"},"roles":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/RoleDTO"}},"branchId":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","INACTIVE","LOCKED"]},"createdAt":{"type":"string","format":"date-time"},"lastLogin":{"type":"string","format":"date-time"}}},"ChangePasswordRequest":{"required":["currentPassword","newPassword"],"type":"object","properties":{"currentPassword":{"type":"string","description":"Current password","example":"OldPass123"},"newPassword":{"maxLength":2147483647,"minLength":8,"type":"string","description":"New password (min 8 characters)","example":"NewSecurePass456"}},"description":"Request to change user password"},"UpdateSupplierRequest":{"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"AddressResponse":{"required":["city","country","houseNumber","postalCode","street"],"type":"object","properties":{"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"}},"nullable":true},"ContactInfoResponse":{"required":["contactPerson","email","phone"],"type":"object","properties":{"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"}}},"PaymentTermsResponse":{"required":["paymentDescription","paymentDueDays"],"type":"object","properties":{"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}},"nullable":true},"QualityCertificateResponse":{"required":["certificateType","issuer","validFrom","validUntil"],"type":"object","properties":{"certificateType":{"type":"string"},"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"}}},"SupplierRatingResponse":{"required":["deliveryScore","priceScore","qualityScore"],"type":"object","properties":{"qualityScore":{"type":"integer","format":"int32"},"deliveryScore":{"type":"integer","format":"int32"},"priceScore":{"type":"integer","format":"int32"}},"nullable":true},"SupplierResponse":{"required":["certificates","contactInfo","createdAt","id","name","status","updatedAt"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"address":{"$ref":"#/components/schemas/AddressResponse"},"contactInfo":{"$ref":"#/components/schemas/ContactInfoResponse"},"paymentTerms":{"$ref":"#/components/schemas/PaymentTermsResponse"},"certificates":{"type":"array","items":{"$ref":"#/components/schemas/QualityCertificateResponse"}},"rating":{"$ref":"#/components/schemas/SupplierRatingResponse"},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"UpdateStorageLocationRequest":{"type":"object","properties":{"name":{"type":"string"},"minTemperature":{"type":"string"},"maxTemperature":{"type":"string"}}},"StorageLocationResponse":{"required":["active","id","name","storageType"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"storageType":{"type":"string"},"temperatureRange":{"$ref":"#/components/schemas/TemperatureRangeResponse"},"active":{"type":"boolean"}}},"TemperatureRangeResponse":{"required":["maxTemperature","minTemperature"],"type":"object","properties":{"minTemperature":{"type":"number"},"maxTemperature":{"type":"number"}},"nullable":true},"UpdateStockRequest":{"type":"object","properties":{"minimumLevelAmount":{"type":"string"},"minimumLevelUnit":{"type":"string"},"minimumShelfLifeDays":{"type":"integer","format":"int32"}}},"MinimumLevelResponse":{"required":["amount","unit"],"type":"object","properties":{"amount":{"type":"number"},"unit":{"type":"string"}},"nullable":true},"ReservationResponse":{"type":"object","properties":{"id":{"type":"string"},"referenceType":{"type":"string"},"referenceId":{"type":"string"},"quantityAmount":{"type":"number"},"quantityUnit":{"type":"string"},"priority":{"type":"string"},"reservedAt":{"type":"string","format":"date-time"},"allocations":{"type":"array","items":{"$ref":"#/components/schemas/StockBatchAllocationResponse"}}}},"StockBatchAllocationResponse":{"type":"object","properties":{"stockBatchId":{"type":"string"},"allocatedQuantityAmount":{"type":"number"},"allocatedQuantityUnit":{"type":"string"}}},"StockBatchResponse":{"type":"object","properties":{"id":{"type":"string"},"batchId":{"type":"string"},"batchType":{"type":"string"},"quantityAmount":{"type":"number"},"quantityUnit":{"type":"string"},"expiryDate":{"type":"string","format":"date"},"status":{"type":"string"},"receivedAt":{"type":"string","format":"date-time"}}},"StockResponse":{"required":["articleId","availableQuantity","batches","id","reservations","storageLocationId","totalQuantity"],"type":"object","properties":{"id":{"type":"string"},"articleId":{"type":"string"},"storageLocationId":{"type":"string"},"minimumLevel":{"$ref":"#/components/schemas/MinimumLevelResponse"},"minimumShelfLifeDays":{"type":"integer","format":"int32","nullable":true},"batches":{"type":"array","items":{"$ref":"#/components/schemas/StockBatchResponse"}},"totalQuantity":{"type":"number"},"quantityUnit":{"type":"string","nullable":true},"availableQuantity":{"type":"number"},"reservations":{"type":"array","items":{"$ref":"#/components/schemas/ReservationResponse"}}}},"UpdateCustomerRequest":{"type":"object","properties":{"name":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"ContractLineItemResponse":{"required":["agreedPrice","agreedQuantity","articleId","unit"],"type":"object","properties":{"articleId":{"type":"string"},"agreedPrice":{"type":"number"},"agreedQuantity":{"type":"number"},"unit":{"type":"string"}}},"CustomerResponse":{"required":["billingAddress","contactInfo","createdAt","deliveryAddresses","id","name","preferences","status","type","updatedAt"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"billingAddress":{"$ref":"#/components/schemas/AddressResponse"},"contactInfo":{"$ref":"#/components/schemas/ContactInfoResponse"},"paymentTerms":{"$ref":"#/components/schemas/PaymentTermsResponse"},"deliveryAddresses":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryAddressResponse"}},"frameContract":{"$ref":"#/components/schemas/FrameContractResponse"},"preferences":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"DeliveryAddressResponse":{"required":["address","contactPerson","deliveryNotes","label"],"type":"object","properties":{"label":{"type":"string"},"address":{"$ref":"#/components/schemas/AddressResponse"},"contactPerson":{"type":"string"},"deliveryNotes":{"type":"string"}}},"FrameContractResponse":{"required":["deliveryRhythm","id","lineItems","validFrom","validUntil"],"type":"object","properties":{"id":{"type":"string"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"},"deliveryRhythm":{"type":"string"},"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/ContractLineItemResponse"}}},"nullable":true},"SetPreferencesRequest":{"required":["preferences"],"type":"object","properties":{"preferences":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["BIO","REGIONAL","TIERWOHL","HALAL","KOSHER","GLUTENFREI","LAKTOSEFREI"]}}}},"LineItem":{"required":["agreedPrice","articleId"],"type":"object","properties":{"articleId":{"type":"string"},"agreedPrice":{"type":"number"},"agreedQuantity":{"type":"number"},"unit":{"type":"string","enum":["PIECE_FIXED","KG","HUNDRED_GRAM","PIECE_VARIABLE"]}}},"SetFrameContractRequest":{"required":["lineItems","rhythm"],"type":"object","properties":{"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"},"rhythm":{"type":"string","enum":["DAILY","WEEKLY","BIWEEKLY","MONTHLY","ON_DEMAND"]},"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/LineItem"}}}},"UpdateProductCategoryRequest":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}},"ProductCategoryResponse":{"required":["description","id","name"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"}}},"UpdateArticleRequest":{"type":"object","properties":{"name":{"type":"string"},"categoryId":{"type":"string"}}},"ArticleResponse":{"required":["articleNumber","categoryId","createdAt","id","name","salesUnits","status","supplierIds","updatedAt"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"articleNumber":{"type":"string"},"categoryId":{"type":"string"},"salesUnits":{"type":"array","items":{"$ref":"#/components/schemas/SalesUnitResponse"}},"status":{"type":"string"},"supplierIds":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SalesUnitResponse":{"required":["id","price","priceModel","unit"],"type":"object","properties":{"id":{"type":"string"},"unit":{"type":"string"},"priceModel":{"type":"string"},"price":{"type":"number"}}},"UpdateSalesUnitPriceRequest":{"required":["price"],"type":"object","properties":{"price":{"type":"number"}}},"CreateUserRequest":{"required":["email","password","roleNames","username"],"type":"object","properties":{"username":{"maxLength":50,"minLength":3,"type":"string","description":"Username (unique)","example":"john.doe"},"email":{"type":"string","description":"Email address (unique)","example":"john.doe@example.com"},"password":{"maxLength":2147483647,"minLength":8,"type":"string","description":"Password (min 8 characters)","example":"SecurePass123"},"roleNames":{"uniqueItems":true,"type":"array","description":"Role names to assign","example":["USER","MANAGER"],"items":{"type":"string","description":"Role names to assign","example":"[\"USER\",\"MANAGER\"]","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]}},"branchId":{"type":"string","description":"Branch ID (optional)","example":"BRANCH-001"}},"description":"Request to create a new user"},"AssignRoleRequest":{"required":["roleName"],"type":"object","properties":{"roleName":{"type":"string","description":"Role name to assign","example":"MANAGER","enum":["ADMIN","PRODUCTION_MANAGER","PRODUCTION_WORKER","QUALITY_MANAGER","QUALITY_INSPECTOR","PROCUREMENT_MANAGER","WAREHOUSE_WORKER","SALES_MANAGER","SALES_STAFF"]}},"description":"Request to assign a role to a user"},"CreateSupplierRequest":{"required":["name","phone"],"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"RateSupplierRequest":{"type":"object","properties":{"qualityScore":{"maximum":5,"minimum":1,"type":"integer","format":"int32"},"deliveryScore":{"maximum":5,"minimum":1,"type":"integer","format":"int32"},"priceScore":{"maximum":5,"minimum":1,"type":"integer","format":"int32"}}},"AddCertificateRequest":{"required":["certificateType"],"type":"object","properties":{"certificateType":{"type":"string"},"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date"},"validUntil":{"type":"string","format":"date"}}},"CreateRecipeRequest":{"required":["articleId","name","outputQuantity","outputUom","type"],"type":"object","properties":{"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"type":{"type":"string","enum":["RAW_MATERIAL","INTERMEDIATE","FINISHED_PRODUCT"]},"description":{"type":"string"},"yieldPercentage":{"type":"integer","format":"int32"},"shelfLifeDays":{"type":"integer","format":"int32"},"outputQuantity":{"type":"string"},"outputUom":{"type":"string"},"articleId":{"type":"string"}}},"IngredientResponse":{"required":["articleId","id","position","quantity","substitutable","uom"],"type":"object","properties":{"id":{"type":"string"},"position":{"type":"integer","format":"int32"},"articleId":{"type":"string"},"quantity":{"type":"string"},"uom":{"type":"string"},"subRecipeId":{"type":"string","nullable":true},"substitutable":{"type":"boolean"}}},"ProductionStepResponse":{"required":["description","id","stepNumber"],"type":"object","properties":{"id":{"type":"string"},"stepNumber":{"type":"integer","format":"int32"},"description":{"type":"string"},"durationMinutes":{"type":"integer","format":"int32","nullable":true},"temperatureCelsius":{"type":"integer","format":"int32","nullable":true}}},"RecipeResponse":{"required":["articleId","createdAt","description","id","ingredients","name","outputQuantity","outputUom","productionSteps","status","type","updatedAt","version","yieldPercentage"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"type":{"type":"string"},"description":{"type":"string"},"yieldPercentage":{"type":"integer","format":"int32"},"shelfLifeDays":{"type":"integer","format":"int32","nullable":true},"outputQuantity":{"type":"string"},"outputUom":{"type":"string"},"articleId":{"type":"string"},"status":{"type":"string"},"ingredients":{"type":"array","items":{"$ref":"#/components/schemas/IngredientResponse"}},"productionSteps":{"type":"array","items":{"$ref":"#/components/schemas/ProductionStepResponse"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"AddProductionStepRequest":{"required":["description"],"type":"object","properties":{"stepNumber":{"minimum":1,"type":"integer","format":"int32"},"description":{"maxLength":500,"minLength":0,"type":"string"},"durationMinutes":{"minimum":1,"type":"integer","format":"int32"},"temperatureCelsius":{"maximum":1000,"minimum":-273,"type":"integer","format":"int32"}}},"AddRecipeIngredientRequest":{"required":["articleId","quantity","uom"],"type":"object","properties":{"position":{"minimum":1,"type":"integer","format":"int32"},"articleId":{"type":"string"},"quantity":{"type":"string"},"uom":{"type":"string"},"subRecipeId":{"type":"string"},"substitutable":{"type":"boolean"}}},"CreateProductionOrderRequest":{"required":["plannedDate","plannedQuantity","plannedQuantityUnit","priority","recipeId"],"type":"object","properties":{"recipeId":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"plannedDate":{"type":"string","format":"date"},"priority":{"type":"string"},"notes":{"type":"string"}}},"ProductionOrderResponse":{"type":"object","properties":{"id":{"type":"string"},"recipeId":{"type":"string"},"status":{"type":"string"},"batchId":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"plannedDate":{"type":"string","format":"date"},"priority":{"type":"string"},"notes":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"StartProductionOrderRequest":{"required":["batchId"],"type":"object","properties":{"batchId":{"type":"string"}}},"PlanBatchRequest":{"required":["bestBeforeDate","plannedQuantity","plannedQuantityUnit","productionDate","recipeId"],"type":"object","properties":{"recipeId":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"productionDate":{"type":"string","format":"date"},"bestBeforeDate":{"type":"string","format":"date"}}},"BatchResponse":{"type":"object","properties":{"id":{"type":"string"},"batchNumber":{"type":"string"},"recipeId":{"type":"string"},"status":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"actualQuantity":{"type":"string"},"actualQuantityUnit":{"type":"string"},"waste":{"type":"string"},"wasteUnit":{"type":"string"},"remarks":{"type":"string"},"productionDate":{"type":"string","format":"date"},"bestBeforeDate":{"type":"string","format":"date"},"consumptions":{"type":"array","items":{"$ref":"#/components/schemas/ConsumptionResponse"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"cancellationReason":{"type":"string"},"cancelledAt":{"type":"string","format":"date-time"}}},"ConsumptionResponse":{"type":"object","properties":{"id":{"type":"string"},"inputBatchId":{"type":"string"},"articleId":{"type":"string"},"quantityUsed":{"type":"string"},"quantityUsedUnit":{"type":"string"},"consumedAt":{"type":"string","format":"date-time"}}},"RecordConsumptionRequest":{"required":["articleId","inputBatchId","quantityUnit","quantityUsed"],"type":"object","properties":{"inputBatchId":{"type":"string"},"articleId":{"type":"string"},"quantityUsed":{"type":"string"},"quantityUnit":{"type":"string"}}},"CompleteBatchRequest":{"required":["actualQuantity","actualQuantityUnit","waste","wasteUnit"],"type":"object","properties":{"actualQuantity":{"type":"string"},"actualQuantityUnit":{"type":"string"},"waste":{"type":"string"},"wasteUnit":{"type":"string"},"remarks":{"type":"string"}}},"CancelBatchRequest":{"required":["reason"],"type":"object","properties":{"reason":{"type":"string"}}},"CreateStorageLocationRequest":{"required":["name","storageType"],"type":"object","properties":{"name":{"type":"string"},"storageType":{"type":"string"},"minTemperature":{"type":"string"},"maxTemperature":{"type":"string"}}},"CreateStockRequest":{"required":["articleId","storageLocationId"],"type":"object","properties":{"articleId":{"type":"string"},"storageLocationId":{"type":"string"},"minimumLevelAmount":{"type":"string"},"minimumLevelUnit":{"type":"string"},"minimumShelfLifeDays":{"type":"integer","format":"int32"}}},"CreateStockResponse":{"required":["articleId","id","storageLocationId"],"type":"object","properties":{"id":{"type":"string"},"articleId":{"type":"string"},"storageLocationId":{"type":"string"},"minimumLevel":{"$ref":"#/components/schemas/MinimumLevelResponse"},"minimumShelfLifeDays":{"type":"integer","format":"int32","nullable":true}}},"ReserveStockRequest":{"required":["priority","quantityAmount","quantityUnit","referenceId","referenceType"],"type":"object","properties":{"referenceType":{"type":"string"},"referenceId":{"type":"string"},"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"},"priority":{"type":"string"}}},"AddStockBatchRequest":{"required":["batchId","batchType","expiryDate","quantityAmount","quantityUnit"],"type":"object","properties":{"batchId":{"type":"string"},"batchType":{"type":"string"},"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"},"expiryDate":{"type":"string"}}},"RemoveStockBatchRequest":{"required":["quantityAmount","quantityUnit"],"type":"object","properties":{"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"}}},"BlockStockBatchRequest":{"required":["reason"],"type":"object","properties":{"reason":{"type":"string"}}},"RecordStockMovementRequest":{"required":["articleId","batchId","batchType","movementType","quantityAmount","quantityUnit","stockBatchId","stockId"],"type":"object","properties":{"stockId":{"type":"string"},"articleId":{"type":"string"},"stockBatchId":{"type":"string"},"batchId":{"type":"string"},"batchType":{"type":"string"},"movementType":{"type":"string"},"direction":{"type":"string"},"quantityAmount":{"type":"string"},"quantityUnit":{"type":"string"},"reason":{"type":"string"},"referenceDocumentId":{"type":"string"}}},"StockMovementResponse":{"required":["articleId","batchId","batchType","direction","id","movementType","performedAt","performedBy","quantityAmount","quantityUnit","stockBatchId","stockId"],"type":"object","properties":{"id":{"type":"string"},"stockId":{"type":"string"},"articleId":{"type":"string"},"stockBatchId":{"type":"string"},"batchId":{"type":"string"},"batchType":{"type":"string"},"movementType":{"type":"string"},"direction":{"type":"string"},"quantityAmount":{"type":"number"},"quantityUnit":{"type":"string"},"reason":{"type":"string","nullable":true},"referenceDocumentId":{"type":"string","nullable":true},"performedBy":{"type":"string"},"performedAt":{"type":"string","format":"date-time"}}},"CreateCustomerRequest":{"required":["city","country","name","phone","postalCode","street","type"],"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["B2C","B2B"]},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"contactPerson":{"type":"string"},"paymentDueDays":{"type":"integer","format":"int32"},"paymentDescription":{"type":"string"}}},"AddDeliveryAddressRequest":{"required":["city","country","postalCode","street"],"type":"object","properties":{"label":{"type":"string"},"street":{"type":"string"},"houseNumber":{"type":"string"},"postalCode":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"contactPerson":{"type":"string"},"deliveryNotes":{"type":"string"}}},"CreateProductCategoryRequest":{"required":["name"],"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}},"RefreshTokenRequest":{"required":["refreshToken"],"type":"object","properties":{"refreshToken":{"type":"string","description":"Refresh token"}},"description":"Refresh token request"},"LoginResponse":{"required":["accessToken","expiresAt","expiresIn","refreshToken","tokenType"],"type":"object","properties":{"accessToken":{"type":"string","description":"JWT access token","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"tokenType":{"type":"string","description":"Token type","example":"Bearer"},"expiresIn":{"type":"integer","description":"Token expiration time in seconds","format":"int64","example":3600},"expiresAt":{"type":"string","description":"Token expiration timestamp","format":"date-time"},"refreshToken":{"type":"string","description":"Refresh token for obtaining new access token"}},"description":"Login response with JWT tokens"},"LoginRequest":{"required":["password","username"],"type":"object","properties":{"username":{"type":"string","description":"Username","example":"admin"},"password":{"type":"string","description":"Password","example":"admin123"}},"description":"Login request with username and password"},"CreateArticleRequest":{"required":["articleNumber","categoryId","name","price","priceModel","unit"],"type":"object","properties":{"name":{"type":"string"},"articleNumber":{"type":"string"},"categoryId":{"type":"string"},"unit":{"type":"string","enum":["PIECE_FIXED","KG","HUNDRED_GRAM","PIECE_VARIABLE"]},"priceModel":{"type":"string","enum":["FIXED","WEIGHT_BASED"]},"price":{"type":"number"}}},"AssignSupplierRequest":{"required":["supplierId"],"type":"object","properties":{"supplierId":{"type":"string"}}},"AddSalesUnitRequest":{"required":["price","priceModel","unit"],"type":"object","properties":{"unit":{"type":"string","enum":["PIECE_FIXED","KG","HUNDRED_GRAM","PIECE_VARIABLE"]},"priceModel":{"type":"string","enum":["FIXED","WEIGHT_BASED"]},"price":{"type":"number"}}},"RecipeSummaryResponse":{"required":["articleId","createdAt","description","id","ingredientCount","name","outputQuantity","outputUom","status","stepCount","type","updatedAt","version","yieldPercentage"],"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"type":{"type":"string"},"description":{"type":"string"},"yieldPercentage":{"type":"integer","format":"int32"},"shelfLifeDays":{"type":"integer","format":"int32","nullable":true},"outputQuantity":{"type":"string"},"outputUom":{"type":"string"},"articleId":{"type":"string"},"status":{"type":"string"},"ingredientCount":{"type":"integer","format":"int32"},"stepCount":{"type":"integer","format":"int32"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"BatchSummaryResponse":{"type":"object","properties":{"id":{"type":"string"},"batchNumber":{"type":"string"},"recipeId":{"type":"string"},"status":{"type":"string"},"plannedQuantity":{"type":"string"},"plannedQuantityUnit":{"type":"string"},"productionDate":{"type":"string","format":"date"},"bestBeforeDate":{"type":"string","format":"date"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CountryResponse":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}}},"RemoveCertificateRequest":{"required":["certificateType"],"type":"object","properties":{"certificateType":{"type":"string"},"issuer":{"type":"string"},"validFrom":{"type":"string","format":"date"}}}},"securitySchemes":{"Bearer Authentication":{"type":"http","description":"JWT authentication token obtained from POST /api/auth/login.\n\nFormat: Bearer \n\nExample:\nBearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n","scheme":"bearer","bearerFormat":"JWT"}}}}
\ No newline at end of file
diff --git a/frontend/packages/api-client/src/index.ts b/frontend/packages/api-client/src/index.ts
index 9d3b64d..27cae2f 100644
--- a/frontend/packages/api-client/src/index.ts
+++ b/frontend/packages/api-client/src/index.ts
@@ -27,6 +27,7 @@ export { createRecipesResource } from './resources/recipes.js';
export { createBatchesResource } from './resources/batches.js';
export { createProductionOrdersResource } from './resources/production-orders.js';
export { createStocksResource } from './resources/stocks.js';
+export { createStockMovementsResource } from './resources/stock-movements.js';
export { createCountriesResource } from './resources/countries.js';
export {
ApiError,
@@ -112,6 +113,9 @@ export type {
ReservationDTO,
StockBatchAllocationDTO,
ReserveStockRequest,
+ StockMovementDTO,
+ RecordStockMovementRequest,
+ StartProductionOrderRequest,
CountryDTO,
} from '@effigenix/types';
@@ -140,9 +144,11 @@ export type { RecipesResource, RecipeType, RecipeStatus, UoM } from './resources
export { RECIPE_TYPE_LABELS, UOM_VALUES, UOM_LABELS } from './resources/recipes.js';
export type { BatchesResource, BatchStatus } from './resources/batches.js';
export { BATCH_STATUS_LABELS } from './resources/batches.js';
-export type { ProductionOrdersResource, Priority } from './resources/production-orders.js';
-export { PRIORITY_LABELS } from './resources/production-orders.js';
+export type { ProductionOrdersResource, Priority, ProductionOrderStatus } from './resources/production-orders.js';
+export { PRIORITY_LABELS, PRODUCTION_ORDER_STATUS_LABELS } from './resources/production-orders.js';
export type { StocksResource, BatchType, StockBatchStatus, StockFilter, ReferenceType, ReservationPriority } from './resources/stocks.js';
+export type { StockMovementsResource, MovementType, MovementDirection, StockMovementFilter } from './resources/stock-movements.js';
+export { MOVEMENT_TYPE_LABELS, MOVEMENT_DIRECTION_LABELS } from './resources/stock-movements.js';
export type { CountriesResource } from './resources/countries.js';
export { BATCH_TYPE_LABELS, STOCK_BATCH_STATUS_LABELS, REFERENCE_TYPE_LABELS, RESERVATION_PRIORITY_LABELS } from './resources/stocks.js';
@@ -159,6 +165,7 @@ import { createRecipesResource } from './resources/recipes.js';
import { createBatchesResource } from './resources/batches.js';
import { createProductionOrdersResource } from './resources/production-orders.js';
import { createStocksResource } from './resources/stocks.js';
+import { createStockMovementsResource } from './resources/stock-movements.js';
import { createCountriesResource } from './resources/countries.js';
import type { TokenProvider } from './token-provider.js';
import type { ApiConfig } from '@effigenix/config';
@@ -186,6 +193,7 @@ export function createEffigenixClient(
batches: createBatchesResource(axiosClient),
productionOrders: createProductionOrdersResource(axiosClient),
stocks: createStocksResource(axiosClient),
+ stockMovements: createStockMovementsResource(axiosClient),
countries: createCountriesResource(axiosClient),
};
}
diff --git a/frontend/packages/api-client/src/resources/production-orders.ts b/frontend/packages/api-client/src/resources/production-orders.ts
index 10fa1bb..6343f76 100644
--- a/frontend/packages/api-client/src/resources/production-orders.ts
+++ b/frontend/packages/api-client/src/resources/production-orders.ts
@@ -1,7 +1,7 @@
/** Production Orders resource – Production BC. */
import type { AxiosInstance } from 'axios';
-import type { ProductionOrderDTO, CreateProductionOrderRequest } from '@effigenix/types';
+import type { ProductionOrderDTO, CreateProductionOrderRequest, StartProductionOrderRequest } from '@effigenix/types';
export type Priority = 'LOW' | 'NORMAL' | 'HIGH' | 'URGENT';
@@ -12,12 +12,32 @@ export const PRIORITY_LABELS: Record = {
URGENT: 'Dringend',
};
-export type { ProductionOrderDTO, CreateProductionOrderRequest };
+export type ProductionOrderStatus = 'CREATED' | 'RELEASED' | 'IN_PRODUCTION' | 'COMPLETED' | 'CANCELLED';
+
+export const PRODUCTION_ORDER_STATUS_LABELS: Record = {
+ CREATED: 'Erstellt',
+ RELEASED: 'Freigegeben',
+ IN_PRODUCTION: 'In Produktion',
+ COMPLETED: 'Abgeschlossen',
+ CANCELLED: 'Storniert',
+};
+
+export type { ProductionOrderDTO, CreateProductionOrderRequest, StartProductionOrderRequest };
const BASE = '/api/production/production-orders';
export function createProductionOrdersResource(client: AxiosInstance) {
return {
+ async list(): Promise {
+ const res = await client.get(BASE);
+ return res.data;
+ },
+
+ async getById(id: string): Promise {
+ const res = await client.get(`${BASE}/${id}`);
+ return res.data;
+ },
+
async create(request: CreateProductionOrderRequest): Promise {
const res = await client.post(BASE, request);
return res.data;
@@ -27,6 +47,11 @@ export function createProductionOrdersResource(client: AxiosInstance) {
const res = await client.post(`${BASE}/${id}/release`);
return res.data;
},
+
+ async start(id: string, request: StartProductionOrderRequest): Promise {
+ const res = await client.post(`${BASE}/${id}/start`, request);
+ return res.data;
+ },
};
}
diff --git a/frontend/packages/api-client/src/resources/stock-movements.ts b/frontend/packages/api-client/src/resources/stock-movements.ts
new file mode 100644
index 0000000..8274490
--- /dev/null
+++ b/frontend/packages/api-client/src/resources/stock-movements.ts
@@ -0,0 +1,73 @@
+/** Stock Movements resource – Inventory BC. */
+
+import type { AxiosInstance } from 'axios';
+import type { StockMovementDTO, RecordStockMovementRequest } from '@effigenix/types';
+
+export type MovementType =
+ | 'GOODS_RECEIPT'
+ | 'PRODUCTION_OUTPUT'
+ | 'PRODUCTION_CONSUMPTION'
+ | 'SALE'
+ | 'RETURN'
+ | 'WASTE'
+ | 'ADJUSTMENT'
+ | 'INTER_BRANCH_TRANSFER';
+
+export const MOVEMENT_TYPE_LABELS: Record = {
+ GOODS_RECEIPT: 'Wareneingang',
+ PRODUCTION_OUTPUT: 'Produktionsausstoß',
+ PRODUCTION_CONSUMPTION: 'Produktionsverbrauch',
+ SALE: 'Verkauf',
+ RETURN: 'Retoure',
+ WASTE: 'Ausschuss',
+ ADJUSTMENT: 'Korrektur',
+ INTER_BRANCH_TRANSFER: 'Filialumlagerung',
+};
+
+export type MovementDirection = 'IN' | 'OUT';
+
+export const MOVEMENT_DIRECTION_LABELS: Record = {
+ IN: 'Eingang',
+ OUT: 'Ausgang',
+};
+
+export type { StockMovementDTO, RecordStockMovementRequest };
+
+export interface StockMovementFilter {
+ stockId?: string;
+ articleId?: string;
+ movementType?: string;
+ batchReference?: string;
+ from?: string;
+ to?: string;
+}
+
+const BASE = '/api/inventory/stock-movements';
+
+export function createStockMovementsResource(client: AxiosInstance) {
+ return {
+ async list(filter?: StockMovementFilter): Promise {
+ const params: Record = {};
+ if (filter?.stockId) params.stockId = filter.stockId;
+ if (filter?.articleId) params.articleId = filter.articleId;
+ if (filter?.movementType) params.movementType = filter.movementType;
+ if (filter?.batchReference) params.batchReference = filter.batchReference;
+ if (filter?.from) params.from = filter.from;
+ if (filter?.to) params.to = filter.to;
+ const res = await client.get(BASE, { params });
+ return res.data;
+ },
+
+ async getById(id: string): Promise {
+ const res = await client.get(`${BASE}/${id}`);
+ return res.data;
+ },
+
+ async record(request: RecordStockMovementRequest): Promise {
+ const res = await client.post(BASE, request);
+ return res.data;
+ },
+ };
+}
+
+export type StockMovementsResource = ReturnType;
diff --git a/frontend/packages/types/src/generated/api.ts b/frontend/packages/types/src/generated/api.ts
index 77691fb..e2b6566 100644
--- a/frontend/packages/types/src/generated/api.ts
+++ b/frontend/packages/types/src/generated/api.ts
@@ -452,6 +452,22 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/api/production/production-orders/{id}/start": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post: operations["startProductionOrder"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/production/production-orders/{id}/release": {
parameters: {
query?: never;
@@ -660,6 +676,26 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/api/inventory/stock-movements": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * List stock movements
+ * @description Filter priority (only one filter applied): stockId > articleId > batchReference > movementType > from/to
+ */
+ get: operations["listMovements"];
+ put?: never;
+ post: operations["recordMovement"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/customers": {
parameters: {
query?: never;
@@ -996,6 +1032,38 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/api/inventory/stock-movements/{id}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: operations["getMovement"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/api/countries": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: operations["search"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/users/{id}/roles/{roleName}": {
parameters: {
query?: never;
@@ -1553,6 +1621,7 @@ export interface components {
id?: string;
recipeId?: string;
status?: string;
+ batchId?: string;
plannedQuantity?: string;
plannedQuantityUnit?: string;
/** Format: date */
@@ -1564,6 +1633,9 @@ export interface components {
/** Format: date-time */
updatedAt?: string;
};
+ StartProductionOrderRequest: {
+ batchId: string;
+ };
PlanBatchRequest: {
recipeId: string;
plannedQuantity: string;
@@ -1668,6 +1740,36 @@ export interface components {
BlockStockBatchRequest: {
reason: string;
};
+ RecordStockMovementRequest: {
+ stockId: string;
+ articleId: string;
+ stockBatchId: string;
+ batchId: string;
+ batchType: string;
+ movementType: string;
+ direction?: string;
+ quantityAmount: string;
+ quantityUnit: string;
+ reason?: string;
+ referenceDocumentId?: string;
+ };
+ StockMovementResponse: {
+ id: string;
+ stockId: string;
+ articleId: string;
+ stockBatchId: string;
+ batchId: string;
+ batchType: string;
+ movementType: string;
+ direction: string;
+ quantityAmount: number;
+ quantityUnit: string;
+ reason?: string | null;
+ referenceDocumentId?: string | null;
+ performedBy: string;
+ /** Format: date-time */
+ performedAt: string;
+ };
CreateCustomerRequest: {
name: string;
/** @enum {string} */
@@ -1802,6 +1904,10 @@ export interface components {
/** Format: date-time */
updatedAt?: string;
};
+ CountryResponse: {
+ code?: string;
+ name?: string;
+ };
RemoveCertificateRequest: {
certificateType: string;
issuer?: string;
@@ -2891,6 +2997,32 @@ export interface operations {
};
};
};
+ startProductionOrder: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["StartProductionOrderRequest"];
+ };
+ };
+ responses: {
+ /** @description OK */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "*/*": components["schemas"]["ProductionOrderResponse"];
+ };
+ };
+ };
+ };
releaseProductionOrder: {
parameters: {
query?: never;
@@ -3278,6 +3410,57 @@ export interface operations {
};
};
};
+ listMovements: {
+ parameters: {
+ query?: {
+ stockId?: string;
+ articleId?: string;
+ movementType?: string;
+ batchReference?: string;
+ from?: string;
+ to?: string;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description OK */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "*/*": components["schemas"]["StockMovementResponse"][];
+ };
+ };
+ };
+ };
+ recordMovement: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["RecordStockMovementRequest"];
+ };
+ };
+ responses: {
+ /** @description OK */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "*/*": components["schemas"]["StockMovementResponse"];
+ };
+ };
+ };
+ };
listCustomers: {
parameters: {
query?: {
@@ -3850,6 +4033,50 @@ export interface operations {
};
};
};
+ getMovement: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description OK */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "*/*": components["schemas"]["StockMovementResponse"];
+ };
+ };
+ };
+ };
+ search: {
+ parameters: {
+ query?: {
+ q?: string;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description OK */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "*/*": components["schemas"]["CountryResponse"][];
+ };
+ };
+ };
+ };
removeRole: {
parameters: {
query?: never;
diff --git a/frontend/packages/types/src/inventory.ts b/frontend/packages/types/src/inventory.ts
index fe2a182..ef8d45a 100644
--- a/frontend/packages/types/src/inventory.ts
+++ b/frontend/packages/types/src/inventory.ts
@@ -30,3 +30,7 @@ export type BlockStockBatchRequest = components['schemas']['BlockStockBatchReque
export type ReservationDTO = components['schemas']['ReservationResponse'];
export type StockBatchAllocationDTO = components['schemas']['StockBatchAllocationResponse'];
export type ReserveStockRequest = components['schemas']['ReserveStockRequest'];
+
+// Stock Movement types
+export type StockMovementDTO = components['schemas']['StockMovementResponse'];
+export type RecordStockMovementRequest = components['schemas']['RecordStockMovementRequest'];
diff --git a/frontend/packages/types/src/production.ts b/frontend/packages/types/src/production.ts
index 39a1ce5..84f1ce1 100644
--- a/frontend/packages/types/src/production.ts
+++ b/frontend/packages/types/src/production.ts
@@ -30,3 +30,4 @@ export type CancelBatchRequest = components['schemas']['CancelBatchRequest'];
// Production Order types
export type ProductionOrderDTO = components['schemas']['ProductionOrderResponse'];
export type CreateProductionOrderRequest = components['schemas']['CreateProductionOrderRequest'];
+export type StartProductionOrderRequest = components['schemas']['StartProductionOrderRequest'];