Files
roa2web-service-auto/data-entry-app/backend/IMPLEMENTATION_SUMMARY_NOMENCLATURE_SYNC.md
Marius Mutu c5fde510a8 feat: Add JWT auth and nomenclature sync to data-entry-app
Integrate shared JWT authentication into data-entry-app:
- Add Oracle pool initialization for auth service
- Add AuthenticationMiddleware to protect API routes
- Update all receipt endpoints to use CurrentUser from JWT
- Add shared auth router (/api/auth/login, /api/auth/refresh)

Add nomenclature synchronization feature:
- Create SQLite models for synced suppliers, local suppliers, and cash registers
- Add nomenclature router with sync triggers and CRUD endpoints
- Add sync service for Oracle → SQLite nomenclature data
- Update nomenclature_service to use synced SQLite data with fallbacks

Create shared frontend components:
- Add shared/frontend/ with LoginView.vue, auth store factory, login.css
- Integrate shared login and auth into data-entry-app frontend
- Add axios-based API service with token refresh interceptor

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 18:36:24 +02:00

456 lines
13 KiB
Markdown

# Implementation Summary: Nomenclature Sync (FAZA 3)
**Date**: 2025-12-13
**Status**: COMPLETED
**Developer**: Claude Code
---
## Overview
Successfully implemented FAZA 3: Nomenclature Sync for the data-entry-app. This feature enables the application to maintain a local SQLite cache of nomenclatures (suppliers, cash registers) from Oracle, reducing latency and improving performance.
## Files Created
### 1. Models
**File**: `/app/db/models/nomenclature.py`
- `SyncedSupplier` - Suppliers synced from Oracle NOM_PARTENERI
- `LocalSupplier` - Suppliers created locally from OCR (not yet in Oracle)
- `SyncedCashRegister` - Cash registers and bank accounts synced from Oracle
### 2. Service Layer
**File**: `/app/services/sync_service.py`
- `SyncService.sync_suppliers()` - Sync suppliers from Oracle to SQLite
- `SyncService.sync_cash_registers()` - Sync cash registers from Oracle to SQLite
- `SyncService.search_supplier()` - Search in synced + local suppliers
- `SyncService.create_local_supplier()` - Create local supplier from OCR data
- `SyncService.get_all_suppliers()` - Get all suppliers for dropdown
- `SyncService.get_all_cash_registers()` - Get all cash registers for dropdown
- `SyncService.get_schema_for_company()` - Map company ID to Oracle schema
**Company-to-Schema Mapping**:
```python
COMPANY_SCHEMAS = {
1: "CONTAFIN",
2: "CONTAFIN2",
}
```
> **TODO**: Move to config table or environment variable
### 3. API Router
**File**: `/app/routers/nomenclature.py`
New endpoints:
- `GET /api/nomenclature/suppliers` - Get all suppliers (synced + local)
- `GET /api/nomenclature/suppliers/search` - Search supplier by fiscal code or name
- `POST /api/nomenclature/suppliers/local` - Create local supplier from OCR
- `GET /api/nomenclature/cash-registers` - Get all cash registers
- `POST /api/nomenclature/sync/suppliers` - Manual supplier sync
- `POST /api/nomenclature/sync/cash-registers` - Manual cash register sync
- `POST /api/nomenclature/sync/all` - Sync all nomenclatures
### 4. Database Migration
**File**: `/migrations/versions/20251213_002805_add_nomenclature_tables.py`
- Creates `synced_suppliers` table with indexes
- Creates `local_suppliers` table with indexes
- Creates `synced_cash_registers` table with indexes
**Applied**: Yes (migration revision: 3a653da79002)
### 5. Documentation
**File**: `NOMENCLATURE_SYNC.md`
- Complete implementation guide
- Architecture overview
- API reference
- Usage examples
- Troubleshooting guide
**File**: `IMPLEMENTATION_SUMMARY_NOMENCLATURE_SYNC.md` (this file)
- Implementation summary
- Files changed
- Testing checklist
## Files Modified
### 1. `/app/db/models/__init__.py`
**Change**: Added imports for nomenclature models
```python
from .nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
```
### 2. `/app/services/nomenclature_service.py`
**Changes**:
- Updated `get_partners()` to accept optional `session` parameter
- Added SQLite fallback: returns synced/local suppliers if available
- Falls back to mock data if no synced data
- Updated `get_cash_registers()` to accept optional `session` parameter
- Added SQLite fallback for cash registers
### 3. `/app/routers/receipts.py`
**Changes**:
- Updated `get_partners()` endpoint to pass `session` to service
- Updated `get_cash_registers()` endpoint to pass `session` to service
### 4. `/app/routers/__init__.py`
**Change**: Added nomenclature router to exports
```python
from . import receipts, nomenclature
__all__ = ["receipts", "nomenclature"]
```
### 5. `/app/main.py`
**Change**: Registered nomenclature router
```python
from app.routers import receipts, ocr, nomenclature
app.include_router(nomenclature.router, prefix="/api/nomenclature", tags=["nomenclature"])
```
## Database Schema
### synced_suppliers
```sql
CREATE TABLE synced_suppliers (
id INTEGER PRIMARY KEY,
oracle_id INTEGER NOT NULL,
company_id INTEGER NOT NULL,
name VARCHAR(200) NOT NULL,
fiscal_code VARCHAR(50),
address VARCHAR(500),
synced_at DATETIME NOT NULL
);
CREATE INDEX ix_synced_suppliers_oracle_id ON synced_suppliers(oracle_id);
CREATE INDEX ix_synced_suppliers_company_id ON synced_suppliers(company_id);
CREATE INDEX ix_synced_suppliers_fiscal_code ON synced_suppliers(fiscal_code);
```
### local_suppliers
```sql
CREATE TABLE local_suppliers (
id INTEGER PRIMARY KEY,
company_id INTEGER NOT NULL,
name VARCHAR(200) NOT NULL,
fiscal_code VARCHAR(50),
address VARCHAR(500),
created_by VARCHAR(100) NOT NULL,
created_at DATETIME NOT NULL,
pending_oracle_sync BOOLEAN NOT NULL
);
CREATE INDEX ix_local_suppliers_company_id ON local_suppliers(company_id);
CREATE INDEX ix_local_suppliers_fiscal_code ON local_suppliers(fiscal_code);
```
### synced_cash_registers
```sql
CREATE TABLE synced_cash_registers (
id INTEGER PRIMARY KEY,
oracle_id INTEGER NOT NULL,
company_id INTEGER NOT NULL,
name VARCHAR(100) NOT NULL,
account_code VARCHAR(20) NOT NULL,
register_type VARCHAR(10) NOT NULL,
synced_at DATETIME NOT NULL
);
CREATE INDEX ix_synced_cash_registers_oracle_id ON synced_cash_registers(oracle_id);
CREATE INDEX ix_synced_cash_registers_company_id ON synced_cash_registers(company_id);
```
## API Endpoints Summary
### Nomenclature Endpoints
#### GET /api/nomenclature/suppliers
Get all suppliers (synced + local) for dropdown/autocomplete.
**Query Params**:
- `search` (optional) - Filter by name or fiscal code
- `company_id` (optional) - Company ID (defaults to user's first company)
**Response**:
```json
[
{
"id": 1,
"oracle_id": 123,
"name": "OMV Petrom",
"fiscal_code": "RO123456",
"source": "synced"
},
{
"id": 2,
"name": "Local Supplier SRL",
"fiscal_code": "RO789012",
"source": "local"
}
]
```
#### GET /api/nomenclature/suppliers/search
Search for supplier by fiscal code or name.
**Query Params**:
- `fiscal_code` (optional) - Fiscal code to search
- `name` (optional) - Name to search (partial match)
- `company_id` (optional) - Company ID
**Response**:
```json
{
"found": true,
"supplier": {
"id": 1,
"oracle_id": 123,
"name": "OMV Petrom",
"fiscal_code": "RO123456",
"address": "Str. Example 123"
},
"source": "synced"
}
```
#### POST /api/nomenclature/suppliers/local
Create a local supplier from OCR data.
**Body**:
```json
{
"name": "New Supplier SRL",
"fiscal_code": "RO12345678",
"address": "Str. Example 123"
}
```
**Response**:
```json
{
"id": 5,
"name": "New Supplier SRL",
"fiscal_code": "RO12345678",
"address": "Str. Example 123",
"is_local": true
}
```
#### GET /api/nomenclature/cash-registers
Get all cash registers for a company.
**Query Params**:
- `company_id` (optional) - Company ID
**Response**:
```json
[
{
"id": 1,
"oracle_id": 10,
"name": "Casa principala",
"account_code": "5311",
"register_type": "cash"
},
{
"id": 2,
"oracle_id": 20,
"name": "Cont BCR",
"account_code": "5121",
"register_type": "bank"
}
]
```
#### POST /api/nomenclature/sync/suppliers
Manually trigger supplier sync from Oracle.
**Response**:
```json
{
"synced": 150,
"errors": 0,
"message": "Synced 150 suppliers with 0 errors"
}
```
#### POST /api/nomenclature/sync/cash-registers
Manually trigger cash register sync from Oracle.
**Response**:
```json
{
"synced": 5,
"errors": 0,
"message": "Synced 5 cash registers with 0 errors"
}
```
#### POST /api/nomenclature/sync/all
Sync all nomenclatures (suppliers + cash registers).
**Response**:
```json
{
"suppliers": {
"synced": 150,
"errors": 0
},
"cash_registers": {
"synced": 5,
"errors": 0
},
"total_synced": 155,
"total_errors": 0,
"message": "Synced 150 suppliers and 5 cash registers"
}
```
## Testing Checklist
### Unit Tests
- [ ] Test `SyncService.sync_suppliers()` with mock Oracle data
- [ ] Test `SyncService.sync_cash_registers()` with mock Oracle data
- [ ] Test `SyncService.search_supplier()` for synced suppliers
- [ ] Test `SyncService.search_supplier()` for local suppliers
- [ ] Test `SyncService.create_local_supplier()`
- [ ] Test upsert logic (update existing vs insert new)
### Integration Tests
- [ ] Test nomenclature router endpoints with authentication
- [ ] Test `/api/nomenclature/suppliers` endpoint
- [ ] Test `/api/nomenclature/suppliers/search` endpoint
- [ ] Test `/api/nomenclature/suppliers/local` endpoint
- [ ] Test `/api/nomenclature/cash-registers` endpoint
- [ ] Test `/api/nomenclature/sync/suppliers` endpoint
- [ ] Test `/api/nomenclature/sync/all` endpoint
### Manual Testing
- [ ] Start backend: `uvicorn app.main:app --reload --port 8003`
- [ ] Verify `/docs` shows new nomenclature endpoints
- [ ] Test sync endpoint (requires Oracle connection)
- [ ] Test search endpoint with various queries
- [ ] Test create local supplier endpoint
- [ ] Verify existing `/api/receipts/nomenclature/partners` still works
- [ ] Verify existing `/api/receipts/nomenclature/cash-registers` still works
### Oracle Connection Testing
- [ ] Verify SSH tunnel is running (dev/Linux)
- [ ] Test Oracle connection via health endpoint
- [ ] Verify company schema mapping is correct
- [ ] Test sync with real Oracle data
- [ ] Verify table names match actual Oracle schema
### Error Handling Testing
- [ ] Test sync with invalid company ID
- [ ] Test sync with Oracle connection error
- [ ] Test search with no results
- [ ] Test create local supplier with duplicate fiscal code
- [ ] Test endpoints with missing authentication token
## Dependencies
All required dependencies are already in `requirements.txt`:
- `oracledb>=2.0.1` - Oracle database connection
- `sqlmodel>=0.0.14` - ORM for SQLite
- `alembic>=1.13.1` - Database migrations
## Deployment Notes
### Development
1. Ensure SSH tunnel is running: `./ssh_tunnel.sh start`
2. Apply migration: `alembic upgrade head`
3. Run initial sync: `POST /api/nomenclature/sync/all`
4. Start backend: `uvicorn app.main:app --reload --port 8003`
### Production
1. Update `COMPANY_SCHEMAS` in `sync_service.py` with production mappings
2. Apply migration: `alembic upgrade head`
3. Set up cron job for periodic sync (daily recommended)
4. Configure Oracle connection (no SSH tunnel needed on Windows prod)
### Migration Commands
```bash
# Check current version
alembic current
# Apply migration
alembic upgrade head
# Rollback migration
alembic downgrade -1
# View migration history
alembic history
```
## Known Issues / TODOs
1. **Company Schema Mapping**: Currently hardcoded in `sync_service.py`
- TODO: Move to config table or environment variable
2. **Oracle Table Names**: Assumes `NOM_PARTENERI` and `NOM_CASE` exist
- TODO: Verify actual table names in production Oracle schema
- TODO: Add error handling for missing tables
3. **Sync Scheduling**: No automatic periodic sync implemented
- TODO: Add background task or cron job for daily sync
4. **Conflict Resolution**: No logic to handle local supplier matching synced supplier
- TODO: Implement merge logic when OCR supplier matches Oracle supplier
5. **Bidirectional Sync**: Local suppliers not pushed to Oracle
- TODO: Implement sync from SQLite to Oracle for approved local suppliers
6. **Performance**: Sync loads all records at once
- TODO: Implement batch processing for large datasets
- TODO: Add incremental sync (requires Oracle last_modified timestamp)
7. **Validation**: No validation for duplicate fiscal codes
- TODO: Add uniqueness constraint and conflict resolution
8. **Testing**: No unit tests written yet
- TODO: Add comprehensive test suite
## Success Criteria
**Completed**:
- SQLite tables created for synced nomenclatures
- Sync service implemented with Oracle integration
- API endpoints for sync and query operations
- Updated existing nomenclature service to use synced data
- Database migration created and applied
- All files have correct Python syntax
- Documentation created
**Pending**:
- Unit tests
- Integration tests
- Manual testing with real Oracle data
- Production deployment
- Scheduled sync setup
## Next Steps
1. **Testing Phase**:
- Write unit tests for sync service
- Write integration tests for API endpoints
- Manual testing with real Oracle connection
- Performance testing with large datasets
2. **Production Readiness**:
- Update company schema mappings for production
- Verify Oracle table names
- Set up cron job for periodic sync
- Add monitoring and alerting
3. **Enhancements**:
- Implement scheduled background sync
- Add sync status dashboard in frontend
- Implement conflict resolution
- Add bidirectional sync (SQLite → Oracle)
## Related Documentation
- Complete Guide: `NOMENCLATURE_SYNC.md`
- Architecture: `docs/data-entry/ARCHITECTURE.md`
- API Docs: Available at `/docs` when app is running
---
**Implementation completed successfully!** All core features are in place and ready for testing.