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>
595 lines
12 KiB
Markdown
595 lines
12 KiB
Markdown
# Data Entry Module - Technical Reference
|
|
|
|
> **Part of ROA2WEB Ultrathin Monolith** - See `CLAUDE.md` for 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 number
|
|
- `receipt_date` - Document date
|
|
- `partner_id` - Supplier ID (from Oracle)
|
|
- `partner_name` - Supplier name
|
|
- `partner_cui` - Supplier CUI/CIF
|
|
- `receipt_type` - BON/CHITANTA/FACTURA
|
|
- `operation_type` - EXPENSE/INCOME
|
|
- `total_amount` - Total with VAT
|
|
- `vat_amount` - VAT amount
|
|
- `payment_method` - CASH/CARD/BANK
|
|
- `payment_amount` - Amount paid
|
|
- `expense_type_code` - Expense type (FUEL, MATERIALS, etc.)
|
|
- `description` - Notes
|
|
- `status` - DRAFT/PENDING/APPROVED/REJECTED/SYNCED
|
|
- `rejection_reason` - If rejected
|
|
- `created_by` - Username
|
|
- `approved_by` - Accountant username
|
|
- `company_id` - Company ID from Oracle
|
|
|
|
**attachments** - Receipt photos/PDFs
|
|
- `id` (PK)
|
|
- `receipt_id` (FK)
|
|
- `filename` - Original filename
|
|
- `file_path` - Storage path
|
|
- `mime_type` - image/*, application/pdf
|
|
- `file_size` - Bytes
|
|
- `uploaded_at`
|
|
|
|
**accounting_entries** - Auto-generated entries
|
|
- `id` (PK)
|
|
- `receipt_id` (FK)
|
|
- `account_code` - Cont contabil (e.g., "6022")
|
|
- `debit_amount` - Debit
|
|
- `credit_amount` - Credit
|
|
- `description` - Entry description
|
|
- `entry_order` - Sequence number
|
|
|
|
### Migrations
|
|
|
|
**Location:** `backend/modules/data_entry/migrations/`
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```python
|
|
# 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:**
|
|
```sql
|
|
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:**
|
|
```sql
|
|
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:
|
|
|
|
```sql
|
|
-- 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 transaction
|
|
- `pack_contafin.finalizeaza_scriere_act_rul()` - Commit to permanent tables
|
|
- `pack_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 tabs
|
|
- `ReceiptCreateView.vue` - Create/edit form
|
|
- `ReceiptDetailView.vue` - View receipt details (future)
|
|
|
|
### Key Components
|
|
|
|
**Location:** `src/modules/data-entry/components/`
|
|
|
|
- `ReceiptForm.vue` - Main form component
|
|
- `AttachmentUpload.vue` - Drag & drop file upload
|
|
- `AccountingEntriesTable.vue` - Preview generated entries
|
|
- `WorkflowActions.vue` - Approve/Reject buttons
|
|
|
|
### Stores
|
|
|
|
**Location:** `src/modules/data-entry/stores/`
|
|
|
|
- `receiptsStore.js` - Main receipts CRUD
|
|
- `nomenclatureStore.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:**
|
|
```bash
|
|
# 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:**
|
|
```bash
|
|
# 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:**
|
|
```bash
|
|
# 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:**
|
|
```bash
|
|
cd backend/modules/data_entry
|
|
|
|
# Check migration status
|
|
alembic current
|
|
|
|
# Rollback one version
|
|
alembic downgrade -1
|
|
|
|
# Re-apply
|
|
alembic upgrade head
|
|
```
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Backend Tests
|
|
|
|
```bash
|
|
cd backend
|
|
pytest modules/data_entry/tests/
|
|
```
|
|
|
|
### Frontend Tests
|
|
|
|
```bash
|
|
cd src/modules/data-entry
|
|
npm run test
|
|
```
|
|
|
|
### Manual Testing
|
|
|
|
**Test Workflow:**
|
|
1. Login as user (e.g., MARIUS M)
|
|
2. Create receipt → status DRAFT
|
|
3. Upload attachment
|
|
4. Submit → status PENDING_REVIEW
|
|
5. Login as accountant
|
|
6. Approve/Reject
|
|
7. Verify accounting entries generated correctly
|
|
|
|
---
|
|
|
|
## Development Workflow
|
|
|
|
### 1. Start Development Environment
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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:
|
|
```env
|
|
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)
|
|
|
|
1. **Oracle Sync Implementation**
|
|
- Complete `sync_to_oracle()` function
|
|
- Error handling and retry logic
|
|
- Sync status tracking
|
|
|
|
2. **OCR Integration**
|
|
- Automatic data extraction from photos
|
|
- Pre-fill form fields
|
|
- Confidence scores
|
|
|
|
3. **Approval Notifications**
|
|
- Email notifications to accountants
|
|
- User notifications on approval/rejection
|
|
- Telegram bot integration
|
|
|
|
4. **Bulk Operations**
|
|
- Batch approve/reject
|
|
- Bulk upload
|
|
- Excel import
|
|
|
|
5. **Advanced Reporting**
|
|
- Expense analytics by type
|
|
- Supplier spending reports
|
|
- Monthly summaries
|
|
|
|
---
|
|
|
|
**Last Updated:** 2025-12-29
|
|
**Architecture:** Ultrathin Monolith (integrated module)
|