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>
13 KiB
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_PARTENERILocalSupplier- 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 SQLiteSyncService.sync_cash_registers()- Sync cash registers from Oracle to SQLiteSyncService.search_supplier()- Search in synced + local suppliersSyncService.create_local_supplier()- Create local supplier from OCR dataSyncService.get_all_suppliers()- Get all suppliers for dropdownSyncService.get_all_cash_registers()- Get all cash registers for dropdownSyncService.get_schema_for_company()- Map company ID to Oracle schema
Company-to-Schema Mapping:
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 namePOST /api/nomenclature/suppliers/local- Create local supplier from OCRGET /api/nomenclature/cash-registers- Get all cash registersPOST /api/nomenclature/sync/suppliers- Manual supplier syncPOST /api/nomenclature/sync/cash-registers- Manual cash register syncPOST /api/nomenclature/sync/all- Sync all nomenclatures
4. Database Migration
File: /migrations/versions/20251213_002805_add_nomenclature_tables.py
- Creates
synced_supplierstable with indexes - Creates
local_supplierstable with indexes - Creates
synced_cash_registerstable 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
from .nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
2. /app/services/nomenclature_service.py
Changes:
- Updated
get_partners()to accept optionalsessionparameter - Added SQLite fallback: returns synced/local suppliers if available
- Falls back to mock data if no synced data
- Updated
get_cash_registers()to accept optionalsessionparameter - Added SQLite fallback for cash registers
3. /app/routers/receipts.py
Changes:
- Updated
get_partners()endpoint to passsessionto service - Updated
get_cash_registers()endpoint to passsessionto service
4. /app/routers/__init__.py
Change: Added nomenclature router to exports
from . import receipts, nomenclature
__all__ = ["receipts", "nomenclature"]
5. /app/main.py
Change: Registered nomenclature router
from app.routers import receipts, ocr, nomenclature
app.include_router(nomenclature.router, prefix="/api/nomenclature", tags=["nomenclature"])
Database Schema
synced_suppliers
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
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
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 codecompany_id(optional) - Company ID (defaults to user's first company)
Response:
[
{
"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 searchname(optional) - Name to search (partial match)company_id(optional) - Company ID
Response:
{
"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:
{
"name": "New Supplier SRL",
"fiscal_code": "RO12345678",
"address": "Str. Example 123"
}
Response:
{
"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:
[
{
"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:
{
"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:
{
"synced": 5,
"errors": 0,
"message": "Synced 5 cash registers with 0 errors"
}
POST /api/nomenclature/sync/all
Sync all nomenclatures (suppliers + cash registers).
Response:
{
"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/suppliersendpoint - Test
/api/nomenclature/suppliers/searchendpoint - Test
/api/nomenclature/suppliers/localendpoint - Test
/api/nomenclature/cash-registersendpoint - Test
/api/nomenclature/sync/suppliersendpoint - Test
/api/nomenclature/sync/allendpoint
Manual Testing
- Start backend:
uvicorn app.main:app --reload --port 8003 - Verify
/docsshows 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/partnersstill works - Verify existing
/api/receipts/nomenclature/cash-registersstill 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 connectionsqlmodel>=0.0.14- ORM for SQLitealembic>=1.13.1- Database migrations
Deployment Notes
Development
- Ensure SSH tunnel is running:
./ssh_tunnel.sh start - Apply migration:
alembic upgrade head - Run initial sync:
POST /api/nomenclature/sync/all - Start backend:
uvicorn app.main:app --reload --port 8003
Production
- Update
COMPANY_SCHEMASinsync_service.pywith production mappings - Apply migration:
alembic upgrade head - Set up cron job for periodic sync (daily recommended)
- Configure Oracle connection (no SSH tunnel needed on Windows prod)
Migration Commands
# Check current version
alembic current
# Apply migration
alembic upgrade head
# Rollback migration
alembic downgrade -1
# View migration history
alembic history
Known Issues / TODOs
-
Company Schema Mapping: Currently hardcoded in
sync_service.py- TODO: Move to config table or environment variable
-
Oracle Table Names: Assumes
NOM_PARTENERIandNOM_CASEexist- TODO: Verify actual table names in production Oracle schema
- TODO: Add error handling for missing tables
-
Sync Scheduling: No automatic periodic sync implemented
- TODO: Add background task or cron job for daily sync
-
Conflict Resolution: No logic to handle local supplier matching synced supplier
- TODO: Implement merge logic when OCR supplier matches Oracle supplier
-
Bidirectional Sync: Local suppliers not pushed to Oracle
- TODO: Implement sync from SQLite to Oracle for approved local suppliers
-
Performance: Sync loads all records at once
- TODO: Implement batch processing for large datasets
- TODO: Add incremental sync (requires Oracle last_modified timestamp)
-
Validation: No validation for duplicate fiscal codes
- TODO: Add uniqueness constraint and conflict resolution
-
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
-
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
-
Production Readiness:
- Update company schema mappings for production
- Verify Oracle table names
- Set up cron job for periodic sync
- Add monitoring and alerting
-
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
/docswhen app is running
Implementation completed successfully! All core features are in place and ready for testing.