Enterprise Resource Planning system pentru gestionare producție, stocuri și aprovizionare.
- Python 3.11+
- Node.js 18+
- MongoDB 6.0+
# Install dependencies
pip install -r requirements.txt
# Create config from sample
copy config\config_sample.yaml config.yaml
# Edit config.yaml and set:
# - identity_server: "localhost" (or "inventree")
# - MongoDB connection string
# - Secret key for JWT
# Initialize local authentication (if using localhost)
python init_local_auth.py
# Fix admin role (if needed)
python fix_admin_role.py
# Start server
python src/backend/app.pyDefault credentials (localhost mode):
- Username:
admin - Password:
admin123 ⚠️ Change password after first login!
cd src/frontend
npm install
npm run dev# Start MongoDB
mongod --dbpath /path/to/data
# Import initial data (optional)
mongorestore --db dataflows_rompharm backups/latest/src/backend/
├── app.py # Main application
├── routes/ # Core routes (auth, users)
├── models/ # Pydantic models
└── utils/ # DB, config, helpers
modules/
├── inventory/ # Inventory management
├── requests/ # Stock requests & production
└── depo_procurement/ # Procurement & purchasing
src/frontend/src/
├── components/ # React components
├── services/ # API services
├── context/ # React context
└── pages/ # Page components
Collections:
├── users # Users & authentication
├── depo_parts # Parts/Articles
├── depo_stocks # Stock master data
├── depo_stocks_movements # Stock ledger (NEW)
├── depo_stocks_balances # Stock balances cache (NEW)
├── depo_requests # Stock requests
├── depo_production # Production data
├── approval_flows # Approval workflows
└── depo_requests_states # Request states
Sistem de contabilitate double-entry pentru stocuri - toate mișcările sunt înregistrate în ledger append-only.
{
_id: ObjectId,
part_id: ObjectId,
batch_code: String,
initial_quantity: Number, // ⭐ Immutable - NU se modifică!
initial_location_id: ObjectId,
supplier: ObjectId,
expiry_date: Date,
purchase_price: Number,
state_id: ObjectId,
created_at: Date,
created_by: String
}{
_id: ObjectId,
stock_id: ObjectId,
part_id: ObjectId,
batch_code: String,
movement_type: String, // RECEIPT, CONSUMPTION, TRANSFER_OUT/IN, ADJUSTMENT, SCRAP
quantity: Number, // + sau -
from_location_id: ObjectId,
to_location_id: ObjectId,
document_type: String,
document_id: ObjectId,
transfer_group_id: String, // Pentru TRANSFER_OUT/IN
created_at: Date,
created_by: String,
notes: String
}{
_id: ObjectId,
stock_id: ObjectId,
location_id: ObjectId,
quantity: Number, // Cantitate curentă
updated_at: Date
}| Type | Operator | Description | Use Case |
|---|---|---|---|
| RECEIPT | + | Primire marfă | Procurement, Production |
| CONSUMPTION | - | Consum | Production, Sales |
| TRANSFER_OUT | - | Ieșire din locație | Transfer între locații |
| TRANSFER_IN | + | Intrare în locație | Transfer între locații |
| ADJUSTMENT | +/- | Ajustare inventar | Inventory count |
| SCRAP | - | Casare | Deteriorare, expirare |
GET /modules/inventory/api/stocks # List stocks
GET /modules/inventory/api/stocks/{id} # Get stock details
POST /modules/inventory/api/stocks # Create stock
PUT /modules/inventory/api/stocks/{id} # Update metadata
POST /modules/inventory/api/stocks/{id}/transfer # Transfer între locații
POST /modules/inventory/api/stocks/{id}/adjust # Ajustare inventar
POST /modules/inventory/api/stocks/{id}/consume # Consum
GET /modules/inventory/api/stocks/{id}/movements # Istoric mișcări
GET /modules/inventory/api/stocks/{id}/balance # Balance pe locații
from modules.inventory.services.stocks_service import create_stock
stock = await create_stock(
db=db,
part_id="693ea9fc71d731f72ad6542c",
batch_code="BATCH-001",
initial_quantity=100,
location_id="693fb21371d731f72ad6544a",
created_by="username",
document_type="PURCHASE_ORDER",
document_id="PO-123"
)from modules.inventory.services.stocks_service import transfer_stock
await transfer_stock(
db=db,
stock_id="694829ba30c2736203fb6e2d",
from_location_id="LOC-A",
to_location_id="LOC-B",
quantity=30,
created_by="username",
document_type="STOCK_TRANSFER"
)from modules.inventory.services.stocks_service import consume_stock
await consume_stock(
db=db,
stock_id="694829ba30c2736203fb6e2d",
location_id="PRODUCTION",
quantity=20,
created_by="username",
document_type="PRODUCTION_ORDER",
document_id="PROD-789"
)from modules.inventory.stock_movements import get_stock_balance
# Balance pentru o locație
balance = get_stock_balance(db, stock_id, location_id)
# Returns: {stock_id, location_id, quantity, updated_at}
# Balance pentru toate locațiile
balance = get_stock_balance(db, stock_id)
# Returns: {stock_id, locations: [...], total_quantity}Sistemul de etichete genereaza PDF-uri cu QR prin DataFlows Docu, folosind date din MongoDB si template-uri predefinite.
- Endpoint:
POST /modules/inventory/api/generate-labels-docu - Implementare:
modules/inventory/routes/labels.py - Config Docu:
config/config.yaml->dataflows_docu - Payload:
{
"table": "depo_parts|depo_stocks|depo_locations",
"items": [{"id": "<ObjectId>", "quantity": 1}]
}depo_parts->VZ128YDOUWXZdepo_stocks->Z4ZW2CN0A0VYdepo_locations->WOPS3UAKOVWH
- Produse (articles):
P{IPN} - Stocuri:
P{IPN}L{BATCH_CODE} - Locuri:
LOC{CODE}
Payload Docu per eticheta:
{
"template_code": "<CODE>",
"data": {
"data": {
"barcode": "...",
"barcode_str": "data:image/png;base64,...",
"part_name": "...",
"part_ipn": "...",
"batch_code": "...",
"expiry_date": "YYYY-MM-DD",
"quantity": 0,
"location_name": "...",
"state_name": "...",
"is_salable": true,
"um": "",
"storage_conditions": "",
"purchase_price": "",
"user_name": "...",
"quant": 1,
"crt_no": 1
}
},
"format": "pdf",
"filename": "labels-...-1",
"options": {}
}Generatorul trimite un job per eticheta (realtime) si apoi concateneaza PDF-urile intr-un singur fisier.
Componenta comuna: src/frontend/src/components/Common/PrintLabelsModal.tsx.
Integrari:
src/frontend/src/pages/ArticlesPage.tsx(selectie multipla -> Print Labels, tabledepo_parts)modules/inventory/frontend/pages/StocksPage.tsx(selectie multipla -> Print Labels, tabledepo_stocks)src/frontend/src/pages/LocationsPage.tsx(selectie multipla -> Print Labels, tabledepo_locations)src/frontend/src/components/Procurement/ReceivedStockTab.tsx(selectie din receptii -> Print Labels, tabledepo_stocks)
- Endpointul
GET /modules/inventory/api/read-labelasteapta formattable:id---..., dar QR-urile generate acum suntP.../LOC.... Daca vrei scanare curead-label, formatul trebuie aliniat. - In
src/frontend/src/pages/MobileProcurementDetailPage.tsxse folosesc endpointuri vechi (/label-templates,/generate-labels) care nu exista in prezent; trebuie migrat la/generate-labels-docudaca se doreste print pe mobil.
# Backup + migrare + indexuri + verificare
python migrate_stocks_to_ledger.py all
# Sau pas cu pas:
python migrate_stocks_to_ledger.py migrate # Migrare date
python migrate_stocks_to_ledger.py indexes # Creare indexuri
python migrate_stocks_to_ledger.py verify # Verificare- Backup - Crează
depo_stocks_backup_YYYYMMDD_HHMMSS - Rename -
quantity→initial_quantity - Add -
initial_location_id - Create - RECEIPT movement pentru fiecare stock
- Create - Balance entry pentru fiecare stock
- Indexes - Crează indexuri pentru performanță
// Verifică că nu mai există câmpul vechi
db.depo_stocks.count({quantity: {$exists: true}}) // Trebuie 0
// Verifică balances
db.depo_stocks_balances.aggregate([
{$group: {_id: null, total: {$sum: "$quantity"}}}
])
// Verifică movements
db.depo_stocks_movements.count({movement_type: "RECEIPT"})- Series Table - Tree table cu serii și materiale
- Used Qty Input - Input pentru cantitate folosită per material
- Unused Materials - Calcul automat:
received - SUM(used) - Decision Section - Status select + comment
- Signatures Section - Approval flow cu semnături
{
request_id: ObjectId,
series: [
{
batch_code: "BATCH-001",
materials: [
{
part: ObjectId,
part_name: String,
batch: String,
received_qty: Number,
used_qty: Number // Input de la user
}
]
}
]
}- Completare Series - User completează
used_qtypentru fiecare material - Save - Salvează în
depo_production.series - Select Status - Alege status din
depo_requests_states(scene: production) - Save Decision - Salvează în
status_log - Sign - Semnează production flow
- Execute - La completare flow:
- Crează stock pentru produse finite (RECEIPT)
- Consumă materiale (CONSUMPTION)
- Actualizează balances
Stări definite în depo_requests_states:
workflow_level- Nivel în workfloworder- Ordine de afișarescenes- Array cu scene-uri unde apare (operations,receive_stock,production)needs_comment- Dacă necesită comentariu
Fiecare decizie se salvează în status_log:
{
status_id: ObjectId,
scene: String, // operations, receive_stock, production
created_at: Date,
created_by: String,
reason: String // Optional comment
}- Operations Flow - Aprobare operațiuni warehouse
- Reception Flow - Aprobare recepție marfă
- Production Flow - Aprobare producție
Fiecare flow:
can_sign_officers- Pot semna (optional)must_sign_officers- Trebuie să semneze (obligatoriu)min_signatures- Număr minim semnături- Suportă role-based officers (
type: "role",reference: "admin")
modules/inventory/
├── routes_refactored.py # Main router
├── stock_movements.py # Ledger helper functions
├── routers/
│ ├── stocks_router.py # Stocks endpoints
│ └── ... # Other routers (TODO)
├── services/
│ ├── common.py # Helper functions
│ ├── stocks_service.py # Stocks business logic
│ └── ... # Other services (TODO)
└── models/
├── stock_models.py # Pydantic models
└── ... # Other models (TODO)
from modules.inventory.services.common import serialize_doc
# Convertește MongoDB doc → JSON
doc = serialize_doc(mongo_document)from modules.inventory.services.common import validate_object_id
# Validează și convertește string → ObjectId
oid = validate_object_id(id_string, "field_name")from modules.inventory.services.common import paginate_results
# Query cu paginare și sortare
result = paginate_results(collection, query, skip, limit, sort_by, sort_order)
# Returns: {results: [...], total: N, skip: X, limit: Y}pytest modules/inventory/tests/
pytest modules/requests/tests/# Test stocks API
curl -X POST http://localhost:8000/modules/inventory/api/stocks \
-H "Content-Type: application/json" \
-d '{"part_id": "...", "batch_code": "TEST-001", "initial_quantity": 100, "location_id": "..."}'
# Test transfer
curl -X POST http://localhost:8000/modules/inventory/api/stocks/{id}/transfer \
-H "Content-Type: application/json" \
-d '{"from_location_id": "...", "to_location_id": "...", "quantity": 30}'// depo_stocks_movements
db.depo_stocks_movements.createIndex({stock_id: 1, created_at: -1});
db.depo_stocks_movements.createIndex({part_id: 1, batch_code: 1});
db.depo_stocks_movements.createIndex({transfer_group_id: 1});
db.depo_stocks_movements.createIndex({document_type: 1, document_id: 1});
// depo_stocks_balances
db.depo_stocks_balances.createIndex({stock_id: 1, location_id: 1}, {unique: true});
db.depo_stocks_balances.createIndex({location_id: 1, quantity: 1});
db.depo_stocks_balances.createIndex({stock_id: 1});# Regenerează balances din ledger
from modules.inventory.stock_movements import regenerate_balances
count = regenerate_balances(db)
print(f"Regenerated {count} balances")# Verifică integritate transfer
from modules.inventory.stock_movements import verify_transfer_integrity
result = verify_transfer_integrity(db, "TRF-2025-0001")
if not result['valid']:
print(f"Error: {result['error']}")Toate ObjectId-urile trebuie convertite la string în API responses:
# În routes
if isinstance(doc['_id'], ObjectId):
doc['_id'] = str(doc['_id'])
# Sau folosește serialize_doc()
from modules.inventory.services.common import serialize_doc
return serialize_doc(doc)- Use balances pentru query-uri frecvente (nu agrega movements)
- Indexuri pe toate câmpurile folosite în query-uri
- Limit numărul de movements returnate (default 100)
- Cache la nivel aplicație pentru stocuri frecvent accesate
// Verifică performanță agregări
db.depo_stocks_movements.explain("executionStats").aggregate([...])
// Verifică utilizare indexuri
db.depo_stocks_movements.getIndexes()- JWT tokens pentru API
- Role-based access control (RBAC)
- Admin vs regular users
- Toate mișcările stoc sunt înregistrate cu
created_by status_logpăstrează istoric deciziiapproval_flowspăstrează semnături cu hash
Pentru probleme sau întrebări:
- Verifică CHANGELOG.md pentru modificări recente
- Verifică logs:
tail -f logs/app.log - Verifică MongoDB:
mongosh dataflows_rompharm
Proprietary - Rompharm Company