H5 was using Bootstrap default (1.25rem = 20px), larger than the H4
page title (18px). This inverted the heading hierarchy. Set H5 to
16px/600 per DESIGN.md section title spec.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DESIGN.md type scale specifies 14px body / 13px data for table cells.
The table was using 1rem (16px), which is Bootstrap's default. This
reduces data density on the most visible component (the order table).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kit articles (multi-component or cantitate_roa≠1) have expected price
differences between GoMag (commercial) and ROA (component sum).
Skip comparison entirely, mark with kit=True flag for UI badge.
Fix kit detection to use float()!=1 (catches cantitate_roa<1 like 0.5).
Update 3 existing tests + add multi-component kit test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PL/SQL cauta_partener_dupa_cod_fiscal gains p_strict_search param:
- strict (=1): search only exact CUI form (ANAF-determined)
- dual (NULL): search all forms (existing anti-dedup behavior)
Skip denomination fallback when strict to force new partner creation.
Python: country filter excludes foreign companies from ANAF batch,
anaf_strict flag threaded sync→import→PL/SQL, normalize RO-space
in cod_fiscal_adjusted comparison to eliminate false positives.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Preset buttons: add min-height 32px desktop, 44px mobile
- btn-sm: add min-height 44px on mobile
- Checkbox: increase min-size on mobile
- Mobile overrides placed AFTER base rules for correct cascade
- Page title h4: override Bootstrap default (24px/500) to match
DESIGN.md spec (18px/600/Display)
- Cache-bust style.css v30
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The billing/shipping diff triangle used inline style="color:#6b7280"
which doesn't adapt to dark mode. Now uses a CSS class with
var(--text-muted) for proper theming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Colored left-border on cards is AI Slop blacklist item #8.
Use warning-light background tint instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix ANAF API: extract CUI from date_generale (not top-level), fix
notFound casing (capital F)
- Fix missing facturare address when same ID as livrare (copy instead
of skip)
- Replace ANAF cache pre-population stub with real logic (3-month CUIs)
- Restructure order detail modal: inline 2-col GOMAG|ROA layout with
compact address lines replacing collapsed sections
- Fix addrMatch() to use field-level comparison with Romanian
abbreviation stripping (STR, NR, BL, SC, AP, ET, ETAJ, APART)
- Add dashboard "Diferente" filter pill for ANAF-adjusted orders
- Update e2e test for new modal structure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent partner duplicates via ANAF CUI verification and dual PL/SQL
search. Fix address matching with street-level comparison and diacritics
normalization. Show partner/address comparison in order detail modal.
- New anaf_service.py: batch ANAF API client with chunking, retry, cache
- PL/SQL: dual CUI search (bare/RO+bare/RO space+bare), 3-tier address
search (street+city+id_loc → city+id_loc → create), strip_diacritics
at storage for addresses and partner names
- SQLite: anaf_cache table, 12 new order columns for partner/address data
- import_service: cod_fiscal_override param, return partner/address from Oracle
- sync_service: ANAF batch integration, denomination mismatch detection,
cache pre-population trigger
- Router: enriched order_detail with partner_info + addresses JSON
- UI: collapsible Detalii Partener + Adrese Comparativ sections in modal,
auto-expand on mismatch, ANAF badges, mobile address cards
- Dashboard: address quality attention indicator
- New scan_duplicate_partners.py script for one-time duplicate audit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
updateSchedulerInterval() only saved when enabled=true, so changing
the dropdown with auto off was lost on refresh. Now always persists.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: GET /api/sync/schedule returned interval_minutes=null when
scheduler was stopped, causing dropdown to stay on first HTML option
(1 min). Setting .value programmatically could trigger onchange, sending
a second PUT with interval=1 right after the user's intended interval.
- GET schedule falls back to DB/default (10 min) when scheduler is off
- Add _schedulerLoading flag to block onchange during loadSchedulerStatus
- Default interval 10 min everywhere (was 5 in backend)
- Cache bust dashboard.js v=33
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kit detection only checked cantitate_roa > 1, missing fractional values
like 0.5 (GoMag 50buc/set → ROA 100buc/set). This caused false price
difference alerts (e.g. GoMag 7.00 vs ROA 14.00 for order #481595156).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses with commas after street (e.g. "Str.Dacia NR.15 BLOC Z2,SCARA A,AP.7")
caused NUMAR column overflow (max 10 chars). Parser now tokenizes by comma and
routes BL/SC/AP/ET/NR prefixes to proper columns. Also extracts NR/BLOC embedded
in street text. Import service now blocks orders when address creation fails
(returns ERROR instead of silently importing without address).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add price_match column to SQLite, cached on order detail view
- Background backfill on startup checks all unchecked imported orders
- Extract _enrich_items_with_codmat() helper to deduplicate SKU enrichment
- Attention card now shows same nefacturate count as filter pill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Period selector had both preset buttons (3/7/30 zile) and a dropdown
with overlapping options. Per plan Commit 6, preset buttons are the
single control: Azi / 3 zile / 7 zile / 30 zile / Custom. Visible
on all screen sizes with horizontal scroll on mobile.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The JS read pret_roa from order.price_check.items[idx] which doesn't
exist. The backend puts pret_roa and price_match directly on each item
in the items array. Fixed both desktop table and mobile view to read
from item.pret_roa and item.price_match instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SKUs with forward slashes (e.g. 0V2071/250159/250158) caused a 404
on /api/mappings/{sku}/prices because path separators in the SKU
were interpreted as URL path segments. Changed to query parameter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Show a welcome card on the dashboard when no sync runs exist yet.
Guides new users: 1. Check Settings → 2. Start Sync → 3. Map SKUs.
Card auto-hides after first successful sync.
Cache-bust: dashboard.js?v=31, style.css?v=24
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate Template CSV, Export CSV, Import CSV into a single
"Import/Export" dropdown. Keep "+ Adauga Mapare" and "Formular complet"
as prominent action buttons.
Cache-bust: mappings.js?v=13
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Default to showing problem orders (ERROR/SKIPPED) first. Imported
orders collapsed behind "X comenzi importate cu succes" toggle.
Reduces noise for operators scanning for issues.
Cache-bust: logs.js?v=14, style.css?v=23
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move Kit Pricing, Price Sync, and Dashboard Polling sections under a
collapsed "Setari avansate" toggle. Basic settings remain visible.
Tooltip warns: "Modificati doar la indicatia echipei tehnice."
Cache-bust: settings.js?v=9
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change default period from 7 to 3 days. Add quick-select preset
buttons (3 zile / 7 zile / 30 zile) that sync with the dropdown.
Reduces noise for daily operators who only need recent orders.
Cache-bust: style.css?v=22, dashboard.js?v=30
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After saving a SKU mapping, check for SKIPPED orders containing that
SKU and show a floating banner with count + "Importa" button. Batch
retries up to 20 orders and shows result feedback.
Backend:
- get_skipped_orders_with_sku() in sqlite_service.py
- GET /api/orders/by-sku/{sku}/pending endpoint
- POST /api/orders/batch-retry endpoint (max 20, sequential)
Frontend:
- Auto-retry banner after quickMap save with batch import button
- Success/error feedback, auto-dismiss after 15s
Cache-bust: shared.js?v=19
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ability to re-import individual ERROR/SKIPPED orders directly from
the order detail modal. Downloads narrow date range from GoMag API,
finds the specific order, and re-runs import_single_order().
Backend:
- New retry_service.py with retry_single_order() — downloads order_date
±1 day from GoMag, finds order by number, imports via import_service
- Guard: blocks retry during active sync (_sync_lock check)
- POST /api/orders/{order_number}/retry endpoint
Frontend:
- "Reimporta" button in modal footer (visible only for ERROR/SKIPPED)
- Spinner during retry, success/error feedback with auto-refresh
Cache-bust: shared.js?v=18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add invoice total comparison in the order detail modal. When an order
has been invoiced, shows whether the invoice total matches the order
total — green badge if OK, red badge with difference amount if not.
Backend: compute reconciliation (difference, match) from existing
invoice.total_cu_tva vs order_total in order_detail endpoint.
Frontend: reconciliation badge below invoice info in modal, hidden
when no invoice exists.
Cache-bust: shared.js?v=17
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a "Needs Attention" card above the orders table that surfaces:
- Import errors count (click → ERROR filter)
- Unmapped SKUs count (click → Missing SKUs page)
- Uninvoiced orders >3 days (click → UNINVOICED filter)
Shows green "Totul in ordine" when all metrics are zero.
Backend: add uninvoiced_old count to get_orders() and unresolved_skus
from get_dashboard_stats() to dashboard/orders API response.
Cache-bust: style.css?v=21, dashboard.js?v=29
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ROA price comparison to order detail modal — operators can now see
if GoMag prices match Oracle before invoicing. Eliminates the #1 risk
of invoicing with wrong prices.
Backend:
- New get_prices_for_order() in validation_service.py — batch Oracle
query with dual-policy routing (sales/production by cont 341/345),
PRETURI_CU_TVA handling, kit total calculation
- Extend GET /api/sync/order/{orderNumber} with per-item pret_roa and
order-level price_check summary
- GET /api/dashboard/orders returns price_match=null (lightweight)
Frontend:
- Modal: price check badge (green/red/grey), "Pret GoMag" + "Pret ROA"
columns, match dot per row, mismatch rows highlighted
- Dashboard: price dot column (₽) in orders table
- Mobile: inline mismatch indicator
Cache-bust: shared.js?v=16, dashboard.js?v=28
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bootstrap's .card sets color: var(--bs-body-color) which stays #212529 in
dark mode since we use data-theme="dark" not data-bs-theme. Added explicit
card dark override and color: var(--text-primary) on .flat-row.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mobile segmented control (7 buttons, 431px) overflowed 375px viewport.
Added overflow-x:auto on segmented containers and min-width:0 on
flex-grow child. Body scroll width now matches viewport.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bootstrap --bs-table-bg defaults to white, not overridden in dark mode.
Added --bs-table-bg: var(--surface) and explicit td background for odd
rows. Zebra now alternates #1E1E1E / #2A2A2A in dark mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DESIGN.md specifies box-shadow glow for SKIPPED status dots:
0 0 6px 2px rgba(202,138,4,0.3). Was missing from .dot-yellow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mobile segmented filter buttons used Bootstrap btn-primary (blue) for
active state. Per DESIGN.md two-accent system, state indicators use
amber. Added .seg-active class with --accent color.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move duplicated order detail modal logic from dashboard.js and logs.js
into a shared renderOrderDetailModal() function in shared.js. Move
modal HTML from dashboard.html and logs.html into base.html.
Shared functions: renderCodmatCell, orderStatusBadge, fmtCost, fmtNum,
computeDiscountSplit, renderReceipt. Both pages now call the shared
modal with page-specific quick map callbacks.
Net -152 lines. Logs page gains invoice info, TVA column, and receipt
footer that were previously dashboard-only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove validation that blocked creating mappings when SKU matches an
existing CODMAT. Users need this for unit quantity conversion (e.g.,
website sells 50 units per SKU but ROA tracks 100, requiring
cantitate_roa=0.5).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Existing 741 rows also updated via UPPER() on customer_name,
shipping_name, billing_name.
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>
Join price policies directly into get_mappings() query so single-article
mappings display prices without extra API calls. Remove VAT percentage
from kit price display.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PACK_FACTURARE: use PTVA from COMENZI_ELEMENTE (NVL2) in adauga_articol_factura
instead of fetching PROC_TVAV from price list, fixing NULL SUMA for discount
lines with multiple TVA rates (11%, 21%).
sync.py: broaden direct SKU enrichment to all unmapped SKUs regardless of
mapping_status, fixing stale status edge cases.
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>
The export CSV button used a hardcoded /api/validate/missing-skus-csv path,
bypassing the IIS /gomag reverse proxy prefix. Also add changelog comments
to PACK_COMENZI and PACK_FACTURARE for the duplicate CODMAT discrimination.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
codmat_policy_map had CODMAT keys only, but build_articles_json looks
up by GoMag SKU — mapped articles like FRSETP250 never got per-article
id_pol, causing Oracle to use default sales policy and fail when price
exists only in production policy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>