mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 13:59:36 +01:00
feat(tui): TUI für Produktionsauftrag-Freigabe, Bestandsreservierung und Reservierungs-Freigabe
- ProductionOrderCreateScreen: Nach Anlage Freigabe per [F] mit Statusanzeige - StockDetailScreen: Reservierungen-Tabelle, Menü für Reservieren/Freigeben - ReserveStockScreen: Neues Formular (Referenztyp, Referenz-ID, Menge, Einheit, Priorität) - API-Client: release(), reserveStock(), releaseReservation() Methoden - Hooks: releaseProductionOrder(), reserveStock(), releaseReservation() - Types: ReservationDTO, StockBatchAllocationDTO, ReserveStockRequest exportiert - DB: Migration 027 erweitert chk_production_order_status um RELEASED
This commit is contained in:
parent
fb8387c10e
commit
376557925a
15 changed files with 585 additions and 13 deletions
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
|
||||||
|
|
||||||
|
<changeSet id="027-add-released-status-to-production-orders" author="effigenix">
|
||||||
|
<sql>ALTER TABLE production_orders DROP CONSTRAINT chk_production_order_status;</sql>
|
||||||
|
<sql>ALTER TABLE production_orders ADD CONSTRAINT chk_production_order_status CHECK (status IN ('PLANNED', 'RELEASED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED'));</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
|
|
@ -31,5 +31,6 @@
|
||||||
<include file="db/changelog/changes/024-create-production-orders-table.xml"/>
|
<include file="db/changelog/changes/024-create-production-orders-table.xml"/>
|
||||||
<include file="db/changelog/changes/025-seed-production-order-permissions.xml"/>
|
<include file="db/changelog/changes/025-seed-production-order-permissions.xml"/>
|
||||||
<include file="db/changelog/changes/026-create-reservations-schema.xml"/>
|
<include file="db/changelog/changes/026-create-reservations-schema.xml"/>
|
||||||
|
<include file="db/changelog/changes/027-add-released-status-to-production-orders.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ import { ProductionOrderCreateScreen } from './components/production/ProductionO
|
||||||
import { StockListScreen } from './components/inventory/StockListScreen.js';
|
import { StockListScreen } from './components/inventory/StockListScreen.js';
|
||||||
import { StockDetailScreen } from './components/inventory/StockDetailScreen.js';
|
import { StockDetailScreen } from './components/inventory/StockDetailScreen.js';
|
||||||
import { StockCreateScreen } from './components/inventory/StockCreateScreen.js';
|
import { StockCreateScreen } from './components/inventory/StockCreateScreen.js';
|
||||||
|
import { ReserveStockScreen } from './components/inventory/ReserveStockScreen.js';
|
||||||
|
|
||||||
function ScreenRouter() {
|
function ScreenRouter() {
|
||||||
const { isAuthenticated, loading } = useAuth();
|
const { isAuthenticated, loading } = useAuth();
|
||||||
|
|
@ -122,6 +123,7 @@ function ScreenRouter() {
|
||||||
{current === 'stock-list' && <StockListScreen />}
|
{current === 'stock-list' && <StockListScreen />}
|
||||||
{current === 'stock-detail' && <StockDetailScreen />}
|
{current === 'stock-detail' && <StockDetailScreen />}
|
||||||
{current === 'stock-create' && <StockCreateScreen />}
|
{current === 'stock-create' && <StockCreateScreen />}
|
||||||
|
{current === 'stock-reserve' && <ReserveStockScreen />}
|
||||||
{/* Produktion */}
|
{/* Produktion */}
|
||||||
{current === 'production-menu' && <ProductionMenu />}
|
{current === 'production-menu' && <ProductionMenu />}
|
||||||
{current === 'recipe-list' && <RecipeListScreen />}
|
{current === 'recipe-list' && <RecipeListScreen />}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Box, Text, useInput } from 'ink';
|
||||||
|
import { useNavigation } from '../../state/navigation-context.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 {
|
||||||
|
UOM_VALUES,
|
||||||
|
UOM_LABELS,
|
||||||
|
REFERENCE_TYPE_LABELS,
|
||||||
|
RESERVATION_PRIORITY_LABELS,
|
||||||
|
} from '@effigenix/api-client';
|
||||||
|
import type { UoM, ReferenceType, ReservationPriority } from '@effigenix/api-client';
|
||||||
|
|
||||||
|
type Field = 'referenceType' | 'referenceId' | 'quantity' | 'unit' | 'priority';
|
||||||
|
const FIELDS: Field[] = ['referenceType', 'referenceId', 'quantity', 'unit', 'priority'];
|
||||||
|
|
||||||
|
const FIELD_LABELS: Record<Field, string> = {
|
||||||
|
referenceType: 'Referenztyp (←→ wechseln)',
|
||||||
|
referenceId: 'Referenz-ID *',
|
||||||
|
quantity: 'Menge *',
|
||||||
|
unit: 'Mengeneinheit * (←→ wechseln)',
|
||||||
|
priority: 'Priorität (←→ wechseln)',
|
||||||
|
};
|
||||||
|
|
||||||
|
const REFERENCE_TYPE_VALUES: ReferenceType[] = ['PRODUCTION_ORDER', 'SALES_ORDER', 'TRANSFER'];
|
||||||
|
const PRIORITY_VALUES: ReservationPriority[] = ['LOW', 'NORMAL', 'HIGH', 'URGENT'];
|
||||||
|
|
||||||
|
export function ReserveStockScreen() {
|
||||||
|
const { params, back } = useNavigation();
|
||||||
|
const { reserveStock, loading, error, clearError } = useStocks();
|
||||||
|
|
||||||
|
const stockId = params.stockId ?? '';
|
||||||
|
|
||||||
|
const [refTypeIdx, setRefTypeIdx] = useState(0);
|
||||||
|
const [referenceId, setReferenceId] = useState('');
|
||||||
|
const [quantity, setQuantity] = useState('');
|
||||||
|
const [uomIdx, setUomIdx] = useState(0);
|
||||||
|
const [priorityIdx, setPriorityIdx] = useState(1); // NORMAL default
|
||||||
|
const [activeField, setActiveField] = useState<Field>('referenceType');
|
||||||
|
const [fieldErrors, setFieldErrors] = useState<Partial<Record<Field, string>>>({});
|
||||||
|
const [success, setSuccess] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const errors: Partial<Record<Field, string>> = {};
|
||||||
|
if (!referenceId.trim()) errors.referenceId = 'Referenz-ID ist erforderlich.';
|
||||||
|
if (!quantity.trim()) errors.quantity = 'Menge ist erforderlich.';
|
||||||
|
setFieldErrors(errors);
|
||||||
|
if (Object.keys(errors).length > 0) return;
|
||||||
|
|
||||||
|
const result = await reserveStock(stockId, {
|
||||||
|
referenceType: REFERENCE_TYPE_VALUES[refTypeIdx] as string,
|
||||||
|
referenceId: referenceId.trim(),
|
||||||
|
quantityAmount: quantity.trim(),
|
||||||
|
quantityUnit: UOM_VALUES[uomIdx] as string,
|
||||||
|
priority: PRIORITY_VALUES[priorityIdx] as string,
|
||||||
|
});
|
||||||
|
if (result) setSuccess(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFieldSubmit = (field: Field) => (_value: string) => {
|
||||||
|
const idx = FIELDS.indexOf(field);
|
||||||
|
if (idx < FIELDS.length - 1) {
|
||||||
|
setActiveField(FIELDS[idx + 1] ?? field);
|
||||||
|
} else {
|
||||||
|
void handleSubmit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useInput((_input, key) => {
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
if (key.return || key.escape) back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeField === 'referenceType') {
|
||||||
|
if (key.leftArrow || key.rightArrow) {
|
||||||
|
const dir = key.rightArrow ? 1 : -1;
|
||||||
|
setRefTypeIdx((i) => (i + dir + REFERENCE_TYPE_VALUES.length) % REFERENCE_TYPE_VALUES.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key.return || key.tab) {
|
||||||
|
setActiveField('referenceId');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key.escape) { back(); 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) {
|
||||||
|
handleFieldSubmit('unit')('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeField === 'priority') {
|
||||||
|
if (key.leftArrow || key.rightArrow) {
|
||||||
|
const dir = key.rightArrow ? 1 : -1;
|
||||||
|
setPriorityIdx((i) => (i + dir + PRIORITY_VALUES.length) % PRIORITY_VALUES.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key.return) {
|
||||||
|
void handleSubmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.tab || key.downArrow) {
|
||||||
|
setActiveField((f) => {
|
||||||
|
const idx = FIELDS.indexOf(f);
|
||||||
|
return FIELDS[(idx + 1) % FIELDS.length] ?? f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (key.upArrow) {
|
||||||
|
setActiveField((f) => {
|
||||||
|
const idx = FIELDS.indexOf(f);
|
||||||
|
return FIELDS[(idx - 1 + FIELDS.length) % FIELDS.length] ?? f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (key.escape) back();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" alignItems="center" paddingY={2}>
|
||||||
|
<LoadingSpinner label="Bestand wird reserviert..." />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" gap={1} paddingY={1}>
|
||||||
|
<Text color="green" bold>Bestand erfolgreich reserviert!</Text>
|
||||||
|
<Text color="gray" dimColor>Enter/Escape zum Zurückkehren</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const refTypeLabel = REFERENCE_TYPE_LABELS[REFERENCE_TYPE_VALUES[refTypeIdx] as ReferenceType] ?? REFERENCE_TYPE_VALUES[refTypeIdx];
|
||||||
|
const uomLabel = UOM_LABELS[UOM_VALUES[uomIdx] as UoM] ?? UOM_VALUES[uomIdx];
|
||||||
|
const priorityLabel = RESERVATION_PRIORITY_LABELS[PRIORITY_VALUES[priorityIdx] as ReservationPriority] ?? PRIORITY_VALUES[priorityIdx];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" gap={1}>
|
||||||
|
<Text color="cyan" bold>Bestand reservieren</Text>
|
||||||
|
{error && <ErrorDisplay message={error} onDismiss={clearError} />}
|
||||||
|
|
||||||
|
<Box flexDirection="column" gap={1} width={60}>
|
||||||
|
{/* Reference Type */}
|
||||||
|
<Box flexDirection="column">
|
||||||
|
<Text color={activeField === 'referenceType' ? 'cyan' : 'gray'}>
|
||||||
|
{FIELD_LABELS.referenceType}: <Text bold color="white">{activeField === 'referenceType' ? `< ${refTypeLabel} >` : refTypeLabel}</Text>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Reference ID */}
|
||||||
|
<FormInput
|
||||||
|
label={FIELD_LABELS.referenceId}
|
||||||
|
value={referenceId}
|
||||||
|
onChange={setReferenceId}
|
||||||
|
onSubmit={handleFieldSubmit('referenceId')}
|
||||||
|
focus={activeField === 'referenceId'}
|
||||||
|
{...(fieldErrors.referenceId ? { error: fieldErrors.referenceId } : {})}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Quantity */}
|
||||||
|
<FormInput
|
||||||
|
label={FIELD_LABELS.quantity}
|
||||||
|
value={quantity}
|
||||||
|
onChange={setQuantity}
|
||||||
|
onSubmit={handleFieldSubmit('quantity')}
|
||||||
|
focus={activeField === 'quantity'}
|
||||||
|
{...(fieldErrors.quantity ? { error: fieldErrors.quantity } : {})}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Unit */}
|
||||||
|
<Box flexDirection="column">
|
||||||
|
<Text color={activeField === 'unit' ? 'cyan' : 'gray'}>
|
||||||
|
{FIELD_LABELS.unit}: <Text bold color="white">{activeField === 'unit' ? `< ${uomLabel} >` : uomLabel}</Text>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Priority */}
|
||||||
|
<Box flexDirection="column">
|
||||||
|
<Text color={activeField === 'priority' ? 'cyan' : 'gray'}>
|
||||||
|
{FIELD_LABELS.priority}: <Text bold color="white">{activeField === 'priority' ? `< ${priorityLabel} >` : priorityLabel}</Text>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box marginTop={1}>
|
||||||
|
<Text color="gray" dimColor>
|
||||||
|
Tab/↑↓ Feld wechseln · ←→ Typ/Einheit/Priorität · Enter bestätigen/speichern · Escape Abbrechen
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -6,10 +6,10 @@ import { useStocks } from '../../hooks/useStocks.js';
|
||||||
import { LoadingSpinner } from '../shared/LoadingSpinner.js';
|
import { LoadingSpinner } from '../shared/LoadingSpinner.js';
|
||||||
import { ErrorDisplay } from '../shared/ErrorDisplay.js';
|
import { ErrorDisplay } from '../shared/ErrorDisplay.js';
|
||||||
import { SuccessDisplay } from '../shared/SuccessDisplay.js';
|
import { SuccessDisplay } from '../shared/SuccessDisplay.js';
|
||||||
import { STOCK_BATCH_STATUS_LABELS } from '@effigenix/api-client';
|
import { STOCK_BATCH_STATUS_LABELS, REFERENCE_TYPE_LABELS, RESERVATION_PRIORITY_LABELS } from '@effigenix/api-client';
|
||||||
import type { StockBatchStatus } from '@effigenix/api-client';
|
import type { StockBatchStatus, ReferenceType, ReservationPriority } from '@effigenix/api-client';
|
||||||
|
|
||||||
type Mode = 'view' | 'menu' | 'batch-actions' | 'block-reason' | 'remove-amount' | 'remove-unit';
|
type Mode = 'view' | 'menu' | 'batch-actions' | 'block-reason' | 'remove-amount' | 'remove-unit' | 'reservation-actions' | 'confirm-release-reservation';
|
||||||
|
|
||||||
const BATCH_STATUS_COLORS: Record<string, string> = {
|
const BATCH_STATUS_COLORS: Record<string, string> = {
|
||||||
AVAILABLE: 'green',
|
AVAILABLE: 'green',
|
||||||
|
|
@ -19,8 +19,8 @@ const BATCH_STATUS_COLORS: Record<string, string> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function StockDetailScreen() {
|
export function StockDetailScreen() {
|
||||||
const { params, back } = useNavigation();
|
const { params, back, navigate } = useNavigation();
|
||||||
const { stock, loading, error, fetchStock, blockBatch, unblockBatch, removeBatch, clearError } = useStocks();
|
const { stock, loading, error, fetchStock, blockBatch, unblockBatch, removeBatch, releaseReservation, clearError } = useStocks();
|
||||||
const [mode, setMode] = useState<Mode>('view');
|
const [mode, setMode] = useState<Mode>('view');
|
||||||
const [menuIndex, setMenuIndex] = useState(0);
|
const [menuIndex, setMenuIndex] = useState(0);
|
||||||
const [batchIndex, setBatchIndex] = useState(0);
|
const [batchIndex, setBatchIndex] = useState(0);
|
||||||
|
|
@ -28,6 +28,7 @@ export function StockDetailScreen() {
|
||||||
const [blockReason, setBlockReason] = useState('');
|
const [blockReason, setBlockReason] = useState('');
|
||||||
const [removeAmount, setRemoveAmount] = useState('');
|
const [removeAmount, setRemoveAmount] = useState('');
|
||||||
const [removeUnit, setRemoveUnit] = useState('');
|
const [removeUnit, setRemoveUnit] = useState('');
|
||||||
|
const [reservationIndex, setReservationIndex] = useState(0);
|
||||||
const [success, setSuccess] = useState<string | null>(null);
|
const [success, setSuccess] = useState<string | null>(null);
|
||||||
|
|
||||||
const stockId = params.stockId ?? '';
|
const stockId = params.stockId ?? '';
|
||||||
|
|
@ -38,6 +39,8 @@ export function StockDetailScreen() {
|
||||||
|
|
||||||
const batches = stock?.batches ?? [];
|
const batches = stock?.batches ?? [];
|
||||||
const selectedBatch = batches[batchIndex];
|
const selectedBatch = batches[batchIndex];
|
||||||
|
const reservations = stock?.reservations ?? [];
|
||||||
|
const selectedReservation = reservations[reservationIndex];
|
||||||
|
|
||||||
const getBatchActions = () => {
|
const getBatchActions = () => {
|
||||||
if (!selectedBatch) return [];
|
if (!selectedBatch) return [];
|
||||||
|
|
@ -104,8 +107,20 @@ export function StockDetailScreen() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReleaseReservation = async () => {
|
||||||
|
if (!selectedReservation?.id) return;
|
||||||
|
const result = await releaseReservation(stockId, selectedReservation.id);
|
||||||
|
if (result) {
|
||||||
|
setSuccess('Reservierung freigegeben.');
|
||||||
|
setMode('view');
|
||||||
|
setReservationIndex(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const MENU_ITEMS = [
|
const MENU_ITEMS = [
|
||||||
{ label: 'Chargen verwalten', action: 'batches' },
|
{ label: 'Chargen verwalten', action: 'batches' },
|
||||||
|
{ label: 'Bestand reservieren', action: 'reserve' },
|
||||||
|
...(reservations.length > 0 ? [{ label: 'Reservierung freigeben', action: 'release-reservation' }] : []),
|
||||||
];
|
];
|
||||||
|
|
||||||
useInput((input, key) => {
|
useInput((input, key) => {
|
||||||
|
|
@ -126,6 +141,24 @@ export function StockDetailScreen() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode === 'confirm-release-reservation') {
|
||||||
|
if (input.toLowerCase() === 'j') {
|
||||||
|
void handleReleaseReservation();
|
||||||
|
}
|
||||||
|
if (input.toLowerCase() === 'n' || key.escape) setMode('reservation-actions');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'reservation-actions') {
|
||||||
|
if (key.upArrow) setReservationIndex((i) => Math.max(0, i - 1));
|
||||||
|
if (key.downArrow) setReservationIndex((i) => Math.min(reservations.length - 1, i + 1));
|
||||||
|
if (key.return && selectedReservation) {
|
||||||
|
setMode('confirm-release-reservation');
|
||||||
|
}
|
||||||
|
if (key.escape) setMode('view');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode === 'batch-actions') {
|
if (mode === 'batch-actions') {
|
||||||
if (key.upArrow) setBatchActionIndex((i) => Math.max(0, i - 1));
|
if (key.upArrow) setBatchActionIndex((i) => Math.max(0, i - 1));
|
||||||
if (key.downArrow) setBatchActionIndex((i) => Math.min(batchActions.length - 1, i + 1));
|
if (key.downArrow) setBatchActionIndex((i) => Math.min(batchActions.length - 1, i + 1));
|
||||||
|
|
@ -142,10 +175,16 @@ export function StockDetailScreen() {
|
||||||
if (key.upArrow) setMenuIndex((i) => Math.max(0, i - 1));
|
if (key.upArrow) setMenuIndex((i) => Math.max(0, i - 1));
|
||||||
if (key.downArrow) setMenuIndex((i) => Math.min(MENU_ITEMS.length - 1, i + 1));
|
if (key.downArrow) setMenuIndex((i) => Math.min(MENU_ITEMS.length - 1, i + 1));
|
||||||
if (key.return && MENU_ITEMS[menuIndex]) {
|
if (key.return && MENU_ITEMS[menuIndex]) {
|
||||||
if (MENU_ITEMS[menuIndex].action === 'batches' && batches.length > 0) {
|
const action = MENU_ITEMS[menuIndex].action;
|
||||||
|
if (action === 'batches' && batches.length > 0) {
|
||||||
setMode('batch-actions');
|
setMode('batch-actions');
|
||||||
setBatchIndex(0);
|
setBatchIndex(0);
|
||||||
setBatchActionIndex(0);
|
setBatchActionIndex(0);
|
||||||
|
} else if (action === 'reserve') {
|
||||||
|
navigate('stock-reserve', { stockId });
|
||||||
|
} else if (action === 'release-reservation') {
|
||||||
|
setMode('reservation-actions');
|
||||||
|
setReservationIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (key.escape) setMode('view');
|
if (key.escape) setMode('view');
|
||||||
|
|
@ -228,6 +267,37 @@ export function StockDetailScreen() {
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Reservations table */}
|
||||||
|
{reservations.length > 0 && (
|
||||||
|
<Box flexDirection="column" borderStyle="round" borderColor="gray" paddingX={1}>
|
||||||
|
<Text color="cyan" bold>Reservierungen ({reservations.length})</Text>
|
||||||
|
<Box paddingX={1}>
|
||||||
|
<Text color="gray" bold>{' Typ'.padEnd(22)}</Text>
|
||||||
|
<Text color="gray" bold>{'Referenz'.padEnd(18)}</Text>
|
||||||
|
<Text color="gray" bold>{'Menge'.padEnd(14)}</Text>
|
||||||
|
<Text color="gray" bold>{'Priorität'.padEnd(12)}</Text>
|
||||||
|
<Text color="gray" bold>Reserviert am</Text>
|
||||||
|
</Box>
|
||||||
|
{reservations.map((r, i) => {
|
||||||
|
const isSelected = mode === 'reservation-actions' && i === reservationIndex;
|
||||||
|
const textColor = isSelected ? 'cyan' : 'white';
|
||||||
|
const refLabel = REFERENCE_TYPE_LABELS[(r.referenceType ?? '') as ReferenceType] ?? r.referenceType;
|
||||||
|
const prioLabel = RESERVATION_PRIORITY_LABELS[(r.priority ?? '') as ReservationPriority] ?? r.priority;
|
||||||
|
const dateStr = r.reservedAt ? new Date(r.reservedAt).toLocaleDateString('de-DE') : '';
|
||||||
|
return (
|
||||||
|
<Box key={r.id} paddingX={1}>
|
||||||
|
<Text color={textColor}>{isSelected ? '▶ ' : ' '}</Text>
|
||||||
|
<Text color={textColor}>{(refLabel ?? '').padEnd(20)}</Text>
|
||||||
|
<Text color={isSelected ? 'cyan' : 'gray'}>{(r.referenceId ?? '').substring(0, 16).padEnd(16)}</Text>
|
||||||
|
<Text color={isSelected ? 'cyan' : 'gray'}>{`${r.quantityAmount ?? ''} ${r.quantityUnit ?? ''}`.padEnd(14)}</Text>
|
||||||
|
<Text color={isSelected ? 'cyan' : 'gray'}>{(prioLabel ?? '').padEnd(12)}</Text>
|
||||||
|
<Text color={isSelected ? 'cyan' : 'gray'}>{dateStr}</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Modes */}
|
{/* Modes */}
|
||||||
{mode === 'menu' && (
|
{mode === 'menu' && (
|
||||||
<Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
|
<Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
|
||||||
|
|
@ -305,6 +375,22 @@ export function StockDetailScreen() {
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{mode === 'reservation-actions' && (
|
||||||
|
<Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
|
||||||
|
<Text color="cyan" bold>Reservierung auswählen</Text>
|
||||||
|
<Text color="gray" dimColor>↑↓ Reservierung wählen · Enter freigeben · Escape zurück</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{mode === 'confirm-release-reservation' && selectedReservation && (
|
||||||
|
<Box flexDirection="column" borderStyle="round" borderColor="yellow" paddingX={1}>
|
||||||
|
<Text color="yellow" bold>Reservierung freigeben?</Text>
|
||||||
|
<Text>Referenz: {selectedReservation.referenceType} / {selectedReservation.referenceId}</Text>
|
||||||
|
<Text>Menge: {selectedReservation.quantityAmount} {selectedReservation.quantityUnit}</Text>
|
||||||
|
<Text color="gray" dimColor>[J] Ja · [N] Nein</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color="gray" dimColor>
|
<Text color="gray" dimColor>
|
||||||
[m] Aktionsmenü · Backspace Zurück
|
[m] Aktionsmenü · Backspace Zurück
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const PRIORITY_VALUES: Priority[] = ['LOW', 'NORMAL', 'HIGH', 'URGENT'];
|
||||||
|
|
||||||
export function ProductionOrderCreateScreen() {
|
export function ProductionOrderCreateScreen() {
|
||||||
const { back } = useNavigation();
|
const { back } = useNavigation();
|
||||||
const { createProductionOrder, loading, error, clearError } = useProductionOrders();
|
const { createProductionOrder, releaseProductionOrder, productionOrder, loading, error, clearError } = useProductionOrders();
|
||||||
const { recipes, fetchRecipes } = useRecipes();
|
const { recipes, fetchRecipes } = useRecipes();
|
||||||
|
|
||||||
const [quantity, setQuantity] = useState('');
|
const [quantity, setQuantity] = useState('');
|
||||||
|
|
@ -37,6 +37,7 @@ export function ProductionOrderCreateScreen() {
|
||||||
const [activeField, setActiveField] = useState<Field>('recipe');
|
const [activeField, setActiveField] = useState<Field>('recipe');
|
||||||
const [fieldErrors, setFieldErrors] = useState<Partial<Record<Field, string>>>({});
|
const [fieldErrors, setFieldErrors] = useState<Partial<Record<Field, string>>>({});
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
|
const [released, setReleased] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void fetchRecipes('ACTIVE');
|
void fetchRecipes('ACTIVE');
|
||||||
|
|
@ -74,11 +75,23 @@ export function ProductionOrderCreateScreen() {
|
||||||
useInput((_input, key) => {
|
useInput((_input, key) => {
|
||||||
if (loading) return;
|
if (loading) return;
|
||||||
|
|
||||||
if (success) {
|
if (released) {
|
||||||
if (key.return || key.escape) back();
|
if (key.return || key.escape) back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
if (_input.toLowerCase() === 'f' && productionOrder?.id) {
|
||||||
|
void (async () => {
|
||||||
|
const result = await releaseProductionOrder(productionOrder.id);
|
||||||
|
if (result) setReleased(true);
|
||||||
|
})();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key.escape) back();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (activeField === 'recipe') {
|
if (activeField === 'recipe') {
|
||||||
if (key.upArrow) setRecipeIdx((i) => Math.max(0, i - 1));
|
if (key.upArrow) setRecipeIdx((i) => Math.max(0, i - 1));
|
||||||
if (key.downArrow) setRecipeIdx((i) => Math.min(recipes.length - 1, i + 1));
|
if (key.downArrow) setRecipeIdx((i) => Math.min(recipes.length - 1, i + 1));
|
||||||
|
|
@ -134,11 +147,34 @@ export function ProductionOrderCreateScreen() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (released) {
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column" gap={1} paddingY={1}>
|
||||||
|
<Text color="green" bold>Produktionsauftrag freigegeben!</Text>
|
||||||
|
{productionOrder && (
|
||||||
|
<Box flexDirection="column" borderStyle="round" borderColor="gray" paddingX={1}>
|
||||||
|
<Box><Text color="gray">ID: </Text><Text>{productionOrder.id}</Text></Box>
|
||||||
|
<Box><Text color="gray">Status: </Text><Text color="green">{productionOrder.status}</Text></Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Text color="gray" dimColor>Enter/Escape zum Zurückkehren</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" gap={1} paddingY={1}>
|
<Box flexDirection="column" gap={1} paddingY={1}>
|
||||||
<Text color="green" bold>Produktionsauftrag erfolgreich erstellt!</Text>
|
<Text color="green" bold>Produktionsauftrag erfolgreich erstellt!</Text>
|
||||||
<Text color="gray" dimColor>Enter/Escape zum Zurückkehren</Text>
|
{productionOrder && (
|
||||||
|
<Box flexDirection="column" borderStyle="round" borderColor="gray" paddingX={1}>
|
||||||
|
<Box><Text color="gray">ID: </Text><Text>{productionOrder.id}</Text></Box>
|
||||||
|
<Box><Text color="gray">Rezept: </Text><Text>{productionOrder.recipeId}</Text></Box>
|
||||||
|
<Box><Text color="gray">Menge: </Text><Text>{productionOrder.plannedQuantity} {productionOrder.plannedQuantityUnit}</Text></Box>
|
||||||
|
<Box><Text color="gray">Status: </Text><Text>{productionOrder.status}</Text></Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Text color="gray" dimColor>[F] Freigeben · Escape Zurück</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,18 @@ export function useProductionOrders() {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const releaseProductionOrder = useCallback(async (id: string) => {
|
||||||
|
setState((s) => ({ ...s, loading: true, error: null }));
|
||||||
|
try {
|
||||||
|
const productionOrder = await client.productionOrders.release(id);
|
||||||
|
setState({ productionOrder, loading: false, error: null });
|
||||||
|
return productionOrder;
|
||||||
|
} catch (err) {
|
||||||
|
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const clearError = useCallback(() => {
|
const clearError = useCallback(() => {
|
||||||
setState((s) => ({ ...s, error: null }));
|
setState((s) => ({ ...s, error: null }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -38,6 +50,7 @@ export function useProductionOrders() {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
createProductionOrder,
|
createProductionOrder,
|
||||||
|
releaseProductionOrder,
|
||||||
clearError,
|
clearError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import type {
|
||||||
CreateStockRequest,
|
CreateStockRequest,
|
||||||
UpdateStockRequest,
|
UpdateStockRequest,
|
||||||
StockFilter,
|
StockFilter,
|
||||||
|
ReserveStockRequest,
|
||||||
} from '@effigenix/api-client';
|
} from '@effigenix/api-client';
|
||||||
import { client } from '../utils/api-client.js';
|
import { client } from '../utils/api-client.js';
|
||||||
|
|
||||||
|
|
@ -123,6 +124,32 @@ export function useStocks() {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const reserveStock = useCallback(async (stockId: string, request: ReserveStockRequest) => {
|
||||||
|
setState((s) => ({ ...s, loading: true, error: null }));
|
||||||
|
try {
|
||||||
|
const reservation = await client.stocks.reserveStock(stockId, request);
|
||||||
|
const stock = await client.stocks.getById(stockId);
|
||||||
|
setState((s) => ({ ...s, stock, loading: false, error: null }));
|
||||||
|
return reservation;
|
||||||
|
} catch (err) {
|
||||||
|
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const releaseReservation = useCallback(async (stockId: string, reservationId: string) => {
|
||||||
|
setState((s) => ({ ...s, loading: true, error: null }));
|
||||||
|
try {
|
||||||
|
await client.stocks.releaseReservation(stockId, reservationId);
|
||||||
|
const stock = await client.stocks.getById(stockId);
|
||||||
|
setState((s) => ({ ...s, stock, loading: false, error: null }));
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const clearError = useCallback(() => {
|
const clearError = useCallback(() => {
|
||||||
setState((s) => ({ ...s, error: null }));
|
setState((s) => ({ ...s, error: null }));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -137,6 +164,8 @@ export function useStocks() {
|
||||||
removeBatch,
|
removeBatch,
|
||||||
blockBatch,
|
blockBatch,
|
||||||
unblockBatch,
|
unblockBatch,
|
||||||
|
reserveStock,
|
||||||
|
releaseReservation,
|
||||||
clearError,
|
clearError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,8 @@ export type Screen =
|
||||||
| 'batch-plan'
|
| 'batch-plan'
|
||||||
| 'batch-record-consumption'
|
| 'batch-record-consumption'
|
||||||
| 'batch-complete'
|
| 'batch-complete'
|
||||||
| 'production-order-create';
|
| 'production-order-create'
|
||||||
|
| 'stock-reserve';
|
||||||
|
|
||||||
interface NavigationState {
|
interface NavigationState {
|
||||||
current: Screen;
|
current: Screen;
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -108,6 +108,9 @@ export type {
|
||||||
RemoveStockBatchRequest,
|
RemoveStockBatchRequest,
|
||||||
BlockStockBatchRequest,
|
BlockStockBatchRequest,
|
||||||
MinimumLevelDTO,
|
MinimumLevelDTO,
|
||||||
|
ReservationDTO,
|
||||||
|
StockBatchAllocationDTO,
|
||||||
|
ReserveStockRequest,
|
||||||
} from '@effigenix/types';
|
} from '@effigenix/types';
|
||||||
|
|
||||||
// Resource types (runtime, stay in resource files)
|
// Resource types (runtime, stay in resource files)
|
||||||
|
|
@ -137,8 +140,8 @@ export type { BatchesResource, BatchStatus } from './resources/batches.js';
|
||||||
export { BATCH_STATUS_LABELS } from './resources/batches.js';
|
export { BATCH_STATUS_LABELS } from './resources/batches.js';
|
||||||
export type { ProductionOrdersResource, Priority } from './resources/production-orders.js';
|
export type { ProductionOrdersResource, Priority } from './resources/production-orders.js';
|
||||||
export { PRIORITY_LABELS } from './resources/production-orders.js';
|
export { PRIORITY_LABELS } from './resources/production-orders.js';
|
||||||
export type { StocksResource, BatchType, StockBatchStatus, StockFilter } from './resources/stocks.js';
|
export type { StocksResource, BatchType, StockBatchStatus, StockFilter, ReferenceType, ReservationPriority } from './resources/stocks.js';
|
||||||
export { BATCH_TYPE_LABELS, STOCK_BATCH_STATUS_LABELS } from './resources/stocks.js';
|
export { BATCH_TYPE_LABELS, STOCK_BATCH_STATUS_LABELS, REFERENCE_TYPE_LABELS, RESERVATION_PRIORITY_LABELS } from './resources/stocks.js';
|
||||||
|
|
||||||
import { createApiClient } from './client.js';
|
import { createApiClient } from './client.js';
|
||||||
import { createAuthResource } from './resources/auth.js';
|
import { createAuthResource } from './resources/auth.js';
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@ export function createProductionOrdersResource(client: AxiosInstance) {
|
||||||
const res = await client.post<ProductionOrderDTO>(BASE, request);
|
const res = await client.post<ProductionOrderDTO>(BASE, request);
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async release(id: string): Promise<ProductionOrderDTO> {
|
||||||
|
const res = await client.post<ProductionOrderDTO>(`${BASE}/${id}/release`);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import type {
|
||||||
UpdateStockRequest,
|
UpdateStockRequest,
|
||||||
RemoveStockBatchRequest,
|
RemoveStockBatchRequest,
|
||||||
BlockStockBatchRequest,
|
BlockStockBatchRequest,
|
||||||
|
ReservationDTO,
|
||||||
|
ReserveStockRequest,
|
||||||
} from '@effigenix/types';
|
} from '@effigenix/types';
|
||||||
|
|
||||||
export type BatchType = 'PURCHASED' | 'PRODUCED';
|
export type BatchType = 'PURCHASED' | 'PRODUCED';
|
||||||
|
|
@ -28,6 +30,23 @@ export const STOCK_BATCH_STATUS_LABELS: Record<StockBatchStatus, string> = {
|
||||||
BLOCKED: 'Gesperrt',
|
BLOCKED: 'Gesperrt',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ReferenceType = 'PRODUCTION_ORDER' | 'SALES_ORDER' | 'TRANSFER';
|
||||||
|
|
||||||
|
export const REFERENCE_TYPE_LABELS: Record<ReferenceType, string> = {
|
||||||
|
PRODUCTION_ORDER: 'Produktionsauftrag',
|
||||||
|
SALES_ORDER: 'Kundenauftrag',
|
||||||
|
TRANSFER: 'Umlagerung',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReservationPriority = 'LOW' | 'NORMAL' | 'HIGH' | 'URGENT';
|
||||||
|
|
||||||
|
export const RESERVATION_PRIORITY_LABELS: Record<ReservationPriority, string> = {
|
||||||
|
LOW: 'Niedrig',
|
||||||
|
NORMAL: 'Normal',
|
||||||
|
HIGH: 'Hoch',
|
||||||
|
URGENT: 'Dringend',
|
||||||
|
};
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
StockDTO,
|
StockDTO,
|
||||||
StockBatchDTO,
|
StockBatchDTO,
|
||||||
|
|
@ -37,6 +56,8 @@ export type {
|
||||||
UpdateStockRequest,
|
UpdateStockRequest,
|
||||||
RemoveStockBatchRequest,
|
RemoveStockBatchRequest,
|
||||||
BlockStockBatchRequest,
|
BlockStockBatchRequest,
|
||||||
|
ReservationDTO,
|
||||||
|
ReserveStockRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface StockFilter {
|
export interface StockFilter {
|
||||||
|
|
@ -89,6 +110,15 @@ export function createStocksResource(client: AxiosInstance) {
|
||||||
async unblockBatch(stockId: string, batchId: string): Promise<void> {
|
async unblockBatch(stockId: string, batchId: string): Promise<void> {
|
||||||
await client.post(`${BASE}/${stockId}/batches/${batchId}/unblock`);
|
await client.post(`${BASE}/${stockId}/batches/${batchId}/unblock`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async reserveStock(stockId: string, request: ReserveStockRequest): Promise<ReservationDTO> {
|
||||||
|
const res = await client.post<ReservationDTO>(`${BASE}/${stockId}/reservations`, request);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
async releaseReservation(stockId: string, reservationId: string): Promise<void> {
|
||||||
|
await client.delete(`${BASE}/${stockId}/reservations/${reservationId}`);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,22 @@ export interface paths {
|
||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/production/production-orders/{id}/release": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post: operations["releaseProductionOrder"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/production/batches": {
|
"/api/production/batches": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -564,6 +580,22 @@ export interface paths {
|
||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/inventory/stocks/{stockId}/reservations": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post: operations["reserveStock"];
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/inventory/stocks/{stockId}/batches": {
|
"/api/inventory/stocks/{stockId}/batches": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -1016,6 +1048,22 @@ export interface paths {
|
||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/inventory/stocks/{stockId}/reservations/{reservationId}": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete: operations["releaseReservation"];
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/customers/{id}/delivery-addresses/{label}": {
|
"/api/customers/{id}/delivery-addresses/{label}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -1201,6 +1249,22 @@ export interface components {
|
||||||
amount: number;
|
amount: number;
|
||||||
unit: string;
|
unit: string;
|
||||||
} | null;
|
} | null;
|
||||||
|
ReservationResponse: {
|
||||||
|
id?: string;
|
||||||
|
referenceType?: string;
|
||||||
|
referenceId?: string;
|
||||||
|
quantityAmount?: number;
|
||||||
|
quantityUnit?: string;
|
||||||
|
priority?: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
reservedAt?: string;
|
||||||
|
allocations?: components["schemas"]["StockBatchAllocationResponse"][];
|
||||||
|
};
|
||||||
|
StockBatchAllocationResponse: {
|
||||||
|
stockBatchId?: string;
|
||||||
|
allocatedQuantityAmount?: number;
|
||||||
|
allocatedQuantityUnit?: string;
|
||||||
|
};
|
||||||
StockBatchResponse: {
|
StockBatchResponse: {
|
||||||
id?: string;
|
id?: string;
|
||||||
batchId?: string;
|
batchId?: string;
|
||||||
|
|
@ -1224,6 +1288,7 @@ export interface components {
|
||||||
totalQuantity: number;
|
totalQuantity: number;
|
||||||
quantityUnit?: string | null;
|
quantityUnit?: string | null;
|
||||||
availableQuantity: number;
|
availableQuantity: number;
|
||||||
|
reservations: components["schemas"]["ReservationResponse"][];
|
||||||
};
|
};
|
||||||
UpdateCustomerRequest: {
|
UpdateCustomerRequest: {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
@ -1582,6 +1647,13 @@ export interface components {
|
||||||
/** Format: int32 */
|
/** Format: int32 */
|
||||||
minimumShelfLifeDays?: number | null;
|
minimumShelfLifeDays?: number | null;
|
||||||
};
|
};
|
||||||
|
ReserveStockRequest: {
|
||||||
|
referenceType: string;
|
||||||
|
referenceId: string;
|
||||||
|
quantityAmount: string;
|
||||||
|
quantityUnit: string;
|
||||||
|
priority: string;
|
||||||
|
};
|
||||||
AddStockBatchRequest: {
|
AddStockBatchRequest: {
|
||||||
batchId: string;
|
batchId: string;
|
||||||
batchType: string;
|
batchType: string;
|
||||||
|
|
@ -2819,6 +2891,28 @@ export interface operations {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
releaseProductionOrder: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description OK */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"*/*": components["schemas"]["ProductionOrderResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
listBatches: {
|
listBatches: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: {
|
query?: {
|
||||||
|
|
@ -3061,6 +3155,32 @@ export interface operations {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
reserveStock: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
stockId: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["ReserveStockRequest"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"*/*": components["schemas"]["ReservationResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
addBatch: {
|
addBatch: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -3809,6 +3929,27 @@ export interface operations {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
releaseReservation: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path: {
|
||||||
|
stockId: string;
|
||||||
|
reservationId: string;
|
||||||
|
};
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description OK */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content?: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
removeDeliveryAddress: {
|
removeDeliveryAddress: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
|
||||||
|
|
@ -25,3 +25,8 @@ export type CreateStockResponse = components['schemas']['CreateStockResponse'];
|
||||||
export type UpdateStockRequest = components['schemas']['UpdateStockRequest'];
|
export type UpdateStockRequest = components['schemas']['UpdateStockRequest'];
|
||||||
export type RemoveStockBatchRequest = components['schemas']['RemoveStockBatchRequest'];
|
export type RemoveStockBatchRequest = components['schemas']['RemoveStockBatchRequest'];
|
||||||
export type BlockStockBatchRequest = components['schemas']['BlockStockBatchRequest'];
|
export type BlockStockBatchRequest = components['schemas']['BlockStockBatchRequest'];
|
||||||
|
|
||||||
|
// Reservation types
|
||||||
|
export type ReservationDTO = components['schemas']['ReservationResponse'];
|
||||||
|
export type StockBatchAllocationDTO = components['schemas']['StockBatchAllocationResponse'];
|
||||||
|
export type ReserveStockRequest = components['schemas']['ReserveStockRequest'];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue