Three code paths could overwrite CRM list prices with wrong values when
web unit (50 buc) differs from ROA unit (100 buc):
- price_sync_service: kit path now skips components that have their own
ARTICOLE_TERTI mapping (individual path handles them with correct ÷0.5)
- validation_service: sync_prices_from_order now skips bax SKUs
(cantitate_roa > 1) in addition to multi-component kits
- pack_import_comenzi: skip negative kit discount (markup), ROUND prices
to nzecimale_pretv decimals
Also adds:
- SQL script for 6 ARTICOLE_TERTI mappings (cantitate_roa=0.5) for cup
articles where web=50buc, ROA=100buc/set
- Oracle schema reference documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Price=0 is a valid state for kit components in crm_politici_pret_art,
inserted automatically by the price sync system. Previously, the kit
validation treated pret=0 the same as missing, blocking orders from
importing even when all SKU mappings were correctly configured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kit discount: v_disc_amt is per-kit, not per-unit — remove division by
v_cantitate_web so discount lines compute correctly (e.g. -2 x 5 = -10).
Price sync: stop auto-inserting missing articles into price policies
(was inserting with wrong proc_tvav from GoMag). Log warning instead.
Kit detection: extend to single-component repackagings (cantitate_roa > 1)
in both PL/SQL package and price sync/validation services.
Add repackaging kit pricing test for separate_line and distributed modes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix vat_included comparison: GoMag API returns int 1, not str "1",
causing all prices to be multiplied by TVA again (double TVA)
- Normalize vat_included to string in gomag_client at parse time
- Price sync now processes kit components individually by looking up
each component's CODMAT as standalone GoMag product
- Add _insert_component_price for components without existing Oracle price
- resolve_mapped_codmats: ROW_NUMBER dedup for CODMATs with multiple
NOM_ARTICOLE entries, prefer article with current stock
- pack_import_comenzi: merge_or_insert_articol to merge quantities when
same article appears from kit + individual on same order
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Oracle PL/SQL: kit pricing logic with Mode A (distributed discount) and
Mode B (separate discount line), dual policy support, PRETURI_CU_TVA flag
- Eliminate procent_pret from entire stack (Oracle, Python, JS, HTML)
- New settings: kit_pricing_mode, kit_discount_codmat, price_sync_enabled
- Settings UI: cards for Kit Pricing and Price Sync configuration
- Mappings UI: kit badges with lazy-loaded component prices from price list
- Price sync from orders: auto-update ROA prices when web prices differ
- Catalog price sync: new service to sync all GoMag product prices to ROA
- Kit component price validation: pre-check prices before import
- New endpoint GET /api/mappings/{sku}/prices for component price display
- New endpoints POST /api/price-sync/start, GET status, GET history
- DDL script 07_drop_procent_pret.sql (run after deploy confirmation)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace single-select gestiune dropdown with multi-select checkboxes.
Settings stores comma-separated IDs, Python builds IN clause with bind
variables, Oracle PL/SQL splits CSV via REGEXP_SUBSTR for stock lookup.
Empty selection = all warehouses (unchanged behavior).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add production pricing policy (id_pol_productie) for articles with cont 341/345,
smart discount VAT splitting across multiple rates, per-article id_pol support,
and mapped SKU price validation. Settings UI updated with new controls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Unified id_articol selection logic in Python (resolve_codmat_ids) and PL/SQL
(resolve_id_articol): filters sters=0 AND inactiv=0, prefers article with
stock in configured gestiune, falls back to MAX(id_articol). Eliminates
mismatch where Python and PL/SQL could pick different id_articol for the
same CODMAT, causing ORA-20000 price-not-found errors.
- Add resolve_codmat_ids helper in validation_service.py (single batch query)
- Refactor validate_skus/validate_prices/ensure_prices to use it
- Add resolve_id_articol function in PL/SQL package body
- Add p_id_gestiune parameter to importa_comanda (spec + body)
- Add /api/settings/gestiuni endpoint and id_gestiune setting
- Add gestiune dropdown in settings UI
Co-Authored-By: Claude Opus 4.6 (1M context) <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>
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>