Add Pydantic validators and service-level checks that reject empty SKU/CODMAT
on create/edit (400). CSV import now silently skips rows without CODMAT and
counts them in skipped_no_codmat instead of treating them as errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix is_complete check: use abs(pct-100)<=0.01 instead of >=99.99
so sets with >100% total are correctly shown as incomplete
- Show pct badge with 2 decimals (e.g. "⚠️ 200.00%")
- Remove product name pre-fill in missing SKUs map modal CODMAT field
- Compact CODMAT lines to single row with placeholders instead of labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IMPORTED pill now includes ALREADY_IMPORTED orders in count
- UNINVOICED filter includes ALREADY_IMPORTED orders
- Pill counts (Toate/Importate/Omise/Erori/Nefacturate) always reflect
full period+search, independent of active status filter
- Nefacturate count computed from SQLite cache across full period,
not just current page
- Bootstrap UI: design tokens, soft badge pills, consistent font sizes,
purge inline styles from templates, move badge-pct to style.css
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New gomag_client.py service: async httpx client that downloads orders
from GoMag API with full pagination and 1s rate-limit sleep
- config.py: add GOMAG_API_KEY, GOMAG_API_SHOP, GOMAG_ORDER_DAYS_BACK,
GOMAG_LIMIT, GOMAG_API_URL settings
- sync_service.py: Phase 0 downloads fresh orders before reading JSONs;
graceful skip if API keys not configured
- start.sh: auto-detect INSTANTCLIENTPATH from .env, fallback to thin mode
- .env.example: document GoMag API variables
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Track already_imported/new_imported counts separately in sync_runs
and surface them in status API + dashboard last-run card
- Cache invoice data in SQLite orders table (factura_* columns);
dashboard falls back to Oracle only for uncached imported orders
- Resolve JSON_OUTPUT_DIR and SQLITE_DB_PATH relative to known
anchored roots in config.py, independent of CWD (fixes WSL2 start)
- Use single Oracle connection for entire validation phase (perf)
- Batch upsert web_products instead of one-by-one
- Remove stale VFP scripts (replaced by gomag-vending.prg workflow)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
orders.status preserves IMPORTED over ALREADY_IMPORTED to avoid
overwriting historical data, so per-run journal views must use
sync_run_orders.status_at_run to show what actually happened in
that specific run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace import_orders (insert-per-run) with orders table (one row per
order, upsert on conflict). Eliminates dedup CTE on every dashboard
query and prevents unbounded row growth at 4-500 orders/sync.
Key changes:
- orders table: PK order_number, upsert via ON CONFLICT DO UPDATE;
COALESCE preserves id_comanda once set; times_skipped auto-increments
- sync_run_orders: lightweight junction (sync_run_id, order_number)
replaces sync_run_id column on orders
- order_items: PK changed to (order_number, sku), INSERT OR IGNORE
- Auto-migration in init_sqlite(): import_orders → orders on first boot,
old table renamed to import_orders_bak
- /api/dashboard/orders: period_days param (3/7/30/0=all, default 7)
- Dashboard: period selector buttons in orders card header
- start.sh: stop existing process on port 5003 before restart;
remove --reload (broken on WSL2 /mnt/e/)
- Add invoice_service, E2E Playwright tests, Oracle package updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add SSE event bus in sync_service (subscribe/unsubscribe/_emit)
- Add GET /api/sync/stream SSE endpoint for real-time sync progress
- Rewrite logs.html: unified runs table + live feed + summary + filters
- Rewrite logs.js: SSE EventSource client, run selection, pagination
- Dashboard: clickable runs navigate to /logs?run=, sync started banner
- Remove "Import Comenzi" nav item, delete sync_detail.html
- Add error_message column to sync_runs table with migration
- Fix: export TNS_ADMIN as OS env var so oracledb finds tnsnames.ora
- Fix: use get_oracle_connection() instead of direct pool.acquire()
- Fix: CRM_POLITICI_PRET_ART INSERT to match actual table schema
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Flask admin with FastAPI app (api/app/) featuring:
- Dashboard with stat cards, sync control, and history
- Mappings CRUD for ARTICOLE_TERTI with CSV import/export
- Article autocomplete from NOM_ARTICOLE
- SKU pre-validation before import
- Sync orchestration: read JSONs -> validate -> import -> log to SQLite
- APScheduler for periodic sync from UI
- File logging to logs/sync_comenzi_YYYYMMDD_HHMMSS.log
- Oracle pool None guard (503 vs 500 on unavailable)
Test suite:
- test_app_basic.py: 30 tests (imports + routes) without Oracle
- test_integration.py: 9 integration tests with Oracle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>