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

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_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:

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

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

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 code
  • company_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 search
  • name (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/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

# 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)
  • 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.