Cube_PA is a heap in SQL Server with no primary key, while the
PostgreSQL cube_pa table uses artnoid as PRIMARY KEY. The cube
normally emits one row per article, but for 3 articles supplied by
two vendors it emitted both vendor prices, so fullRefresh hit:
duplicate key value violates unique constraint "cube_pa_pkey"
These are not exact duplicates: the two rows carry different PA
values, so the choice changes the article's purchase price. Resolve
by keeping the price whose last change is the most recent, read from
the dated history packed into ARTFOU2.PRIXACHAT
("[01/01/1901,0.990:16/05/2018,1.010]"). SUIVIDATEMODIF cannot be
used alone: it is not touched when only the price changes (S028 shows
modif 2017 but a price change in 2025). It only breaks ties.
The lookup runs solely when duplicates are present, so the nightly
sync is unaffected in the normal case.
Verified against the live 427 752-row Cube_PA: 427 749 rows out, all
artnoid distinct, no fallback triggered. Parsing PRIXACHAT over 20 000
ARTFOU2 rows: 19 999 parsed, 1 empty, 0 failures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The code centrale is LaFoir'Fouille's 11-digit product identifier
(prefix 1000x). Only products referenced by the central purchasing
office have one: ~355 800 of 427 700 articles.
The column was already synced from SQL Server and returned by the
detail route via `a.*`, but was absent from the list route and could
not be filtered on.
- list route: select artcentrale, add `artcentrale` (partial match)
and `has_artcentrale=1|0` query params
- filter matches on prefix AND length 11, so malformed values with a
valid prefix ('10000', '10000192289P') are excluded
- COALESCE in the predicate: without it, NOT (NULL LIKE '1000%') is
NULL and the 61 442 NULL rows would vanish from has_artcentrale=0
- normalise '' to null in both routes so they agree
- sync: use safeStr so empty values are stored as NULL, not ''
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add codefou query param (ILIKE partial match) to /api/performance/hitparade.
Also return CODE_FOURNISSEUR and FOURNISSEUR (nom) columns in each result row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CA était négatif (MntMvtTTC brut) → filtre mntmvtttc < 0 + ABS comme le endpoint /ca
- GenreMvt=3 uniquement → IN(3,4,9)
- Niveau 1/2 retournait vide (articles au niveau 3) → remontée via chemin_pere
chemin_pere = "no_id_niv1.no_id_niv2" → SPLIT_PART + JOIN NOMENCLATURE parent
- Ajout taux_marge dans la réponse
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- List endpoint now returns total count and pages for pagination
- Added statut filter (en_cours/passees/futures) to list endpoint
- Added GET /historique using pub_entetes for complete pub history
(pub_ecoulement only has recent data; pub_entetes has all 95 pubs)
- /historique returns nb_sites, ca_total, qte_totale via LEFT JOIN
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FOUCAD ne couvre que 53/153 fournisseurs actifs (commande auto uniquement).
FOUPORT2.SEUIL est la source officielle de l'ERP et couvre 108 fournisseurs.
4 fournisseurs dans COMMANDE_AUTO_QTEPROPO n'avaient pas de franco visible
(IMDIFA, MYCANDYSHOP, PTIT CLOWN, PUCKATOR BV).
- Sync : ajout de FOUPORT et FOUPORT2 dans le pipeline de synchronisation
- API : franco = COALESCE(fouport2.seuil, foucad.franco) dans les deux routes
- Schema PG : création des tables fouport et fouport2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Nouvelle route GET /api/commandes-auto : propositions par site et fournisseur
avec comparaison au franco (franco_atteint, ecart_franco)
- Nouvelle route GET /api/commandes-auto/:codefou : détail articles d'un fournisseur
- Sync FOUCAD (config commande auto + franco) vers PostgreSQL
- Ajout table foucad dans init.sql
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ranking.js: déduplique par (gencod, site) avant batchUpsert pour éviter
"ON CONFLICT DO UPDATE command cannot affect row a second time"
- server.js: bouton "Copier les logs" utilise execCommand fallback quand
navigator.clipboard indisponible (accès HTTP sans HTTPS)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- requestTimeout 300000ms pour éviter timeout sur grosses tables (MvtArt)
- Chaque étape sync est wrappée en try/catch pour ne pas bloquer les suivantes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ON CONFLICT DO NOTHING to mvtart insert to work with unique index
- Document ranking endpoints in API_DOCUMENTATION.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sync Ranking table from SQL Server to PostgreSQL with ranking_ca,
ranking_qte, ranking_mag_* metrics. Expose via /api/ranking and
integrate into /api/articles/:id/referentiel response.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>