feat: Migrate to ultrathin monolith architecture
Consolidate 3 separate applications (reports-app, data-entry-app, telegram-bot) into a unified
architecture with single backend and frontend:
Backend Changes:
- Unified FastAPI backend at backend/ with modular structure
- Modules: reports, data_entry, telegram in backend/modules/
- Centralized config.py and main.py with all routers registered
- Single worker mode (--workers 1) for Telegram bot compatibility
- Shared Oracle connection pool and JWT authentication
- Unified requirements.txt and environment configuration
Frontend Changes:
- Single Vue.js SPA with module-based routing
- Unified frontend at src/ with modules in src/modules/{reports,data-entry}/
- Shared components and stores in src/shared/
- Error boundaries for module isolation
- Dual API proxy in Vite for module communication
Infrastructure:
- New unified startup scripts: start-prod.sh, start-test.sh, start-backend.sh
- Environment templates: .env.dev.example, .env.test.example, .env.prod.example
- Updated deployment scripts for Windows IIS
- Simplified SSH tunnel management
Documentation:
- Comprehensive CLAUDE.md with architecture overview
- Module-specific docs in docs/{data-entry,telegram}/
- Architecture decision records in docs/ARCHITECTURE-DECISIONS.md
- Deployment guides consolidated in deployment/windows/docs/
This migration reduces complexity, improves maintainability, and enables easier
deployment while maintaining all existing functionality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
595
docs/data-entry/DATA-ENTRY-MODULE.md
Normal file
595
docs/data-entry/DATA-ENTRY-MODULE.md
Normal file
@@ -0,0 +1,595 @@
|
||||
# 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 which tunnel is running
|
||||
./ssh-tunnel-prod.sh status
|
||||
./ssh-tunnel-test.sh status
|
||||
|
||||
# Restart with correct tunnel
|
||||
./start-prod.sh # or ./start-test.sh
|
||||
```
|
||||
|
||||
### 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-prod.sh 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-prod.sh # Production Oracle server
|
||||
# OR
|
||||
./start-test.sh # 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)
|
||||
Reference in New Issue
Block a user