Complete implementation of multi-server Oracle database support: Backend: - Multi-pool Oracle with lazy loading per server - Email-to-server cache for automatic server discovery - JWT tokens include server_id claim - /auth/check-identity and /auth/check-email endpoints - /auth/my-servers endpoint for listing user's accessible servers - Server switch with password re-authentication Frontend: - New ServerSelector component for header dropdown - Multi-step login flow (identity → server → password) - Server switching from header with password modal - Mobile drawer menu with server selection - Dark mode support for all new components - URL bookmark support with ?server= query param Scripts: - Unified start.sh replacing start-prod.sh/start-test.sh - Unified ssh-tunnel.sh with multi-server support - Updated status.sh for new architecture Tests: - E2E tests for multi-server and single-server login flows - Backend unit tests for all new endpoints - Oracle multi-pool integration tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
12 KiB
Data Entry Module - Technical Reference
Part of ROA2WEB Ultrathin Monolith - See
CLAUDE.mdfor general architecture
Module Overview
Purpose: Introducere date în ERP (bonuri fiscale, chitanțe) cu workflow de aprobare
Location:
- Backend:
backend/modules/data_entry/ - Frontend:
src/modules/data-entry/ - Database:
backend/modules/data/receipts/receipts.db(SQLite) - Uploads:
backend/modules/data/receipts/uploads/
API Base Path: /api/data-entry/
Technical Stack
Backend:
- ORM: SQLModel (Pydantic + SQLAlchemy)
- Migrations: Alembic
- Database: SQLite (Phase 1) → Oracle integration (Phase 2)
- Validation: Pydantic v2
Frontend:
- Framework: Vue.js 3 Composition API
- UI Library: PrimeVue
- State: Pinia stores
- Routing: Vue Router (integrated with main app)
Workflow States
1. DRAFT
↓ User completes form + uploads receipt photo
2. PENDING_REVIEW
↓ System auto-generates accounting entries
3. APPROVED / REJECTED
↓ Accountant approves or rejects
↓ (if rejected, user can resubmit)
4. SYNCED
└→ (Phase 2) Data synced to Oracle ERP
State Transitions
| From | To | Allowed Action | Who |
|---|---|---|---|
| DRAFT | PENDING_REVIEW | Submit for review | User |
| DRAFT | DRAFT | Update/Delete | User |
| PENDING_REVIEW | APPROVED | Approve | Accountant |
| PENDING_REVIEW | REJECTED | Reject | Accountant |
| REJECTED | PENDING_REVIEW | Resubmit | User |
| APPROVED | SYNCED | Sync to Oracle | System (Phase 2) |
Database Schema (SQLite)
Main Tables
receipts - Receipt header
id(PK)receipt_number- Document numberreceipt_date- Document datepartner_id- Supplier ID (from Oracle)partner_name- Supplier namepartner_cui- Supplier CUI/CIFreceipt_type- BON/CHITANTA/FACTURAoperation_type- EXPENSE/INCOMEtotal_amount- Total with VATvat_amount- VAT amountpayment_method- CASH/CARD/BANKpayment_amount- Amount paidexpense_type_code- Expense type (FUEL, MATERIALS, etc.)description- Notesstatus- DRAFT/PENDING/APPROVED/REJECTED/SYNCEDrejection_reason- If rejectedcreated_by- Usernameapproved_by- Accountant usernamecompany_id- Company ID from Oracle
attachments - Receipt photos/PDFs
id(PK)receipt_id(FK)filename- Original filenamefile_path- Storage pathmime_type- image/*, application/pdffile_size- Bytesuploaded_at
accounting_entries - Auto-generated entries
id(PK)receipt_id(FK)account_code- Cont contabil (e.g., "6022")debit_amount- Debitcredit_amount- Creditdescription- Entry descriptionentry_order- Sequence number
Migrations
Location: backend/modules/data_entry/migrations/
# Create new migration
cd backend/modules/data_entry
alembic revision --autogenerate -m "description"
# Apply migrations
alembic upgrade head
# Rollback
alembic downgrade -1
Important: Migrations run automatically on module startup via lifespan event in main.py.
Expense Types (Hardcoded - Phase 1)
| Code | Type | Account | VAT | Description |
|---|---|---|---|---|
FUEL |
Combustibil | 6022 | 19% | Combustibil auto |
MATERIALS |
Materiale | 6028 | 19% | Materiale consumabile |
OFFICE |
Rechizite | 6024 | 19% | Rechizite birou |
PHONE |
Telefonie | 626 | 19% | Servicii telefonie |
PARKING |
Parcare | 6022 | 19% | Taxe parcare |
FOOD |
Alimentație | 6028 | 0% | Alimente (fără TVA) |
TRANSPORT |
Transport | 624 | 19% | Transport marfă |
OTHER |
Altele | 628 | 19% | Alte cheltuieli |
Auto-generation Logic:
When user submits receipt, system automatically generates:
# Entry 1: Expense account (Debit)
account_code = expense_type.account # e.g., "6022"
debit_amount = total_amount - vat_amount
credit_amount = 0
# Entry 2: VAT account (Debit) - if VAT > 0
account_code = "4426" # TVA deductibilă
debit_amount = vat_amount
credit_amount = 0
# Entry 3: Payment account (Credit)
account_code = cash_register.account # e.g., "5311"
debit_amount = 0
credit_amount = total_amount
Oracle Nomenclatures (Read-Only)
Module uses shared Oracle connection for nomenclatures:
Partners (Suppliers)
Query:
SELECT B.ID_PART, B.DENUMIRE, B.COD_FISCAL, B.ADRESA
FROM {schema}.CORESP_TIP_PART A
INNER JOIN {schema}.VNOM_PARTENERI B ON A.ID_PART = B.ID_PART
WHERE A.ID_TIP_PART = 17 -- Furnizori
AND (B.INACTIV = 0 OR B.INACTIV IS NULL)
ORDER BY B.DENUMIRE
Partner Types (id_tip_part):
17- Furnizori (Suppliers)22- Casa LEI (Cash LEI)23- Casa Valută (Cash FX)24- Bancă LEI (Bank LEI)25- Bancă Valută (Bank FX)
Cash Registers & Bank Accounts
Query:
SELECT B.ID_PART, B.DENUMIRE, B.COD_FISCAL, B.CONT
FROM {schema}.CORESP_TIP_PART A
INNER JOIN {schema}.VNOM_PARTENERI B ON A.ID_PART = B.ID_PART
WHERE A.ID_TIP_PART IN (22, 23, 24, 25)
AND (B.INACTIV = 0 OR B.INACTIV IS NULL)
ORDER BY B.DENUMIRE
Account Mapping:
- Casa LEI → Account
5311 - Casa Valută → Account
5314 - Bancă LEI → Account
5121 - Bancă Valută → Account
5124
Oracle Integration (Phase 2)
Sync Process
When receipt is APPROVED, sync to Oracle using stored procedures:
-- 1. Initialize
EXEC {schema}.pack_contafin.init_scriere_act_rul_local(
p_id_utilizator => :user_id,
p_id_baza => :company_id,
p_id_luna => :month_id
);
-- 2. Insert temp document
INSERT INTO {schema}.ACT_TEMP (
ID_ACT, DATA_ACT, NR_DOCUMENT, ID_PART,
TIP_ACT, STARE_ACT, ...
) VALUES (
:act_id, :receipt_date, :receipt_number, :partner_id,
'CUMPARARE', 'TEMP', ...
);
-- 3. Insert temp entries
INSERT INTO {schema}.ACT_TEMP_RUL (
ID_ACT, CONT, SUMA_D, SUMA_C, EXPLICATIE
) VALUES (
:act_id, :account_code, :debit, :credit, :description
);
-- 4. Finalize
EXEC {schema}.pack_contafin.finalizeaza_scriere_act_rul(
p_id_utilizator => :user_id,
p_id_act => :act_id
);
Stored Procedures:
pack_contafin.init_scriere_act_rul_local()- Initialize transactionpack_contafin.finalizeaza_scriere_act_rul()- Commit to permanent tablespack_contafin.get_next_act_id()- Get next document ID
Reference: See docs/PACK_CONTAFIN.pck for full stored procedure definitions.
API Endpoints
Receipts CRUD
List Receipts
GET /api/data-entry/receipts/
Query params: page, page_size, status, search, date_from, date_to
Get Receipt
GET /api/data-entry/receipts/{id}
Create Receipt
POST /api/data-entry/receipts/
Body: ReceiptCreate schema
Update Receipt (DRAFT only)
PUT /api/data-entry/receipts/{id}
Body: ReceiptUpdate schema
Delete Receipt (DRAFT only)
DELETE /api/data-entry/receipts/{id}
Workflow Actions
Submit for Review
POST /api/data-entry/receipts/{id}/submit
Status: DRAFT → PENDING_REVIEW
Auto-generates accounting entries
Approve
POST /api/data-entry/receipts/{id}/approve
Body: { "notes": "Optional approval notes" }
Status: PENDING_REVIEW → APPROVED
Reject
POST /api/data-entry/receipts/{id}/reject
Body: { "reason": "Rejection reason (required)" }
Status: PENDING_REVIEW → REJECTED
Resubmit (after rejection)
POST /api/data-entry/receipts/{id}/resubmit
Status: REJECTED → PENDING_REVIEW
Attachments
Upload
POST /api/data-entry/receipts/{id}/attachments
Content-Type: multipart/form-data
Field: file (image/*, application/pdf, max 10MB)
Download
GET /api/data-entry/attachments/{id}/download
Response: File stream
Delete
DELETE /api/data-entry/attachments/{id}
Nomenclatures
Partners (from Oracle)
GET /api/data-entry/receipts/nomenclature/partners
Query: search (optional)
Cash Registers (from Oracle)
GET /api/data-entry/receipts/nomenclature/cash-registers
Expense Types (hardcoded)
GET /api/data-entry/receipts/nomenclature/expense-types
Statistics
GET /api/data-entry/receipts/stats
Response: { total, by_status: { DRAFT: N, ... } }
Frontend Components
Views
Location: src/modules/data-entry/views/
ReceiptListView.vue- Main list with filters and status tabsReceiptCreateView.vue- Create/edit formReceiptDetailView.vue- View receipt details (future)
Key Components
Location: src/modules/data-entry/components/
ReceiptForm.vue- Main form componentAttachmentUpload.vue- Drag & drop file uploadAccountingEntriesTable.vue- Preview generated entriesWorkflowActions.vue- Approve/Reject buttons
Stores
Location: src/modules/data-entry/stores/
receiptsStore.js- Main receipts CRUDnomenclatureStore.js- Partners, cash registers, expense types
Common Issues
1. Nomenclatoare goale / furnizori lipsă
Symptom: Partner dropdown is empty
Causes:
- Wrong Oracle server (PROD vs TEST)
- Selected company doesn't exist on current server
- No suppliers configured for company
Solution:
# Check SSH tunnel status (for servers requiring SSH)
./ssh-tunnel.sh status
# Restart with correct environment
./start.sh prod # or ./start.sh test (test uses direct connection)
2. SQLite locked errors
Symptom: database is locked
Causes:
- Multiple backend instances accessing same DB
- Alembic migration in progress
Solution:
# Check for multiple processes
ps aux | grep uvicorn
# Kill duplicates, restart
./start.sh prod restart
3. Upload fails
Symptom: 500 error on file upload
Causes:
- File too large (>10MB)
- Wrong MIME type
- Upload directory permissions
Solution:
# Check upload directory
ls -la backend/modules/data/receipts/uploads/
# Create if missing
mkdir -p backend/modules/data/receipts/uploads/
chmod 755 backend/modules/data/receipts/uploads/
4. Migration errors
Symptom: Alembic fails on startup
Solution:
cd backend/modules/data_entry
# Check migration status
alembic current
# Rollback one version
alembic downgrade -1
# Re-apply
alembic upgrade head
Testing
Backend Tests
cd backend
pytest modules/data_entry/tests/
Frontend Tests
cd src/modules/data-entry
npm run test
Manual Testing
Test Workflow:
- Login as user (e.g., MARIUS M)
- Create receipt → status DRAFT
- Upload attachment
- Submit → status PENDING_REVIEW
- Login as accountant
- Approve/Reject
- Verify accounting entries generated correctly
Development Workflow
1. Start Development Environment
# Start unified monolith (backend + frontend)
./start.sh prod # Production Oracle server
# OR
./start.sh test # Test Oracle server
2. Make Changes
Backend:
- Edit files in
backend/modules/data_entry/ - Uvicorn auto-reloads on file changes
Frontend:
- Edit files in
src/modules/data-entry/ - Vite HMR auto-updates browser
3. Database Changes
cd backend/modules/data_entry
# Generate migration
alembic revision --autogenerate -m "Add new field"
# Review migration file
nano migrations/versions/xxx_add_new_field.py
# Apply
alembic upgrade head
4. Testing
# Backend
cd backend
pytest modules/data_entry/tests/ -v
# Frontend
cd src/modules/data-entry
npm run test
Production Deployment
See deployment/windows/ for Windows deployment scripts.
Module Activation:
Enabled via .env flag:
MODULE_DATA_ENTRY_ENABLED=true
Database Location:
- Development:
backend/modules/data/receipts/receipts.db - Production:
C:\inetpub\wwwroot\roa2web\backend\modules\data\receipts\receipts.db
Uploads Location:
- Development:
backend/modules/data/receipts/uploads/ - Production:
C:\inetpub\wwwroot\roa2web\backend\modules\data\receipts\uploads\
Future Enhancements (Phase 2)
-
Oracle Sync Implementation
- Complete
sync_to_oracle()function - Error handling and retry logic
- Sync status tracking
- Complete
-
OCR Integration
- Automatic data extraction from photos
- Pre-fill form fields
- Confidence scores
-
Approval Notifications
- Email notifications to accountants
- User notifications on approval/rejection
- Telegram bot integration
-
Bulk Operations
- Batch approve/reject
- Bulk upload
- Excel import
-
Advanced Reporting
- Expense analytics by type
- Supplier spending reports
- Monthly summaries
Last Updated: 2025-12-29 Architecture: Ultrathin Monolith (integrated module)