diff --git a/frontend/apps/cli/src/App.tsx b/frontend/apps/cli/src/App.tsx
index f0d00ae..227479c 100644
--- a/frontend/apps/cli/src/App.tsx
+++ b/frontend/apps/cli/src/App.tsx
@@ -42,6 +42,7 @@ import { RecipeListScreen } from './components/production/RecipeListScreen.js';
import { RecipeCreateScreen } from './components/production/RecipeCreateScreen.js';
import { RecipeDetailScreen } from './components/production/RecipeDetailScreen.js';
import { AddProductionStepScreen } from './components/production/AddProductionStepScreen.js';
+import { AddIngredientScreen } from './components/production/AddIngredientScreen.js';
function ScreenRouter() {
const { isAuthenticated, loading } = useAuth();
@@ -109,6 +110,7 @@ function ScreenRouter() {
{current === 'recipe-create' && }
{current === 'recipe-detail' && }
{current === 'recipe-add-production-step' && }
+ {current === 'recipe-add-ingredient' && }
);
}
diff --git a/frontend/apps/cli/src/components/production/AddIngredientScreen.tsx b/frontend/apps/cli/src/components/production/AddIngredientScreen.tsx
new file mode 100644
index 0000000..71856d7
--- /dev/null
+++ b/frontend/apps/cli/src/components/production/AddIngredientScreen.tsx
@@ -0,0 +1,129 @@
+import { useState } from 'react';
+import { Box, Text, useInput } from 'ink';
+import { useNavigation } from '../../state/navigation-context.js';
+import { FormInput } from '../shared/FormInput.js';
+import { LoadingSpinner } from '../shared/LoadingSpinner.js';
+import { ErrorDisplay } from '../shared/ErrorDisplay.js';
+import { client } from '../../utils/api-client.js';
+
+type Field = 'position' | 'articleId' | 'quantity' | 'uom' | 'subRecipeId' | 'substitutable';
+const FIELDS: Field[] = ['position', 'articleId', 'quantity', 'uom', 'subRecipeId', 'substitutable'];
+
+const FIELD_LABELS: Record = {
+ position: 'Position (optional)',
+ articleId: 'Artikel-ID *',
+ quantity: 'Menge *',
+ uom: 'Einheit *',
+ subRecipeId: 'Sub-Rezept-ID (optional)',
+ substitutable: 'Austauschbar (ja/nein, optional)',
+};
+
+function errorMessage(err: unknown): string {
+ return err instanceof Error ? err.message : 'Unbekannter Fehler';
+}
+
+export function AddIngredientScreen() {
+ const { params, navigate, back } = useNavigation();
+ const recipeId = params['recipeId'] ?? '';
+
+ const [values, setValues] = useState>({
+ position: '', articleId: '', quantity: '', uom: '', subRecipeId: '', substitutable: '',
+ });
+ const [activeField, setActiveField] = useState('position');
+ const [fieldErrors, setFieldErrors] = useState>>({});
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const setField = (field: Field) => (value: string) => {
+ setValues((v) => ({ ...v, [field]: value }));
+ };
+
+ useInput((_input, key) => {
+ if (loading) 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();
+ });
+
+ const handleSubmit = async () => {
+ const errors: Partial> = {};
+ if (!values.articleId.trim()) errors.articleId = 'Artikel-ID ist erforderlich.';
+ if (!values.quantity.trim()) errors.quantity = 'Menge ist erforderlich.';
+ if (values.quantity.trim() && isNaN(Number(values.quantity))) errors.quantity = 'Muss eine Zahl sein.';
+ if (!values.uom.trim()) errors.uom = 'Einheit ist erforderlich.';
+ if (values.position.trim() && (!Number.isInteger(Number(values.position)) || Number(values.position) < 1)) {
+ errors.position = 'Muss eine positive Ganzzahl sein.';
+ }
+ if (values.substitutable.trim() && !['ja', 'nein'].includes(values.substitutable.trim().toLowerCase())) {
+ errors.substitutable = 'Muss "ja" oder "nein" sein.';
+ }
+ setFieldErrors(errors);
+ if (Object.keys(errors).length > 0) return;
+
+ setLoading(true);
+ setError(null);
+ try {
+ await client.recipes.addIngredient(recipeId, {
+ articleId: values.articleId.trim(),
+ quantity: values.quantity.trim(),
+ uom: values.uom.trim(),
+ ...(values.position.trim() ? { position: Number(values.position) } : {}),
+ ...(values.subRecipeId.trim() ? { subRecipeId: values.subRecipeId.trim() } : {}),
+ ...(values.substitutable.trim() ? { substitutable: values.substitutable.trim().toLowerCase() === 'ja' } : {}),
+ });
+ navigate('recipe-detail', { recipeId });
+ } catch (err: unknown) {
+ setError(errorMessage(err));
+ setLoading(false);
+ }
+ };
+
+ const handleFieldSubmit = (field: Field) => (_value: string) => {
+ const idx = FIELDS.indexOf(field);
+ if (idx < FIELDS.length - 1) {
+ setActiveField(FIELDS[idx + 1] ?? field);
+ } else {
+ void handleSubmit();
+ }
+ };
+
+ if (!recipeId) return ;
+ if (loading) return ;
+
+ return (
+
+ Zutat hinzufügen
+ {error && setError(null)} />}
+
+
+ {FIELDS.map((field) => (
+
+ ))}
+
+
+
+
+ Tab/↑↓ Feld wechseln · Enter auf letztem Feld speichern · Escape Abbrechen
+
+
+
+ );
+}
diff --git a/frontend/apps/cli/src/components/production/RecipeDetailScreen.tsx b/frontend/apps/cli/src/components/production/RecipeDetailScreen.tsx
index a88db83..06a16b4 100644
--- a/frontend/apps/cli/src/components/production/RecipeDetailScreen.tsx
+++ b/frontend/apps/cli/src/components/production/RecipeDetailScreen.tsx
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Box, Text, useInput } from 'ink';
-import type { RecipeDTO, RecipeType, ProductionStepDTO } from '@effigenix/api-client';
+import type { RecipeDTO, RecipeType, ProductionStepDTO, IngredientDTO } from '@effigenix/api-client';
import { RECIPE_TYPE_LABELS } from '@effigenix/api-client';
import { useNavigation } from '../../state/navigation-context.js';
import { LoadingSpinner } from '../shared/LoadingSpinner.js';
@@ -9,8 +9,8 @@ import { SuccessDisplay } from '../shared/SuccessDisplay.js';
import { ConfirmDialog } from '../shared/ConfirmDialog.js';
import { client } from '../../utils/api-client.js';
-type MenuAction = 'add-step' | 'remove-step' | 'back';
-type Mode = 'menu' | 'select-step-to-remove' | 'confirm-remove';
+type MenuAction = 'add-ingredient' | 'remove-ingredient' | 'add-step' | 'remove-step' | 'activate' | 'back';
+type Mode = 'menu' | 'select-step-to-remove' | 'confirm-remove' | 'select-ingredient-to-remove' | 'confirm-remove-ingredient' | 'confirm-activate';
function errorMessage(err: unknown): string {
return err instanceof Error ? err.message : 'Unbekannter Fehler';
@@ -29,25 +29,32 @@ export function RecipeDetailScreen() {
const [successMessage, setSuccessMessage] = useState(null);
const [selectedStepIndex, setSelectedStepIndex] = useState(0);
const [stepToRemove, setStepToRemove] = useState(null);
+ const [selectedIngredientIndex, setSelectedIngredientIndex] = useState(0);
+ const [ingredientToRemove, setIngredientToRemove] = useState(null);
const isDraft = recipe?.status === 'DRAFT';
const menuItems: { id: MenuAction; label: string }[] = [
...(isDraft ? [
+ { id: 'add-ingredient' as const, label: '[Zutat hinzufügen]' },
+ { id: 'remove-ingredient' as const, label: '[Zutat entfernen]' },
{ id: 'add-step' as const, label: '[Schritt hinzufügen]' },
{ id: 'remove-step' as const, label: '[Schritt entfernen]' },
+ { id: 'activate' as const, label: '[Rezept aktivieren]' },
] : []),
{ id: 'back' as const, label: '[Zurück]' },
];
- useEffect(() => {
- setSelectedAction(0);
- }, [isDraft]);
+ const clampedAction = Math.min(selectedAction, menuItems.length - 1);
const sortedSteps = recipe?.productionSteps
? [...recipe.productionSteps].sort((a, b) => a.stepNumber - b.stepNumber)
: [];
+ const sortedIngredients = recipe?.ingredients
+ ? [...recipe.ingredients].sort((a, b) => a.position - b.position)
+ : [];
+
const loadRecipe = useCallback(() => {
setLoading(true);
setError(null);
@@ -77,14 +84,35 @@ export function RecipeDetailScreen() {
}
if (key.escape) { setMode('menu'); setSelectedStepIndex(0); }
}
+
+ if (mode === 'select-ingredient-to-remove') {
+ if (key.upArrow) setSelectedIngredientIndex((i) => Math.max(0, i - 1));
+ if (key.downArrow) setSelectedIngredientIndex((i) => Math.min(sortedIngredients.length - 1, i + 1));
+ if (key.return && sortedIngredients.length > 0) {
+ setIngredientToRemove(sortedIngredients[selectedIngredientIndex] ?? null);
+ setMode('confirm-remove-ingredient');
+ }
+ if (key.escape) { setMode('menu'); setSelectedIngredientIndex(0); }
+ }
});
const handleAction = async () => {
if (!recipe) return;
- const item = menuItems[selectedAction];
+ const item = menuItems[clampedAction];
if (!item) return;
switch (item.id) {
+ case 'add-ingredient':
+ navigate('recipe-add-ingredient', { recipeId });
+ break;
+ case 'remove-ingredient':
+ if (sortedIngredients.length === 0) {
+ setError('Keine Zutaten vorhanden.');
+ return;
+ }
+ setSelectedIngredientIndex(0);
+ setMode('select-ingredient-to-remove');
+ break;
case 'add-step':
navigate('recipe-add-production-step', { recipeId });
break;
@@ -96,6 +124,9 @@ export function RecipeDetailScreen() {
setSelectedStepIndex(0);
setMode('select-step-to-remove');
break;
+ case 'activate':
+ setMode('confirm-activate');
+ break;
case 'back':
back();
break;
@@ -118,6 +149,36 @@ export function RecipeDetailScreen() {
setStepToRemove(null);
}, [recipe, stepToRemove]);
+ const handleRemoveIngredient = useCallback(async () => {
+ if (!recipe || !ingredientToRemove) return;
+ setMode('menu');
+ setActionLoading(true);
+ try {
+ await client.recipes.removeIngredient(recipe.id, ingredientToRemove.id);
+ const updated = await client.recipes.getById(recipe.id);
+ setRecipe(updated);
+ setSuccessMessage(`Zutat (Position ${ingredientToRemove.position}) entfernt.`);
+ } catch (err: unknown) {
+ setError(errorMessage(err));
+ }
+ setActionLoading(false);
+ setIngredientToRemove(null);
+ }, [recipe, ingredientToRemove]);
+
+ const handleActivate = useCallback(async () => {
+ if (!recipe) return;
+ setMode('menu');
+ setActionLoading(true);
+ try {
+ const updated = await client.recipes.activateRecipe(recipe.id);
+ setRecipe(updated);
+ setSuccessMessage('Rezept wurde aktiviert.');
+ } catch (err: unknown) {
+ setError(errorMessage(err));
+ }
+ setActionLoading(false);
+ }, [recipe]);
+
if (loading) return ;
if (error && !recipe) return ;
if (!recipe) return Rezept nicht gefunden.;
@@ -208,6 +269,28 @@ export function RecipeDetailScreen() {
)}
+ {mode === 'select-ingredient-to-remove' && (
+
+ Zutat zum Entfernen auswählen:
+ {sortedIngredients.map((ing, index) => (
+
+
+ {index === selectedIngredientIndex ? '▶ ' : ' '}Position {ing.position}: {ing.quantity} {ing.uom} (Artikel: {ing.articleId})
+
+
+ ))}
+ ↑↓ auswählen · Enter bestätigen · Escape abbrechen
+
+ )}
+
+ {mode === 'confirm-remove-ingredient' && ingredientToRemove && (
+ void handleRemoveIngredient()}
+ onCancel={() => { setMode('menu'); setIngredientToRemove(null); }}
+ />
+ )}
+
{mode === 'confirm-remove' && stepToRemove && (
)}
+ {mode === 'confirm-activate' && (
+ void handleActivate()}
+ onCancel={() => setMode('menu')}
+ />
+ )}
+
{mode === 'menu' && (
Aktionen:
{actionLoading && }
{!actionLoading && menuItems.map((item, index) => (
-
- {index === selectedAction ? '▶ ' : ' '}{item.label}
+
+ {index === clampedAction ? '▶ ' : ' '}{item.label}
))}
diff --git a/frontend/apps/cli/src/state/navigation-context.tsx b/frontend/apps/cli/src/state/navigation-context.tsx
index 33697a2..8b39a88 100644
--- a/frontend/apps/cli/src/state/navigation-context.tsx
+++ b/frontend/apps/cli/src/state/navigation-context.tsx
@@ -38,7 +38,8 @@ export type Screen =
| 'recipe-list'
| 'recipe-create'
| 'recipe-detail'
- | 'recipe-add-production-step';
+ | 'recipe-add-production-step'
+ | 'recipe-add-ingredient';
interface NavigationState {
current: Screen;
diff --git a/frontend/openapi.json b/frontend/openapi.json
index f896926..1facdd2 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": "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"}], "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}": {"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/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": {"200": {"description": "Role assigned successfully", "content": {"*/*": {"schema": {"$ref": "#/components/schemas/UserDTO"}}}}, "403": {"description": "Missing permission", "content": {"*/*": {"schema": {"$ref": "#/components/schemas/UserDTO"}}}}, "404": {"description": "User or role not found", "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": {"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}/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/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/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": {"200": {"description": "Roles retrieved successfully", "content": {"*/*": {"schema": {"$ref": "#/components/schemas/RoleDTO"}}}}, "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"}}}}}}, "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/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": []}]}}, "/api/inventory/stocks": {"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/StockResponse"}}}}}, "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_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}, "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": ["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"}}}, "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": ["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"}, "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"}}}, "CreateStorageLocationRequest": {"required": ["name", "storageType"], "type": "object", "properties": {"name": {"type": "string"}, "storageType": {"type": "string"}, "minTemperature": {"type": "string"}, "maxTemperature": {"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"}}}, "RemoveCertificateRequest": {"required": ["certificateType"], "type": "object", "properties": {"certificateType": {"type": "string"}, "issuer": {"type": "string"}, "validFrom": {"type": "string", "format": "date"}}}, "CreateStockRequest": {"required": ["articleId", "storageLocationId"], "type": "object", "properties": {"articleId": {"type": "string"}, "storageLocationId": {"type": "string"}, "minimumLevelAmount": {"type": "string"}, "minimumLevelUnit": {"type": "string"}, "minimumShelfLifeDays": {"type": "integer", "format": "int32"}}}, "StockResponse": {"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}}}}, "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": "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": "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"}], "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}": {"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/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": {"200": {"description": "Role assigned successfully", "content": {"*/*": {"schema": {"$ref": "#/components/schemas/UserDTO"}}}}, "403": {"description": "Missing permission", "content": {"*/*": {"schema": {"$ref": "#/components/schemas/UserDTO"}}}}, "404": {"description": "User or role not found", "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": {"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}/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/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/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": {"200": {"description": "Roles retrieved successfully", "content": {"*/*": {"schema": {"$ref": "#/components/schemas/RoleDTO"}}}}, "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"}}}}}}, "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/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": []}]}}, "/api/inventory/stocks": {"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/StockResponse"}}}}}, "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_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}, "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": ["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"}}}, "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": ["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"}, "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"}}}, "CreateStorageLocationRequest": {"required": ["name", "storageType"], "type": "object", "properties": {"name": {"type": "string"}, "storageType": {"type": "string"}, "minTemperature": {"type": "string"}, "maxTemperature": {"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"}}}, "RemoveCertificateRequest": {"required": ["certificateType"], "type": "object", "properties": {"certificateType": {"type": "string"}, "issuer": {"type": "string"}, "validFrom": {"type": "string", "format": "date"}}}, "CreateStockRequest": {"required": ["articleId", "storageLocationId"], "type": "object", "properties": {"articleId": {"type": "string"}, "storageLocationId": {"type": "string"}, "minimumLevelAmount": {"type": "string"}, "minimumLevelUnit": {"type": "string"}, "minimumShelfLifeDays": {"type": "integer", "format": "int32"}}}, "StockResponse": {"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}}}}, "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"}}}}
diff --git a/frontend/packages/api-client/src/resources/recipes.ts b/frontend/packages/api-client/src/resources/recipes.ts
index 6908cca..e5d2142 100644
--- a/frontend/packages/api-client/src/resources/recipes.ts
+++ b/frontend/packages/api-client/src/resources/recipes.ts
@@ -1,9 +1,4 @@
-/**
- * Recipes resource – Production BC.
- * Endpoints: POST /api/recipes,
- * POST /api/recipes/{id}/ingredients,
- * DELETE /api/recipes/{id}/ingredients/{ingredientId}
- */
+/** Recipes resource – Production BC. */
import type { AxiosInstance } from 'axios';
import type {
@@ -59,10 +54,8 @@ export function createRecipesResource(client: AxiosInstance) {
return res.data;
},
- async removeIngredient(recipeId: string, ingredientId: string): Promise {
+ async removeIngredient(recipeId: string, ingredientId: string): Promise {
await client.delete(`${BASE}/${recipeId}/ingredients/${ingredientId}`);
- const res = await client.get(`${BASE}/${recipeId}`);
- return res.data;
},
async addProductionStep(id: string, request: AddProductionStepRequest): Promise {
@@ -73,6 +66,11 @@ export function createRecipesResource(client: AxiosInstance) {
async removeProductionStep(id: string, stepNumber: number): Promise {
await client.delete(`${BASE}/${id}/steps/${stepNumber}`);
},
+
+ async activateRecipe(id: string): Promise {
+ const res = await client.post(`${BASE}/${id}/activate`);
+ return res.data;
+ },
};
}