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

feat(tui): Bestandsbewegungen und Produktionsaufträge anbinden

- StockMovement: API-Client, Hook, List/Detail/Record-Screens mit Typ-Filter
- ProductionOrder: list/getById/start im API-Client, List/Detail-Screens mit Freigabe- und Start-Aktion
- Inventar-Menü um Bestandsbewegungen erweitert
- Produktionsmenü zeigt jetzt Auftragsliste statt direkt Create
- OpenAPI-Typen regeneriert (StockMovementResponse, StartProductionOrderRequest, batchId in ProductionOrderResponse)
This commit is contained in:
Sebastian Frick 2026-02-25 12:36:42 +01:00
parent 0474b5fa93
commit 7d721f9ef0
18 changed files with 1279 additions and 9 deletions

View file

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

View file

@ -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<Priority, string> = {
URGENT: 'Dringend',
};
export type { ProductionOrderDTO, CreateProductionOrderRequest };
export type ProductionOrderStatus = 'CREATED' | 'RELEASED' | 'IN_PRODUCTION' | 'COMPLETED' | 'CANCELLED';
export const PRODUCTION_ORDER_STATUS_LABELS: Record<ProductionOrderStatus, string> = {
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<ProductionOrderDTO[]> {
const res = await client.get<ProductionOrderDTO[]>(BASE);
return res.data;
},
async getById(id: string): Promise<ProductionOrderDTO> {
const res = await client.get<ProductionOrderDTO>(`${BASE}/${id}`);
return res.data;
},
async create(request: CreateProductionOrderRequest): Promise<ProductionOrderDTO> {
const res = await client.post<ProductionOrderDTO>(BASE, request);
return res.data;
@ -27,6 +47,11 @@ export function createProductionOrdersResource(client: AxiosInstance) {
const res = await client.post<ProductionOrderDTO>(`${BASE}/${id}/release`);
return res.data;
},
async start(id: string, request: StartProductionOrderRequest): Promise<ProductionOrderDTO> {
const res = await client.post<ProductionOrderDTO>(`${BASE}/${id}/start`, request);
return res.data;
},
};
}

View file

@ -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<MovementType, string> = {
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<MovementDirection, string> = {
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<StockMovementDTO[]> {
const params: Record<string, string> = {};
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<StockMovementDTO[]>(BASE, { params });
return res.data;
},
async getById(id: string): Promise<StockMovementDTO> {
const res = await client.get<StockMovementDTO>(`${BASE}/${id}`);
return res.data;
},
async record(request: RecordStockMovementRequest): Promise<StockMovementDTO> {
const res = await client.post<StockMovementDTO>(BASE, request);
return res.data;
},
};
}
export type StockMovementsResource = ReturnType<typeof createStockMovementsResource>;

View file

@ -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;

View file

@ -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'];

View file

@ -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'];