Files
erp-mvp/README.md
T

924 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ERP MVP
Интегрированный monorepo ERP MVP v1 для демонстрации базового операционного цикла: справочники, закупки, продажи, складские движения, PDF-заглушки документов, dashboard и demo data.
## Структура
```text
erp-mvp/
backend/
frontend/
.env.example
docker-compose.yml
README.md
```
## Запуск
Опционально можно скопировать пример переменных окружения:
```bash
cp .env.example .env
```
Для production обязательно замените `JWT_SECRET`, пароли PostgreSQL и dev credentials.
```bash
docker compose up --build
```
После запуска:
- Frontend: http://localhost:5173
- Backend health: http://localhost:8080/api/health
- PostgreSQL: localhost:5432
## Backend
Backend использует переменные окружения для подключения к PostgreSQL:
- `POSTGRES_DB`
- `POSTGRES_USER`
- `POSTGRES_PASSWORD`
- `SPRING_DATASOURCE_URL`
- `SPRING_DATASOURCE_USERNAME`
- `SPRING_DATASOURCE_PASSWORD`
- `CORS_ALLOWED_ORIGINS`
- `JWT_SECRET`
- `JWT_EXPIRATION_MINUTES`
- `ADMIN_EMAIL`
- `ADMIN_PASSWORD`
- `DEMO_DATA_ENABLED`
По умолчанию frontend разрешен с `http://localhost:5173`.
## Backend Foundation
Backend содержит базовую инфраструктуру для дальнейшей разработки ERP-модулей:
- расширяемую package structure с `common`, `config`, `health` и заготовками `modules`;
- единый API response envelope;
- global exception handler;
- базовые custom exceptions;
- pagination DTO;
- JPA auditing foundation через `BaseEntity`;
- OpenAPI / Swagger UI;
- Flyway migrations для системной инициализации.
## Auth + Roles
Backend содержит stateless JWT authentication foundation:
- таблица `app_users`;
- роли `ADMIN`, `MANAGER`, `WAREHOUSE`, `FINANCE`;
- BCrypt password hashing;
- seed admin user через Spring Boot initializer;
- JWT Bearer auth для защищенных `/api/**` endpoint-ов;
- Swagger Bearer JWT authorization scheme.
Dev credentials по умолчанию:
- Email: `admin@erp.local`
- Password: `admin12345`
Значения можно переопределить через переменные окружения:
- `ADMIN_EMAIL`
- `ADMIN_PASSWORD`
- `JWT_SECRET`
- `JWT_EXPIRATION_MINUTES`
## API Response Format
Успешный ответ:
```json
{
"success": true,
"data": {},
"error": null,
"timestamp": "2026-05-14T10:00:00Z"
}
```
Ответ с ошибкой:
```json
{
"success": false,
"data": null,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": []
},
"timestamp": "2026-05-14T10:00:00Z"
}
```
## API URLs
- Swagger UI: http://localhost:8080/swagger-ui/index.html
- Health check: http://localhost:8080/api/health
- OpenAPI JSON: http://localhost:8080/v3/api-docs
- Frontend login: http://localhost:5173/login
## Auth Endpoints
- `POST /api/auth/login`
- `GET /api/auth/me`
- `POST /api/auth/logout`
- `GET /api/auth/protected-test`
Login:
```bash
curl -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}'
```
Protected endpoint with JWT:
```bash
TOKEN="<paste-access-token>"
curl "http://localhost:8080/api/auth/protected-test" \
-H "Authorization: Bearer $TOKEN"
```
## Catalog Module
Catalog module содержит базовые ERP-справочники без закупок, заказов, остатков и документов:
- Products / SKU
- Suppliers
- B2B Customers
- Warehouses
Backend endpoints:
- `GET /api/catalog/products`
- `GET /api/catalog/products/{id}`
- `POST /api/catalog/products`
- `PUT /api/catalog/products/{id}`
- `DELETE /api/catalog/products/{id}`
- `GET /api/catalog/suppliers`
- `GET /api/catalog/suppliers/{id}`
- `POST /api/catalog/suppliers`
- `PUT /api/catalog/suppliers/{id}`
- `DELETE /api/catalog/suppliers/{id}`
- `GET /api/catalog/customers`
- `GET /api/catalog/customers/{id}`
- `POST /api/catalog/customers`
- `PUT /api/catalog/customers/{id}`
- `DELETE /api/catalog/customers/{id}`
- `GET /api/catalog/warehouses`
- `GET /api/catalog/warehouses/{id}`
- `POST /api/catalog/warehouses`
- `PUT /api/catalog/warehouses/{id}`
- `DELETE /api/catalog/warehouses/{id}`
List endpoints support:
```text
?page=0&size=20&search=&active=true
```
Role access:
- `GET`: any authenticated user.
- `POST`, `PUT`, `DELETE`: `ADMIN` and `MANAGER`.
- `DELETE` performs soft delete by setting `active=false`.
Frontend URLs:
- Products: http://localhost:5173/catalog/products
- Suppliers: http://localhost:5173/catalog/suppliers
- Customers: http://localhost:5173/catalog/customers
- Warehouses: http://localhost:5173/catalog/warehouses
Create product example:
```bash
LOGIN_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.data.accessToken')
curl -X POST "http://localhost:8080/api/catalog/products" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"sku": "SKU-001",
"name": "Demo product",
"category": "General",
"unit": "pcs",
"barcode": "1234567890",
"description": "Demo catalog item"
}'
```
## Procurement Module
Procurement module содержит простой процесс закупок без продаж, документов и оплат:
- purchase order header;
- supplier from Catalog;
- optional receiving warehouse from Catalog;
- product items from Catalog;
- quantity, unit price, line total;
- status flow and status history.
- stock receipt when `ORDERED` moves to `RECEIVED`.
Backend endpoints:
- `GET /api/procurement/purchase-orders`
- `GET /api/procurement/purchase-orders/{id}`
- `POST /api/procurement/purchase-orders`
- `PUT /api/procurement/purchase-orders/{id}`
- `PATCH /api/procurement/purchase-orders/{id}/status`
- `GET /api/procurement/purchase-orders/{id}/status-history`
- `DELETE /api/procurement/purchase-orders/{id}`
List endpoint supports:
```text
?page=0&size=20&search=&status=DRAFT&supplierId=&fromDate=&toDate=
```
Statuses:
- `DRAFT`
- `APPROVED`
- `ORDERED`
- `RECEIVED`
- `CANCELLED`
Allowed transitions:
- `DRAFT` to `APPROVED`
- `APPROVED` to `ORDERED`
- `ORDERED` to `RECEIVED`
- `DRAFT` to `CANCELLED`
- `APPROVED` to `CANCELLED`
- `ORDERED` to `CANCELLED`
Role access:
- Read: `ADMIN`, `MANAGER`, `WAREHOUSE`, `FINANCE`.
- Create/update/cancel: `ADMIN`, `MANAGER`.
- Mark received: `ADMIN`, `MANAGER`, `WAREHOUSE`.
- `FINANCE` can only read.
Frontend URLs:
- Purchase orders: http://localhost:5173/procurement/purchase-orders
- Purchase order details: http://localhost:5173/procurement/purchase-orders/{id}
Create purchase order example:
```bash
LOGIN_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.data.accessToken')
# Create an active supplier, warehouse, and product first in Catalog, then use their ids below.
SUPPLIER_ID="<supplier-id>"
WAREHOUSE_ID="<warehouse-id>"
PRODUCT_ID="<product-id>"
CREATE_PO_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/procurement/purchase-orders" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"supplierId\": \"$SUPPLIER_ID\",
\"warehouseId\": \"$WAREHOUSE_ID\",
\"expectedDeliveryDate\": \"2026-06-01\",
\"notes\": \"Initial procurement request\",
\"items\": [
{
\"productId\": \"$PRODUCT_ID\",
\"quantity\": 10,
\"unitPrice\": 1250.00
}
]
}")
PO_ID=$(echo "$CREATE_PO_RESPONSE" | jq -r '.data.id')
curl -X PATCH "http://localhost:8080/api/procurement/purchase-orders/$PO_ID/status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"status":"APPROVED","comment":"Approved for ordering"}'
```
## Sales Module
Sales module содержит простой B2B customer order process без документов, оплат и внешних интеграций:
- customer order header;
- customer from Catalog;
- optional warehouse from Catalog;
- product items from Catalog;
- quantity, unit price, line total;
- status flow and status history.
- stock issue when `IN_PROGRESS` moves to `SHIPPED`.
Backend endpoints:
- `GET /api/sales/customer-orders`
- `GET /api/sales/customer-orders/{id}`
- `POST /api/sales/customer-orders`
- `PUT /api/sales/customer-orders/{id}`
- `PATCH /api/sales/customer-orders/{id}/status`
- `GET /api/sales/customer-orders/{id}/status-history`
- `DELETE /api/sales/customer-orders/{id}`
List endpoint supports:
```text
?page=0&size=20&search=&status=NEW&customerId=&warehouseId=&fromDate=&toDate=
```
Statuses:
- `NEW`
- `CONFIRMED`
- `IN_PROGRESS`
- `SHIPPED`
- `CLOSED`
- `CANCELLED`
Allowed transitions:
- `NEW` to `CONFIRMED`
- `CONFIRMED` to `IN_PROGRESS`
- `IN_PROGRESS` to `SHIPPED`
- `SHIPPED` to `CLOSED`
- `NEW` to `CANCELLED`
- `CONFIRMED` to `CANCELLED`
- `IN_PROGRESS` to `CANCELLED`
Role access:
- Read: `ADMIN`, `MANAGER`, `WAREHOUSE`, `FINANCE`.
- Create/update/cancel: `ADMIN`, `MANAGER`.
- Status changes: `ADMIN`, `MANAGER`.
- Warehouse status changes: `CONFIRMED` to `IN_PROGRESS`, `IN_PROGRESS` to `SHIPPED`.
- `FINANCE` can only read.
Frontend URLs:
- Customer orders: http://localhost:5173/sales/customer-orders
- Customer order details: http://localhost:5173/sales/customer-orders/{id}
Create customer order example:
```bash
LOGIN_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.data.accessToken')
# Create an active customer and product first in Catalog, then use their ids below.
# WAREHOUSE_ID is optional.
CUSTOMER_ID="<customer-id>"
PRODUCT_ID="<product-id>"
WAREHOUSE_ID="<warehouse-id>"
CREATE_ORDER_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/sales/customer-orders" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"customerId\": \"$CUSTOMER_ID\",
\"warehouseId\": \"$WAREHOUSE_ID\",
\"requestedDeliveryDate\": \"2026-06-10\",
\"notes\": \"Initial B2B order\",
\"items\": [
{
\"productId\": \"$PRODUCT_ID\",
\"quantity\": 5,
\"unitPrice\": 1800.00
}
]
}")
ORDER_ID=$(echo "$CREATE_ORDER_RESPONSE" | jq -r '.data.id')
curl -X PATCH "http://localhost:8080/api/sales/customer-orders/$ORDER_ID/status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"status":"CONFIRMED","comment":"Confirmed with customer"}'
```
## Warehouse Module
Warehouse module содержит складские остатки и историю движений без документов, оплат и внешних интеграций:
- stock balance by warehouse and product;
- stock movement history;
- inbound movement when purchase order becomes `RECEIVED`;
- outbound movement when customer order becomes `SHIPPED`;
- manual stock adjustments;
- insufficient stock check before shipping.
Backend endpoints:
- `GET /api/warehouse/stock-balances`
- `GET /api/warehouse/stock-movements`
- `POST /api/warehouse/stock-adjustments`
Stock balances list supports:
```text
?page=0&size=20&warehouseId=&productId=&search=
```
Stock movements list supports:
```text
?page=0&size=20&warehouseId=&productId=&movementType=INBOUND&sourceType=PURCHASE_ORDER&sourceId=&fromDate=&toDate=
```
Movement types:
- `INBOUND`
- `OUTBOUND`
- `ADJUSTMENT_IN`
- `ADJUSTMENT_OUT`
Source types:
- `PURCHASE_ORDER`
- `CUSTOMER_ORDER`
- `MANUAL_ADJUSTMENT`
Business rules:
- `PurchaseOrder` must have `warehouseId` before marking it as `RECEIVED`.
- `RECEIVED` creates `INBOUND` stock movement per PO item and increases stock.
- `CustomerOrder` must have `warehouseId` before marking it as `SHIPPED`.
- `SHIPPED` creates `OUTBOUND` stock movement per SO item and decreases stock.
- Shipping is blocked if stock balance does not exist or quantity is insufficient.
- Manual `ADJUSTMENT_OUT` is blocked if it would make stock negative.
Role access:
- Read stock balances/movements: `ADMIN`, `MANAGER`, `WAREHOUSE`, `FINANCE`.
- Manual stock adjustment: `ADMIN`, `WAREHOUSE`.
- PO receipt: `ADMIN`, `MANAGER`, `WAREHOUSE`.
- SO shipment: `ADMIN`, `MANAGER`, `WAREHOUSE`.
- `FINANCE` is read-only.
Frontend URLs:
- Stock balances: http://localhost:5173/warehouse/stock-balances
- Stock movements: http://localhost:5173/warehouse/stock-movements
Manual adjustment example:
```bash
LOGIN_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.data.accessToken')
WAREHOUSE_ID="<warehouse-id>"
PRODUCT_ID="<product-id>"
curl -X POST "http://localhost:8080/api/warehouse/stock-adjustments" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"warehouseId\": \"$WAREHOUSE_ID\",
\"productId\": \"$PRODUCT_ID\",
\"type\": \"ADJUSTMENT_IN\",
\"quantity\": 25.000,
\"comment\": \"Opening stock balance\"
}"
```
Mark PO as received:
```bash
curl -X PATCH "http://localhost:8080/api/procurement/purchase-orders/$PO_ID/status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"status":"RECEIVED","comment":"Received into warehouse"}'
```
Mark SO as shipped:
```bash
curl -X PATCH "http://localhost:8080/api/sales/customer-orders/$ORDER_ID/status" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"status":"SHIPPED","comment":"Shipped from warehouse"}'
```
## Documents Module
Documents module генерирует простые PDF placeholders для customer orders без email, подписей, оплат и внешних сервисов.
Document types:
- `INVOICE`
- `CONTRACT`
- `DELIVERY_NOTE`
Backend endpoints:
- `GET /api/documents`
- `GET /api/documents/{id}`
- `GET /api/documents/customer-orders/{customerOrderId}`
- `POST /api/documents/customer-orders/{customerOrderId}/generate`
- `GET /api/documents/{id}/download`
List endpoint supports:
```text
?page=0&size=20&documentType=INVOICE&sourceType=CUSTOMER_ORDER&sourceId=&status=GENERATED&fromDate=&toDate=
```
Business rules:
- Customer order must exist.
- Documents cannot be generated for `CANCELLED` customer orders.
- `DELIVERY_NOTE` can be generated only for `SHIPPED` or `CLOSED` orders.
- `INVOICE` and `CONTRACT` can be generated for any status except `CANCELLED`.
- Regenerating the same document type for the same customer order updates the existing PDF and keeps the document number.
Role access:
- Read/list/download: `ADMIN`, `MANAGER`, `WAREHOUSE`, `FINANCE`.
- Generate `INVOICE` and `CONTRACT`: `ADMIN`, `MANAGER`, `FINANCE`.
- Generate `DELIVERY_NOTE`: `ADMIN`, `MANAGER`, `WAREHOUSE`.
Frontend URLs:
- Documents: http://localhost:5173/documents
- Customer order details documents block: http://localhost:5173/sales/customer-orders/{id}
Generate invoice example:
```bash
LOGIN_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.data.accessToken')
CUSTOMER_ORDER_ID="<customer-order-id>"
GENERATE_DOCUMENT_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/documents/customer-orders/$CUSTOMER_ORDER_ID/generate" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"documentType":"INVOICE"}')
DOCUMENT_ID=$(echo "$GENERATE_DOCUMENT_RESPONSE" | jq -r '.data.id')
```
Download PDF example:
```bash
curl -L "http://localhost:8080/api/documents/$DOCUMENT_ID/download" \
-H "Authorization: Bearer $TOKEN" \
-o invoice-placeholder.pdf
```
## Dashboard / Analytics
Dashboard module содержит read-only ERP analytics для руководителя без Power BI, внешней BI-системы и изменения бизнес-логики.
Backend endpoints:
- `GET /api/dashboard/summary`
- `GET /api/dashboard/sales`
- `GET /api/dashboard/procurement`
- `GET /api/dashboard/warehouse`
- `GET /api/dashboard/recent-activities`
- `GET /api/dashboard/low-stock`
Metrics included:
- catalog counts: products, active products, suppliers, customers, warehouses;
- sales: customer order counts, active orders, total sales amount, status breakdown, daily sales, top customers;
- procurement: purchase order counts, active orders, total procurement amount, status breakdown, daily procurement, top suppliers;
- warehouse: stock items, quantity on hand, low stock, movements, stock by warehouse, movements by type;
- documents: total generated documents and documents by type;
- recent activities across customer orders, purchase orders, stock movements, and documents.
Query examples:
```text
GET /api/dashboard/sales?fromDate=2026-05-01&toDate=2026-05-31
GET /api/dashboard/procurement?fromDate=2026-05-01&toDate=2026-05-31
GET /api/dashboard/recent-activities?limit=20
GET /api/dashboard/low-stock?threshold=10&page=0&size=20
```
Role access:
- `ADMIN`
- `MANAGER`
- `WAREHOUSE`
- `FINANCE`
Frontend URL:
- Dashboard: http://localhost:5173/dashboard
Dashboard sections:
- KPI cards
- Sales by Status
- Procurement by Status
- Warehouse Overview
- Low Stock Items
- Recent Activities
Summary curl example:
```bash
LOGIN_RESPONSE=$(curl -s -X POST "http://localhost:8080/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"admin@erp.local","password":"admin12345"}')
TOKEN=$(echo "$LOGIN_RESPONSE" | jq -r '.data.accessToken')
curl "http://localhost:8080/api/dashboard/summary" \
-H "Authorization: Bearer $TOKEN"
```
Low stock curl example:
```bash
curl "http://localhost:8080/api/dashboard/low-stock?threshold=10&page=0&size=20" \
-H "Authorization: Bearer $TOKEN"
```
## Demo Data
Demo seed заполняет dev/demo окружение тестовыми данными для демонстрации ERP без ручной подготовки справочников, заказов, остатков и документов.
Seed выключен по умолчанию:
```yaml
app:
demo-data:
enabled: ${DEMO_DATA_ENABLED:false}
```
Включить можно через env:
```bash
DEMO_DATA_ENABLED=true
```
В dev `docker-compose.yml` для backend уже задано:
```yaml
DEMO_DATA_ENABLED: ${DEMO_DATA_ENABLED:-true}
```
Demo users:
- `admin@erp.local` / `admin12345` / `ADMIN`
- `manager@erp.local` / `manager12345` / `MANAGER`
- `warehouse@erp.local` / `warehouse12345` / `WAREHOUSE`
- `finance@erp.local` / `finance12345` / `FINANCE`
Seeded data:
- catalog: 8 products, 3 suppliers, 3 customers, 2 warehouses;
- procurement: 3 purchase orders, including one `RECEIVED` order that creates inbound stock movements;
- sales: 4 customer orders, including one `SHIPPED` order that creates outbound stock movements;
- warehouse: manual low-stock adjustments for `Water 0.5L` and `Apple Juice 1L`;
- documents: invoice, contract and delivery note for shipped order; invoice and contract for confirmed order.
Idempotency:
- users are matched by email;
- products by SKU;
- warehouses by code;
- suppliers/customers by BIN or company name;
- purchase/customer orders by `DEMO-SEED-*` marker in notes;
- manual stock movements by source, warehouse, product and comment;
- documents reuse the existing unique `(sourceType, sourceId, documentType)` row.
How to verify:
1. Run `docker compose up --build`.
2. Open http://localhost:5173/login.
3. Use any demo credential button.
4. Check Dashboard, Catalog, Purchase Orders, Customer Orders, Stock Balances, Stock Movements, and Documents.
## UI / UX
Frontend содержит единый ERP-style интерфейс без внешней UI-библиотеки:
- общий layout с grouped sidebar и top header;
- reusable UI components в `frontend/src/components/ui`;
- единые table/loading/error/empty/pagination states;
- единые status badges для закупок, продаж, документов и складских движений;
- форматтеры в `frontend/src/utils/formatters.ts` для сумм, количеств, дат, статусов, типов документов и движений;
- role-aware UI: frontend скрывает недоступные кнопки, backend остается главным источником прав.
Available frontend routes:
- `/login`
- `/dashboard`
- `/catalog/products`
- `/catalog/suppliers`
- `/catalog/customers`
- `/catalog/warehouses`
- `/procurement/purchase-orders`
- `/procurement/purchase-orders/{id}`
- `/sales/customer-orders`
- `/sales/customer-orders/{id}`
- `/warehouse/stock-balances`
- `/warehouse/stock-movements`
- `/documents`
Role-aware UI behavior:
- `FINANCE`: read-only UI for catalog, purchase orders, customer orders, warehouse; can generate invoice/contract documents.
- `WAREHOUSE`: no catalog create/edit UI; can receive PO, process/ship SO, create warehouse adjustments, generate delivery notes.
- `ADMIN` and `MANAGER`: main operational actions for catalog, procurement, sales, and documents.
- Warehouse manual `ADJUSTMENT_OUT`, catalog deactivate, PO cancel, and SO cancel use confirmation dialogs.
Common UI components:
- `Button`, `Input`, `Select`, `Textarea`
- `Card`, `Badge`, `PageHeader`
- `EmptyState`, `LoadingState`, `ErrorState`
- `ConfirmDialog`, `FormField`, `Pagination`
## Debug Error Endpoint
Dev-only endpoint для проверки единого формата ошибок:
```bash
curl "http://localhost:8080/api/debug/error?type=validation"
curl "http://localhost:8080/api/debug/error?type=not-found"
curl "http://localhost:8080/api/debug/error?type=business"
curl "http://localhost:8080/api/debug/error?type=generic"
```
Endpoint должен быть удален или отключен перед production.
## Useful Commands
Run:
```bash
docker compose up --build
```
Stop:
```bash
docker compose down
```
Reset database:
```bash
docker compose down -v
docker compose up --build
```
Backend logs:
```bash
docker compose logs -f backend
```
Frontend logs:
```bash
docker compose logs -f frontend
```
Postgres logs:
```bash
docker compose logs -f postgres
```
Server deployment compose:
```bash
docker compose -f docker-compose.server.yml up -d --build
```
`docker-compose.server.yml` does not publish app ports directly. It expects an external Docker network named `common_network` and an edge reverse proxy, for example nginx, to route HTTPS traffic to `erp-frontend` and `/api` traffic to `erp-backend`.
# ERP MVP Demo Script
Pitch:
> This MVP demonstrates the core ERP operating loop: catalog data, procurement, sales orders, warehouse stock movements, document generation and management dashboard in one integrated system.
5-7 minute flow:
1. Login as Admin: `admin@erp.local` / `admin12345`.
2. Open Dashboard and show KPI cards for catalog, sales, procurement, warehouse, documents and low stock.
3. Open Catalog -> Products, Suppliers, Customers, Warehouses and show the seeded master data.
4. Open Purchase Orders and open the received PO.
5. Explain that moving PO to `RECEIVED` created inbound stock movements.
6. Open Stock Balances and show updated warehouse quantities.
7. Open Customer Orders and open the shipped order.
8. Explain that moving SO to `SHIPPED` created outbound stock movements and checked available stock.
9. Open Documents and show generated invoice, contract and delivery note placeholders.
10. Download one PDF from Documents or Customer Order details.
11. Return to Dashboard and show recent activities, low stock and updated analytics.
## Manual QA Checklist
Backend:
- [ ] `/api/health` returns `UP`.
- [ ] Swagger opens at http://localhost:8080/swagger-ui/index.html.
- [ ] Login works.
- [ ] Catalog CRUD works.
- [ ] Purchase order flow works.
- [ ] Customer order flow works.
- [ ] Stock balances update correctly.
- [ ] Documents generate and download.
- [ ] Dashboard loads.
Frontend:
- [ ] Login works.
- [ ] Sidebar navigation works.
- [ ] Dashboard loads.
- [ ] Forms save data.
- [ ] Status transitions work.
- [ ] Errors display properly.
- [ ] Download PDF works.
- [ ] Logout works.
## Troubleshooting
PostgreSQL port already in use:
- Stop the local PostgreSQL process or change the `5432:5432` mapping in `docker-compose.yml`.
Backend cannot connect to database:
- Check `docker compose logs -f backend` and confirm `SPRING_DATASOURCE_URL`, `POSTGRES_DB`, `POSTGRES_USER` and `POSTGRES_PASSWORD` match.
Flyway migration failed:
- For dev data loss is acceptable, reset with `docker compose down -v` and then `docker compose up --build`.
Frontend cannot call backend due to CORS:
- Confirm `CORS_ALLOWED_ORIGINS` includes `http://localhost:5173`.
JWT expired or unauthorized:
- Logout, login again, and check that requests include `Authorization: Bearer <token>`.
Demo data not visible:
- Ensure `DEMO_DATA_ENABLED=true`, then reset the dev database if it was initialized before enabling demo data.
PDF download blocked by browser:
- Try downloading from the Documents page again and make sure pop-up/download blocking is not preventing the file save.
Database credentials changed but old volume remains:
- Run `docker compose down -v` so PostgreSQL can initialize with the new `.env` values.
# MVP Scope
Included:
- Auth and roles.
- Catalog.
- Procurement.
- Sales.
- Warehouse stock.
- Documents.
- Dashboard.
- Demo data.
Not included in MVP v1:
- Real 1C integration.
- Real WMS/TMS integration.
- Payment module.
- Email/WhatsApp sending.
- Electronic signature.
- Advanced approval workflow.
- Advanced BI/Power BI.
- Multi-company accounting.