diff --git a/SUIVI.md b/SUIVI.md
index 31a5ffb..f77688f 100644
--- a/SUIVI.md
+++ b/SUIVI.md
@@ -3,15 +3,56 @@
> Tableau de bord. À mettre à jour au fil de l'eau — c'est le premier fichier à lire
> pour savoir où on en est.
-**Les compteurs, mesurés le 29/07/2026 après la série de sept chantiers.** **2 572 tests
-Go** verts sur 31 paquets — 0 échec, 5 écartés, contre 2 352 avant la série, dont 10
-viennent du correctif du démarrage à froid intégré depuis `main` — et **764 tests
-front** sur 34 fichiers, 0 échec, contre 686 sur 31 fichiers. `gofmt` propre, `go vet` vert,
-`svelte-check` sur 338 fichiers sans une erreur ni un avertissement, et les deux gardes du
-dépôt, `boundary` et `deps`, vertes. Le bundle de l'écran client reconstruit pèse **79 429
-octets gzip, soit 70,5 % du budget de 112 640** — 33 211 octets de marge. La passe `-race`
-**n'a pas pu être jouée sur ce poste** : elle exige cgo, le dépôt est en zéro cgo et il n'y
-a pas de gcc ici. La CI Linux est seule à la couvrir, et c'est d'elle qu'il faut l'exiger.
+**Les compteurs, mesurés le 30/07/2026 après le nommage des produits dans les
+signalements.** **2 572 tests Go** verts sur 31 paquets — 0 échec, 5 écartés — et **766
+tests front** sur 34 fichiers, 0 échec. `gofmt` propre, `go vet` vert, `svelte-check` sur
+338 fichiers sans une erreur ni un avertissement, et les deux gardes du dépôt, `boundary`
+et `deps`, vertes. Le bundle de l'écran client reconstruit pèse **79 425 octets gzip, soit
+70,5 % du budget de 112 640** — 33 215 octets de marge. **La passe `-race` est verte elle
+aussi**, 31 paquets, 0 échec.
+
+**Il y a bien un gcc sur ce poste, et les relevés précédents disaient le contraire.** La
+passe `-race` exige cgo, donc un compilateur C, et trois entrées de suivi de suite l'ont
+déclarée injouable ici en la renvoyant à la CI Linux. C'était faux : WinLibs est installé
+par winget, et son `gcc` — MinGW-W64 UCRT, 16.1.0 — n'est que dans le PATH **utilisateur**,
+absent du PATH machine. Un shell qui n'hérite pas de l'environnement de session ne le voit
+pas, et `go test -race` répond alors `cgo: C compiler "gcc" not found`, ce qui ressemble à
+une absence. Le chemin, pour ne pas le rechercher une quatrième fois :
+
+```
+%LOCALAPPDATA%\Microsoft\WinGet\Packages\BrechtSanders.WinLibs.POSIX.UCRT_Microsoft.Winget.Source_8wekyb3d8bbwe\mingw64\bin
+```
+
+Les trois invariants de concurrence du Hub sont donc vérifiables **avant** de pousser, et
+`make test` joue ses deux passes comme prévu — c'est exactement ce que l'en-tête du
+`Makefile` demande de ne jamais perdre.
+
+**Les listes de signalements nomment enfin les produits (30/07/2026).** §10.3 bis exigeait
+le nom dans le OÙ depuis le début — *« `id` Odoo **et** nom du produit — de quoi ouvrir la
+fiche sans chercher »* — et les trois listes de la page Catalogue n'affichaient que
+l'identifiant. Corriger « 4412 » demande de chercher d'abord de quel produit il s'agit ;
+corriger « AIL VIOLET SAF » commence tout de suite. Le nom est désormais porté par le
+signalement lui-même, de la qualification jusqu'à l'écran.
+
+| # | Ce qui a changé | État |
+|---|---|---|
+| 1 | `domain.Finding.ProductName`, rempli par les treize constructeurs de `internal/catalog` qui portent une ligne, et par `duplicateID` du parseur | ✅ |
+| 2 | Migration `0002_findings_product_name.sql` : colonne `TEXT NOT NULL DEFAULT ''` sur `findings` | ✅ |
+| 3 | `product_name` dans la route `GET /admin/api/imports` et dans `FindingDTO` | ✅ |
+| 4 | Le nom dessiné avant l'identifiant dans les trois listes — anomalies, unités divergentes, non pesables | ✅ |
+| 5 | Deux tests de store devenus indépendants du numéro de schéma (`MigrationCount()`), et `reopenWithMigrations` qui embarque **toutes** les migrations livrées | ✅ |
+
+**Pourquoi un instantané et pas une jointure.** Aller chercher le nom dans `products` au
+moment de l'affichage aurait évité la migration, et menti deux fois : les signalements d'un
+import de mars auraient porté le nom d'aujourd'hui, et un lot **refusé** — dont aucun
+produit n'entre jamais en base — n'aurait eu aucun nom du tout, alors que c'est précisément
+le lot qu'on veut diagnostiquer. `weighings.product_name` existe pour la même raison depuis
+le début.
+
+**Ce qui n'a pas été touché, et pourquoi.** Le panneau « Produits retirés depuis l'import
+précédent » ne publie encore qu'un **nombre** : les noms demanderaient une route de plus,
+et le panneau dit lui-même où les lire. Ce n'est pas une liste de produits, c'est un
+compteur — il sort du périmètre de cette correction.
**Le démarrage à froid de la v0.6 montrait une panne qui n'existait pas (29/07/2026).**
Poste redémarré après l'installation : session `openscale` ouverte seule, puis une page
diff --git a/docs/02-architecture.md b/docs/02-architecture.md
index 35b965e..eadceb3 100644
--- a/docs/02-architecture.md
+++ b/docs/02-architecture.md
@@ -2112,7 +2112,9 @@ flowchart TD
- **`IMAGE_MISSING`** comme anomalie de catalogue : voir §10.7. Une tuile sans photo est un **état d'affichage**, pas un défaut d'import — et sur les fichiers réels, `IMAGE_MISSING` se déclencherait sur **49 %** des lignes de `flv.csv` et **100 %** de celles de `flv_1.csv`. *(`IMAGE_INVALID`, en revanche, **revient** : depuis que le champ est rempli, un contenu base64 qui n'est ni JPEG, ni PNG, ni GIF, ni BMP est un fait signalable — §10.7.)*
- **`products.visible`** (colonne dérivée stockée) et **`products.anomalies`** (JSON doublonnant la table des signalements) : remplacés par `qualification` + `reason` (§12.3).
-**Les signalements sont conservés par import** (`findings`, §12.3), avec ligne CSV, code, valeur fautive et phrase française — au lieu du `RapportIntegrite` purgé à chaque exécution. Deux signalements ne portent sur aucun produit en particulier : `UNEXPECTED_HEADER` (§10.2) et `UNKNOWN_CATEGORY` (§10.2 bis) ; ni l'un ni l'autre n'empêche quoi que ce soit.
+**Les signalements sont conservés par import** (`findings`, §12.3), avec ligne CSV, identifiant Odoo, **nom du produit**, code, valeur fautive et phrase française — au lieu du `RapportIntegrite` purgé à chaque exécution. Deux signalements ne portent sur aucun produit en particulier : `UNEXPECTED_HEADER` (§10.2) et `UNKNOWN_CATEGORY` (§10.2 bis) ; ni l'un ni l'autre n'empêche quoi que ce soit.
+
+Le nom est un **instantané d'affichage**, écrit par l'import qui l'a lu, et pour la raison qui vaut déjà pour `weighings.product_name` : le nom bouge dans Odoo, la ligne qui décrit un import de mars ne bouge pas. Aller le chercher dans `products` au moment de l'affichage montrerait, des signalements d'un import passé, un fait que cet import n'a pas dit — et n'en montrerait aucun pour un lot refusé, dont aucun produit n'est jamais entré en base. Il est vide dans les deux cas où le fichier n'en donne pas : un signalement qui ne porte sur aucun produit, et une ligne trop abîmée pour porter un nom, ce que `UNREADABLE_ROW` dit déjà.
#### 10.3 bis Le rapport d'import est un outil de correction d'Odoo, pas seulement un filtre
@@ -3006,6 +3008,12 @@ CREATE TABLE local_decisions (
CREATE TABLE findings (
import_id INTEGER NOT NULL REFERENCES imports(id) ON DELETE CASCADE,
csv_line INTEGER NOT NULL, product_id TEXT,
+ product_name TEXT NOT NULL DEFAULT '', -- instantané d'affichage, comme
+ -- weighings.product_name : le nom bouge dans
+ -- Odoo, la ligne qui décrit un import ne
+ -- bouge pas. Vide quand le fichier n'en donne
+ -- pas — signalement sans produit, ou ligne
+ -- trop abîmée pour porter un nom
code TEXT NOT NULL, -- INVALID_BARCODE, UNIT_MISMATCH, …
issue TEXT NOT NULL CHECK (issue IN ('anomaly','info')),
message TEXT NOT NULL, value TEXT NOT NULL DEFAULT ''
@@ -3822,7 +3830,7 @@ Contrastes **AAA** (≥ 7:1) sur tout texte ≥ 24 px, **AA** partout ailleurs
| Matériel | Balance : **protocole** (deux entrées, §9.3), **liste déroulante des ports détectés avec description USB**, **« Détecter automatiquement »** (ouvre chaque port 3 s, applique le parseur, annonce « COM8 : 12 trames valides, GRAM XFOC » — **c'est la détection qui répond à « y a-t-il une balance ? », pas l'exploitant**), case **« ce poste n'a pas de balance »** (`scale.present`, qui éteint le feu au lieu de le laisser rouge), **visualiseur des 20 dernières trames brutes en direct** (hexa + décodé, **toujours actif** : ce n'est plus un réglage), **cadence médiane**. Imprimante : driver, transport, **« Lister les files »**, **« Rechercher l'imprimante »**, statut, 3 auto-tests, trame de statut brute en hexa. |
| Étiquette | Menu de gabarit, **aperçu PNG rafraîchi à chaque frappe** (identique à l'impression, A2), décalage X/Y en dots avec flèches ±1 **bornées par la géométrie**, noircissement, vitesse, exemplaires, **bandeau chiffré de `Diagnose()`** avec la mention « troncature volontaire (ADR-003) », éditeur tabulaire du gabarit. |
| Règles | Grille de tarifs (code/libellé/abrégé/**remise en %**/ordre), **la ligne du tarif de référence en lecture seule pour son prix et modifiable pour ses mots**, les deux arrondis **avec l'explication de l'écart au centime**, les 14 garde-fous avec **leur seuil et leur message modifiables, leur sévérité en lecture seule** (§6.4), **aperçu en direct** (« à 8 g, ce produit serait refusé »), code-barres. **La liste des dérogations de poids minimum actives** — un produit nommé par ligne, avec son `min_weight_g`, sa date et son auteur —, en lecture ici et **modifiable depuis l'onglet Catalogue**, où se trouve le produit (§10.6). |
-| Catalogue | **Où le poste va chercher le catalogue** — répertoire local ou serveur WebDAV, avec le répertoire surveillé, ou l'adresse et le compte (§10.1, ADR-038) ; le mot de passe s'y saisit **en écriture seule**, laissé vide il ne bouge pas, et choisir WebDAV avertit **au moment du choix** que le dépôt d'un CSV depuis l'écran n'y existe plus · Dernier import et son **inventaire** · **liste des anomalies et des unités divergentes, chacune avec son numéro de ligne du CSV, son motif en français et la valeur fautive** · **liste des non-pesables** (préemballés, sans code-barres), présentée comme un **inventaire neutre** et jamais comme une liste d'erreurs · produits **retirés** depuis l'import précédent (§10.9) · historique des 20 derniers imports · **glisser-déposer d'un CSV (A4)** · **interrupteur « Ne plus proposer ce produit »** · **« Autoriser ce produit à peser moins de 10 g »** (dérogation par produit, §10.6) · « Oublier la quarantaine » · état de la source. |
+| Catalogue | **Où le poste va chercher le catalogue** — répertoire local ou serveur WebDAV, avec le répertoire surveillé, ou l'adresse et le compte (§10.1, ADR-038) ; le mot de passe s'y saisit **en écriture seule**, laissé vide il ne bouge pas, et choisir WebDAV avertit **au moment du choix** que le dépôt d'un CSV depuis l'écran n'y existe plus · Dernier import et son **inventaire** · **liste des anomalies et des unités divergentes, chacune avec le nom du produit, son numéro de ligne du CSV, son motif en français et la valeur fautive** · **liste des non-pesables** (préemballés, sans code-barres), nommés eux aussi, présentée comme un **inventaire neutre** et jamais comme une liste d'erreurs · produits **retirés** depuis l'import précédent (§10.9) · historique des 20 derniers imports · **glisser-déposer d'un CSV (A4)** · **interrupteur « Ne plus proposer ce produit »** · **« Autoriser ce produit à peser moins de 10 g »** (dérogation par produit, §10.6) · « Oublier la quarantaine » · état de la source. |
| Journal | 200 dernières pesées, filtres, export CSV, détail **avec la trame brute** et bouton « Rejouer cette trame ». Journal technique en dessous. |
| Poste | Numéro, nom, coop, empreinte, export/import avec **aperçu du diff champ par champ**, 5 versions restaurables, version du binaire, chemins, espace disque. *(Pas de bouton « redémarrer » : aucun bloc de configuration ne l'exige — §11.4.)* |
diff --git a/docs/03-glossaire.md b/docs/03-glossaire.md
index 0411145..0e04367 100644
--- a/docs/03-glossaire.md
+++ b/docs/03-glossaire.md
@@ -483,7 +483,7 @@ portent.
| Un rayon de la grille | `Category.Code` / `Label` / `Rank` / `Color` / `Visible` |
| L'enregistrement d'une pesée au journal | `Weighing.ID` / `OccurredAt` / `Station` / `JobID` / `IdempotencyKey` / `ProductID` / `ProductName` / `Reference` / `Mode` / `GrossWeight` / `Tare` / `NetWeight` / `Quantity` / `BaseUnitPrice` / `Lines` / `Barcode` / `Source` / `Stability` / `RateMS` / `Frame` / `Result` / `Detail` / `DurationMS` · `WeighingLine.TierCode` / `UnitPrice` / `Amount` |
| L'enregistrement d'un import | `Import.ID` / `OccurredAt` / `Source` / `FileName` / `SHA256` / `ByteCount` / `RowsRead` / `UnreadableRows` / `Weighable` / `NotWeighable` / `Anomalies` / `UnitMismatches` / `ImagesDecoded` / `ImagesRejected` / `ProductsWithdrawn` / `Result` / `Code` / `Reason` / `DurationMS` |
-| Ce qu'un import a à dire sur une ligne | `Finding.ImportID` / `CSVLine` / `ProductID` / `Code` / `Issue` / `Message` / `Value` |
+| Ce qu'un import a à dire sur une ligne | `Finding.ImportID` / `CSVLine` / `ProductID` / `ProductName` / `Code` / `Issue` / `Message` / `Value` |
| Une photo, une décision humaine, un contenu banni | `Image.SHA256` / `ByteCount` / `Format` / `Width` / `Height` / `SeenAt` · `LocalDecision.ProductID` / `Offered` / `MinWeightG` / `Reason` / `DecidedAt` / `DecidedBy` · `QuarantineEntry.SHA256` / `FailureCount` / `FirstFailureAt` / `LastFailureAt` / `Code` / `Reason` |
| Les blocs de la configuration, un champ par bloc de §11.2 | `Config.Version` / `Readme` / `ModifiedAt` / `Station` / `Network` / `UI` / `Scale` / `Printer` / `Pricing` / `Barcode` / `Limits` / `Stability` / `Catalog` / `Journal` / `Admin` / `Maintenance` |
| Le contenu de chaque bloc | `StationConfig.Number` / `Name` / `Coop` · `NetworkConfig.Listen` / `AdminOnLAN` · `UIConfig.Language` / `Sound` / `IdleTimeoutSeconds` / `ReprintWindowSeconds` / `ShowGridPrices` · `ScaleConfig.Type` / `Present` / `ManualEntryAllowed` / `DegradeAfterSeconds` / `Options` · `PrinterConfig.Type` / `Template` / `Options` · `CatalogConfig.Type` / `Options` / `Images` / `Categories` / `FallbackCategory` · `ImagesConfig.Source` / `Path` · `JournalConfig.MaxRows` / `MaxDays` / `MaxTechnical` · `AdminConfig.PasswordHash` / `RecoveryCodeHash` / `SessionMinutes` / `AttemptsPerMinute` · `MaintenanceConfig.WeeklyIntegrityCheck` / `DiskAlertMB` · `BarcodeConfig.VerifyReferenceCheckDigit` |
@@ -519,7 +519,7 @@ portent.
| `qualification IN ('pesable','non_pesable','anomalie')` | `qualification IN ('weighable','not_weighable','anomaly')` |
| `idx_produits_grille` / `idx_produits_reference` | `idx_products_grid` / `idx_products_reference` |
| table `decisions_locales (produit_id, proposer, poids_min_g, motif, horodate, par)` | table `local_decisions (product_id, offered, min_weight_g, reason, decided_at, decided_by)` |
-| table `signalements (import_id, ligne_csv, produit_id, code, issue, message, valeur)` | table `findings (import_id, csv_line, product_id, code, issue, message, value)` |
+| table `signalements (import_id, ligne_csv, produit_id, nom_produit, code, issue, message, valeur)` | table `findings (import_id, csv_line, product_id, product_name, code, issue, message, value)` |
| `signalements.issue IN ('anomalie','information')` | `findings.issue IN ('anomaly','info')` |
| `idx_signalements_import` | `idx_findings_import` |
| table `pesees` | table `weighings` |
diff --git a/internal/catalog/csvodoo/csvodoo.go b/internal/catalog/csvodoo/csvodoo.go
index 8edac5d..6cd2efc 100644
--- a/internal/catalog/csvodoo/csvodoo.go
+++ b/internal/catalog/csvodoo/csvodoo.go
@@ -396,11 +396,12 @@ func (o Options) checkReadable(batch *ports.Batch) error {
// flv_1.csv.
func duplicateID(row catalog.Row, first int) domain.Finding {
return domain.Finding{
- CSVLine: row.Line,
- ProductID: row.ID,
- Code: domain.FindingUnreadableRow,
- Issue: domain.IssueAnomaly,
- Value: row.ID,
+ CSVLine: row.Line,
+ ProductID: row.ID,
+ ProductName: row.Name,
+ Code: domain.FindingUnreadableRow,
+ Issue: domain.IssueAnomaly,
+ Value: row.ID,
Message: fmt.Sprintf("Corriger l'identifiant : « %s » est déjà porté par la ligne "+
"%d. L'identifiant Odoo est la clé du produit — deux lignes qui le partagent "+
"font disparaître la première sans un mot.", row.ID, first),
diff --git a/internal/catalog/findings.go b/internal/catalog/findings.go
index 0147349..a72ce4a 100644
--- a/internal/catalog/findings.go
+++ b/internal/catalog/findings.go
@@ -13,8 +13,9 @@ import (
// The structure is imposed by the type and not by the goodwill of whoever writes the
// message (§10.3 bis). Each one answers three questions in this order:
//
-// WHERE — the Odoo id and the line, carried by the Finding itself, so that
-// somebody can open the record without searching;
+// WHERE — the Odoo id, the NAME and the line, carried by the Finding itself, so
+// that somebody can open the record without searching — and recognise the
+// product before they do;
// WHAT — the action, in the imperative, with the value that is expected;
// WHY — the concrete consequence, in the French of a shop.
//
@@ -29,11 +30,12 @@ import (
// healthy catalog (§10.4a).
func unreadableRow(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingUnreadableRow,
- Issue: domain.IssueAnomaly,
- Value: r.ID + " / " + r.Name,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingUnreadableRow,
+ Issue: domain.IssueAnomaly,
+ Value: r.ID + " / " + r.Name,
Message: "Corriger la ligne : elle doit porter un identifiant et un nom. " +
"Sans les deux, ce n'est pas un produit mais du texte, et un fichier " +
"dont trop de lignes sont dans ce cas est refusé en entier.",
@@ -43,11 +45,12 @@ func unreadableRow(r Row) domain.Finding {
// priceUnreadable reports a price that is not a number this application can use.
func priceUnreadable(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingPriceUnreadable,
- Issue: domain.IssueAnomaly,
- Value: r.Price,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingPriceUnreadable,
+ Issue: domain.IssueAnomaly,
+ Value: r.Price,
Message: fmt.Sprintf("Corriger le prix : « %s » n'est pas un nombre exploitable "+
"(au plus deux décimales, au plus %s €, ni signe ni espace). "+
"On ne met pas un prix inventé sur une étiquette : sans prix lisible, "+
@@ -58,11 +61,12 @@ func priceUnreadable(r Row) domain.Finding {
// zeroPrice reports a product at 0,00 €.
func zeroPrice(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingZeroPrice,
- Issue: domain.IssueAnomaly,
- Value: r.Price,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingZeroPrice,
+ Issue: domain.IssueAnomaly,
+ Value: r.Price,
Message: "Renseigner le prix : il vaut zéro. Une étiquette à 0,00 € passe en " +
"caisse sans rien facturer, et le client repart avec la marchandise.",
}
@@ -71,10 +75,11 @@ func zeroPrice(r Row) domain.Finding {
// noBarcode reports an article the till could not read. It is NOT a defect.
func noBarcode(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingNoBarcode,
- Issue: domain.IssueInfo,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingNoBarcode,
+ Issue: domain.IssueInfo,
Message: "Rien à corriger si ce produit ne se pèse pas : sans code-barres, il " +
"n'est pas référencé en caisse et n'a donc pas de tuile. " +
"Lui donner un code-barres de pesée dans Odoo suffirait à le proposer.",
@@ -88,11 +93,12 @@ func noBarcode(r Row) domain.Finding {
// offending codes of flv_1.csv are one digit away from being right.
func invalidBarcode(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingInvalidBarcode,
- Issue: domain.IssueAnomaly,
- Value: r.Barcode,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingInvalidBarcode,
+ Issue: domain.IssueAnomaly,
+ Value: r.Barcode,
Message: fmt.Sprintf("Corriger le code-barres « %s » : %s. "+
"Un scanner de caisse le refusera.", r.Barcode, whyNotEAN13(r.Barcode)),
}
@@ -118,11 +124,12 @@ func whyNotEAN13(code string) string {
// is wrong.
func prepackaged(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingPrepackagedProduct,
- Issue: domain.IssueInfo,
- Value: r.Barcode,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingPrepackagedProduct,
+ Issue: domain.IssueInfo,
+ Value: r.Barcode,
Message: fmt.Sprintf("Rien à corriger : « %s » est un code fournisseur. "+
"Le produit porte déjà son propre code-barres et n'a aucune raison d'être "+
"pesé, donc pas de tuile.", r.Barcode),
@@ -135,11 +142,12 @@ func prepackaged(r Row) domain.Finding {
// number, so it is fixable in Odoo (§10.3).
func internalCode(r Row) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingInternalCodeNotWeighable,
- Issue: domain.IssueInfo,
- Value: r.Barcode,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingInternalCodeNotWeighable,
+ Issue: domain.IssueInfo,
+ Value: r.Barcode,
Message: fmt.Sprintf("Corriger le code-barres dans Odoo si ce produit doit être "+
"pesé : « %s » est un code interne du magasin que la balance ne sait pas "+
"encoder. Les codes pesables commencent par 0493 à 0499.", r.Barcode),
@@ -159,11 +167,12 @@ func reservedZone(r Row, plan domain.PrefixPlan, reserved string) domain.Finding
field = "nombre d'unités"
}
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingReservedZoneNotEmpty,
- Issue: domain.IssueAnomaly,
- Value: r.Barcode,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingReservedZoneNotEmpty,
+ Issue: domain.IssueAnomaly,
+ Value: r.Barcode,
Message: fmt.Sprintf("Corriger le code-barres : les chiffres %d à 12 valent « %s » "+
"au lieu de « %s ». La référence déborde sur le champ %s : à l'impression, "+
"l'étiquette désignerait un autre article, avec son prix.",
@@ -181,11 +190,12 @@ func unitMismatch(r Row, plan domain.PrefixPlan) domain.Finding {
sold = "à l'unité"
}
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingUnitMismatch,
- Issue: domain.IssueInfo,
- Value: r.Barcode,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingUnitMismatch,
+ Issue: domain.IssueInfo,
+ Value: r.Barcode,
Message: fmt.Sprintf("Corriger l'unité dans Odoo : elle annonce une grandeur %s "+
"alors que le code-barres « %s » vend %s. Le code-barres fait foi — c'est la "+
"seule des deux informations que la caisse lit —, le produit reste proposé et "+
@@ -196,11 +206,12 @@ func unitMismatch(r Row, plan domain.PrefixPlan) domain.Finding {
// unknownUnit reports a wording that is none of the three the exchange format uses.
func unknownUnit(r Row, plan domain.PrefixPlan) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingUnknownUnit,
- Issue: domain.IssueInfo,
- Value: r.PriceSuffix,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingUnknownUnit,
+ Issue: domain.IssueInfo,
+ Value: r.PriceSuffix,
Message: fmt.Sprintf("Corriger l'unité dans Odoo : elle ne vaut ni « kg », ni "+
"« Litre(s) », ni « Unité(s) ». Le produit reste proposé, avec le libellé de "+
"prix par défaut de son préfixe («%s »).", plan.PriceLabel),
@@ -231,11 +242,12 @@ func UnexpectedHeader(got, want []string) domain.Finding {
// is no scenario where the grid is empty because of an unexpected category (§10.2 bis).
func UnknownCategory(r Row, letter, fallback string) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingUnknownCategory,
- Issue: domain.IssueInfo,
- Value: letter,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingUnknownCategory,
+ Issue: domain.IssueInfo,
+ Value: letter,
Message: fmt.Sprintf("Corriger la catégorie dans Odoo : « %s » n'est ni F, ni L, "+
"ni V, ni A. Le produit est rangé dans « %s » et reste proposé.",
letter, fallback),
@@ -248,11 +260,12 @@ func UnknownCategory(r Row, letter, fallback string) domain.Finding {
// photo. Half the real catalog has no photo anyway (§10.7).
func ImageInvalid(r Row, why string) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingImageInvalid,
- Issue: domain.IssueInfo,
- Value: why,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingImageInvalid,
+ Issue: domain.IssueInfo,
+ Value: why,
Message: fmt.Sprintf("Remplacer la photo dans Odoo : %s. Elle n'est ni JPEG, ni "+
"PNG, ni GIF, ni BMP, donc elle n'est pas enregistrée. Le produit garde sa "+
"tuile, sans photo.", why),
@@ -266,11 +279,12 @@ func ImageInvalid(r Row, why string) domain.Finding {
// photo (§10.7b).
func ImageTooLarge(r Row, why string) domain.Finding {
return domain.Finding{
- CSVLine: r.Line,
- ProductID: r.ID,
- Code: domain.FindingImageTooLarge,
- Issue: domain.IssueInfo,
- Value: why,
+ CSVLine: r.Line,
+ ProductID: r.ID,
+ ProductName: r.Name,
+ Code: domain.FindingImageTooLarge,
+ Issue: domain.IssueInfo,
+ Value: why,
Message: fmt.Sprintf("Réduire la photo dans Odoo : %s. Elle n'est pas "+
"enregistrée ; le produit garde sa tuile, sans photo.", why),
}
diff --git a/internal/catalog/qualify_test.go b/internal/catalog/qualify_test.go
index 7b6b2d5..e301c2f 100644
--- a/internal/catalog/qualify_test.go
+++ b/internal/catalog/qualify_test.go
@@ -305,7 +305,8 @@ func TestEveryMessageIsFrenchImperativeAndNamesTheConsequence(t *testing.T) {
func(r *catalog.Row) { r.Magnitude = catalog.Discrete },
func(r *catalog.Row) { r.Magnitude = catalog.MagnitudeUnknown },
} {
- _, findings, _ := catalog.Qualify(row(broken))
+ faulty := row(broken)
+ _, findings, _ := catalog.Qualify(faulty)
if len(findings) != 1 {
t.Fatalf("%d signalements", len(findings))
}
@@ -315,6 +316,13 @@ func TestEveryMessageIsFrenchImperativeAndNamesTheConsequence(t *testing.T) {
t.Errorf("%s : ligne %d, attendu 42 — le OÙ", f.Code, f.CSVLine)
case f.ProductID == "" && f.Code != domain.FindingUnexpectedHeader:
t.Errorf("%s : sans identifiant Odoo — le OÙ", f.Code)
+ // The name is part of the WHERE, and it is the row's own: « 4412 » is a number
+ // somebody looks up before starting, « AIL VIOLET SAF » is the product they know.
+ // Comparing against the row also covers the one case where it is legitimately
+ // empty — a line so damaged it carries no name, which is UNREADABLE_ROW itself.
+ case f.ProductName != faulty.Name:
+ t.Errorf("%s : nom %q, attendu %q — le OÙ nomme aussi le produit",
+ f.Code, f.ProductName, faulty.Name)
case !strings.HasSuffix(f.Message, "."):
t.Errorf("%s : « %s » n'est pas une phrase", f.Code, f.Message)
case len(strings.Fields(f.Message)) < 12:
diff --git a/internal/domain/journal.go b/internal/domain/journal.go
index 8d16dc4..9ceb2d7 100644
--- a/internal/domain/journal.go
+++ b/internal/domain/journal.go
@@ -203,8 +203,18 @@ type Finding struct {
// ProductID may be empty: UNEXPECTED_HEADER and UNKNOWN_CATEGORY bear on no
// product in particular.
ProductID string
- Code string
- Issue string
+ // ProductName is the commercial name AS THIS IMPORT READ IT, and it is a display
+ // snapshot for the same reason weighings carry one: the name moves in Odoo, the row
+ // that describes an import does not. A report that names « TOMATE GRAPPE » is opened
+ // in Odoo by somebody who recognises the product; one that names « 4412 » is a
+ // number to look up first.
+ //
+ // It is empty in the two cases where the file gives none: a finding that bears on no
+ // product, and a row so damaged it carries no name at all — which is exactly what
+ // UNREADABLE_ROW says.
+ ProductName string
+ Code string
+ Issue string
// Message is FRENCH, imperative, and names the consequence in shop language.
Message string
// Value is the offending value, so nobody has to guess which digit is wrong.
diff --git a/internal/store/imports.go b/internal/store/imports.go
index e6f9688..9f517c5 100644
--- a/internal/store/imports.go
+++ b/internal/store/imports.go
@@ -56,8 +56,8 @@ func insertFindings(ctx context.Context, tx *sql.Tx, importID int64, findings []
return nil
}
stmt, err := tx.PrepareContext(ctx, `
- INSERT INTO findings (import_id, csv_line, product_id, code, issue, message, value)
- VALUES (?, ?, ?, ?, ?, ?, ?)`)
+ INSERT INTO findings (import_id, csv_line, product_id, product_name, code, issue, message, value)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
if err != nil {
return err
}
@@ -65,9 +65,12 @@ func insertFindings(ctx context.Context, tx *sql.Tx, importID int64, findings []
for _, f := range findings {
// product_id is nullable and left NULL rather than '': UNEXPECTED_HEADER and
- // UNKNOWN_CATEGORY bear on no product in particular.
+ // UNKNOWN_CATEGORY bear on no product in particular. product_name is a display
+ // SNAPSHOT and is stored as plain text, empty included: it is the name this import
+ // read, not a reference to a row that may not exist -- a rejected batch writes no
+ // product at all, and its findings are exactly the ones that need naming.
if _, err := stmt.ExecContext(ctx, importID, f.CSVLine, nullString(f.ProductID),
- f.Code, f.Issue, f.Message, f.Value); err != nil {
+ f.ProductName, f.Code, f.Issue, f.Message, f.Value); err != nil {
return fmt.Errorf("signalement %s (ligne CSV %d) : %w", f.Code, f.CSVLine, err)
}
}
@@ -133,7 +136,7 @@ func (d *DB) LastAppliedImport(ctx context.Context) (domain.Import, error) {
// like the file it describes.
func (d *DB) Findings(ctx context.Context, importID int64) ([]domain.Finding, error) {
rows, err := d.reader.QueryContext(ctx, `
- SELECT import_id, csv_line, product_id, code, issue, message, value
+ SELECT import_id, csv_line, product_id, product_name, code, issue, message, value
FROM findings
WHERE import_id = ?
ORDER BY CASE issue WHEN 'anomaly' THEN 0 ELSE 1 END, csv_line`, importID)
@@ -148,7 +151,8 @@ func (d *DB) Findings(ctx context.Context, importID int64) ([]domain.Finding, er
f domain.Finding
productID sql.NullString
)
- if err := rows.Scan(&f.ImportID, &f.CSVLine, &productID, &f.Code, &f.Issue, &f.Message, &f.Value); err != nil {
+ if err := rows.Scan(&f.ImportID, &f.CSVLine, &productID, &f.ProductName,
+ &f.Code, &f.Issue, &f.Message, &f.Value); err != nil {
return nil, err
}
if productID.Valid {
diff --git a/internal/store/imports_test.go b/internal/store/imports_test.go
index 0816fd6..01579a2 100644
--- a/internal/store/imports_test.go
+++ b/internal/store/imports_test.go
@@ -86,9 +86,9 @@ func TestFindingsAreAWorkPlan(t *testing.T) {
SHA256: "sha-rejet", RowsRead: 355, Anomalies: 16, UnitMismatches: 1,
Result: domain.ImportRejected, Code: "ERR-CAT-03", Reason: "contenu inexploitable",
}, []domain.Finding{
- {CSVLine: 300, ProductID: "5209", Code: domain.FindingUnitMismatch,
+ {CSVLine: 300, ProductID: "5209", ProductName: "OEUFS PLEIN AIR", Code: domain.FindingUnitMismatch,
Issue: domain.IssueInfo, Message: "L'unité déclarée ne correspond pas au préfixe.", Value: "Unité(s)"},
- {CSVLine: 12, ProductID: "20", Code: domain.FindingReservedZoneNotEmpty,
+ {CSVLine: 12, ProductID: "20", ProductName: "TOMME DE SAVOIE", Code: domain.FindingReservedZoneNotEmpty,
Issue: domain.IssueAnomaly, Message: "Corrigez ce code dans Odoo.", Value: "0493100100006"},
{CSVLine: 1, Code: domain.FindingUnexpectedHeader,
Issue: domain.IssueInfo, Message: "En-tête inattendu, colonnes reconnues par leur position."},
@@ -117,6 +117,19 @@ func TestFindingsAreAWorkPlan(t *testing.T) {
if findings[1].ProductID != "" {
t.Errorf("ProductID = %q, want vide", findings[1].ProductID)
}
+ if findings[1].ProductName != "" {
+ t.Errorf("ProductName = %q, want vide", findings[1].ProductName)
+ }
+ // The NAME survives the round trip, and it is what makes the list of anomalies a work
+ // plan rather than a list of Odoo ids to look up first. It is stored on the finding
+ // and not read back from products: THIS import was rejected, so it wrote no product
+ // at all -- a name fetched from the catalog would be missing exactly here.
+ if findings[0].ProductName != "TOMME DE SAVOIE" {
+ t.Errorf("nom de l'anomalie = %q, want TOMME DE SAVOIE", findings[0].ProductName)
+ }
+ if findings[2].ProductName != "OEUFS PLEIN AIR" {
+ t.Errorf("nom de l'unité divergente = %q, want OEUFS PLEIN AIR", findings[2].ProductName)
+ }
// The rejected import kept its code and its reason.
history, err := db.Imports(ctx, 1, 0)
if err != nil {
diff --git a/internal/store/migrations/0002_findings_product_name.sql b/internal/store/migrations/0002_findings_product_name.sql
new file mode 100644
index 0000000..db6937d
--- /dev/null
+++ b/internal/store/migrations/0002_findings_product_name.sql
@@ -0,0 +1,20 @@
+-- 0002 — le nom du produit dans les signalements d'import.
+--
+-- Les trois listes de la page Catalogue — anomalies, unités divergentes, produits non
+-- pesables — ne nommaient les produits que par leur identifiant Odoo. Corriger « 4412 »
+-- demande de chercher d'abord de quel produit il s'agit ; corriger « TOMATE GRAPPE »
+-- commence tout de suite. Le nom est ce qui fait la différence entre une liste de codes
+-- et un plan de travail (§10.3 bis).
+--
+-- C'est un INSTANTANÉ D'AFFICHAGE, comme weighings.product_name et pour la même raison :
+-- le nom bouge dans Odoo, la ligne qui décrit un import de mars ne bouge pas. Un affichage
+-- qui irait chercher le nom courant dans products montrerait, des signalements d'un import
+-- passé, un fait que cet import n'a pas dit — et n'en montrerait aucun pour un lot refusé,
+-- dont aucun produit n'est jamais entré en base.
+--
+-- DEFAULT '' et non NULL : le champ est vide dans les deux cas où le fichier ne donne pas
+-- de nom — un signalement qui ne porte sur aucun produit (UNEXPECTED_HEADER), et une ligne
+-- si abîmée qu'elle n'en a pas, ce qu'UNREADABLE_ROW dit déjà. Les signalements écrits
+-- avant cette migration prennent la même valeur vide : l'écran l'annonce plutôt que
+-- d'inventer un nom.
+ALTER TABLE findings ADD COLUMN product_name TEXT NOT NULL DEFAULT '';
diff --git a/internal/store/store.go b/internal/store/store.go
index 914f112..59ec974 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -72,9 +72,10 @@ type DB struct {
clock Clock
// migrationSource is the embedded FS in production. It is a field, not the package
- // variable used directly, for one testable reason: with a single shipped migration
- // no value of user_version can satisfy 0 < v < len(files), so the "back up an
- // EXISTING base before migrating it" branch of §12.5 would have no test.
+ // variable used directly, so that a test can add a script PAST the shipped set: the
+ // "back up an EXISTING base before migrating it" branch of §12.5 needs a base at
+ // version N and a binary that knows N+1, which is the update a station lives through
+ // and which no fixed migration set can stage against itself.
migrationSource fs.FS
retention atomic.Pointer[Retention]
diff --git a/internal/store/store_test.go b/internal/store/store_test.go
index bdd4f6c..28d55a4 100644
--- a/internal/store/store_test.go
+++ b/internal/store/store_test.go
@@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
+ "io/fs"
"os"
"path/filepath"
"strings"
@@ -57,8 +58,10 @@ func TestOpenAppliesEveryMigration(t *testing.T) {
if err != nil {
t.Fatalf("SchemaVersion: %v", err)
}
- if v != 1 {
- t.Fatalf("user_version = %d, want 1", v)
+ // MigrationCount and not a literal: the number grows with every shipped script, and a
+ // test that had to be edited alongside the schema is a test that gets edited to pass.
+ if v != MigrationCount() {
+ t.Fatalf("user_version = %d, want %d", v, MigrationCount())
}
if _, err := os.Stat(db.Path()); err != nil {
t.Fatalf("le fichier de base n'existe pas : %v", err)
@@ -130,8 +133,8 @@ func TestOpenTwiceMigratesNothingAndBacksUpNothing(t *testing.T) {
if err != nil {
t.Fatalf("SchemaVersion: %v", err)
}
- if v != 1 {
- t.Fatalf("user_version = %d, want 1", v)
+ if v != MigrationCount() {
+ t.Fatalf("user_version = %d, want %d", v, MigrationCount())
}
// No pending migration means nothing to lose, hence no copy: a backup on every
// start would fill a station's disk with identical files.
@@ -167,9 +170,14 @@ func TestOpenRefusesSchemaFromNewerVersion(t *testing.T) {
}
}
-// TestMigrateBacksUpBeforeTouchingAnExistingBase exercises the branch of §12.5 that no
-// shipped migration set can reach: with a single file, no user_version satisfies
-// 0 < v < len(files).
+// TestMigrateBacksUpBeforeTouchingAnExistingBase exercises the branch of §12.5 a station
+// reaches on the update that carries a new script: 0 < user_version < len(files), so
+// there is an N-1 state worth a copy before anything is touched.
+//
+// The synthetic script is numbered PAST the shipped set. Slipping one in the middle would
+// have migrate() re-apply an already-applied file — the loop walks files[v:] by index,
+// not by name — and the test would fail on a duplicate table rather than on what it
+// checks.
func TestMigrateBacksUpBeforeTouchingAnExistingBase(t *testing.T) {
clk := newClock(TestEpoch)
db, path := openAt(t, clk)
@@ -181,16 +189,17 @@ func TestMigrateBacksUpBeforeTouchingAnExistingBase(t *testing.T) {
t.Fatalf("Close: %v", err)
}
+ next := MigrationCount() + 1
clk.Advance(time.Hour)
upgraded := reopenWithMigrations(t, path, clk, map[string]string{
- "0002_add_marker.sql": `CREATE TABLE marker (id INTEGER PRIMARY KEY) STRICT;`,
+ fmt.Sprintf("%04d_add_marker.sql", next): `CREATE TABLE marker (id INTEGER PRIMARY KEY) STRICT;`,
})
defer upgraded.Close()
- if v, err := upgraded.SchemaVersion(); err != nil || v != 2 {
- t.Fatalf("user_version = %d (err %v), want 2", v, err)
+ if v, err := upgraded.SchemaVersion(); err != nil || v != next {
+ t.Fatalf("user_version = %d (err %v), want %d", v, err, next)
}
- want := fmt.Sprintf("%s.before-v2-%s", path, clk.Now().UTC().Format(backupStampLayout))
+ want := fmt.Sprintf("%s.before-v%d-%s", path, next, clk.Now().UTC().Format(backupStampLayout))
if _, err := os.Stat(want); err != nil {
copies, _ := filepath.Glob(path + ".before-v*")
t.Fatalf("sauvegarde %s absente (présentes : %v)", want, copies)
@@ -221,8 +230,11 @@ func TestKeepLastBackupsKeepsThree(t *testing.T) {
}
// Five successive migrations, one hour apart so that each backup gets its own name.
+ // They are numbered past the shipped set, for the reason the test above states.
+ first := MigrationCount() + 1
+ last := first + 4
scripts := map[string]string{}
- for v := 2; v <= 6; v++ {
+ for v := first; v <= last; v++ {
clk.Advance(time.Hour)
scripts[fmt.Sprintf("%04d_step.sql", v)] = fmt.Sprintf("CREATE TABLE step%d (id INTEGER PRIMARY KEY) STRICT;", v)
next := reopenWithMigrations(t, path, clk, scripts)
@@ -241,8 +253,9 @@ func TestKeepLastBackupsKeepsThree(t *testing.T) {
if len(copies) != backupsKept {
t.Fatalf("%d sauvegardes conservées, want %d : %v", len(copies), backupsKept, copies)
}
- // The three kept are the three most recent: v4, v5, v6.
- for _, want := range []string{".before-v4-", ".before-v5-", ".before-v6-"} {
+ // The three kept are the three most recent.
+ for v := last - backupsKept + 1; v <= last; v++ {
+ want := fmt.Sprintf(".before-v%d-", v)
if !slicesContainsSubstring(copies, want) {
t.Errorf("la sauvegarde %s manque parmi %v", want, copies)
}
@@ -336,15 +349,26 @@ func TestOpenCreatesTheDataDirectory(t *testing.T) {
}
}
-// reopenWithMigrations opens path with a synthetic migration set: the shipped script
+// reopenWithMigrations opens path with a synthetic migration set: EVERY shipped script
// plus the extra ones the test needs.
+//
+// Every one of them, and not just the first: the base was created by the real Open, so a
+// set that dropped a shipped script would present the file as ahead of the binary — the
+// ERR-DB-02 branch — instead of exercising the upgrade the test is about.
func reopenWithMigrations(t *testing.T, path string, clk Clock, extra map[string]string) *DB {
t.Helper()
- shipped, err := migrationScripts.ReadFile("migrations/0001_initial.sql")
- if err != nil {
- t.Fatalf("lecture de la migration livrée : %v", err)
+ shipped, err := fs.Glob(migrationScripts, "migrations/*.sql")
+ if err != nil || len(shipped) == 0 {
+ t.Fatalf("lecture des migrations livrées : %d fichier(s), err %v", len(shipped), err)
+ }
+ fsys := fstest.MapFS{}
+ for _, name := range shipped {
+ script, err := migrationScripts.ReadFile(name)
+ if err != nil {
+ t.Fatalf("lecture de %s : %v", name, err)
+ }
+ fsys[name] = &fstest.MapFile{Data: script}
}
- fsys := fstest.MapFS{"migrations/0001_initial.sql": {Data: shipped}}
for name, script := range extra {
fsys["migrations/"+name] = &fstest.MapFile{Data: []byte(script)}
}
diff --git a/internal/web/admin.go b/internal/web/admin.go
index 02fc829..fbfce63 100644
--- a/internal/web/admin.go
+++ b/internal/web/admin.go
@@ -352,14 +352,17 @@ func importOf(record domain.Import) importDTO {
// findingDTO is one row an import had something to say about.
//
// CSVLine is what makes the report usable: it names the row to fix IN ODOO, which is
-// the only place anybody can fix it.
+// the only place anybody can fix it. ProductName is what makes it readable: it is the
+// name the import itself read, and the screen shows it rather than send whoever corrects
+// the file looking up an Odoo id first.
type findingDTO struct {
- CSVLine int `json:"csv_line"`
- ProductID string `json:"product_id"`
- Code string `json:"code"`
- Issue string `json:"issue"`
- Message string `json:"message"`
- Value string `json:"value"`
+ CSVLine int `json:"csv_line"`
+ ProductID string `json:"product_id"`
+ ProductName string `json:"product_name"`
+ Code string `json:"code"`
+ Issue string `json:"issue"`
+ Message string `json:"message"`
+ Value string `json:"value"`
}
// findingsOf converts what one import reported.
@@ -367,8 +370,8 @@ func findingsOf(findings []domain.Finding) []findingDTO {
out := make([]findingDTO, 0, len(findings))
for _, f := range findings {
out = append(out, findingDTO{
- CSVLine: f.CSVLine, ProductID: f.ProductID, Code: f.Code,
- Issue: f.Issue, Message: f.Message, Value: f.Value,
+ CSVLine: f.CSVLine, ProductID: f.ProductID, ProductName: f.ProductName,
+ Code: f.Code, Issue: f.Issue, Message: f.Message, Value: f.Value,
})
}
return out
diff --git a/internal/web/admin_test.go b/internal/web/admin_test.go
index 3a6c9f9..dd3c8a7 100644
--- a/internal/web/admin_test.go
+++ b/internal/web/admin_test.go
@@ -244,7 +244,8 @@ func TestTheTechnicalJournalAndTheImportsAreServed(t *testing.T) {
RowsRead: 355, Weighable: 331, NotWeighable: 8, Anomalies: 16,
}}
b.store.findings[7] = []domain.Finding{{
- CSVLine: 42, ProductID: "4412", Code: "RESERVED_ZONE_NOT_EMPTY",
+ CSVLine: 42, ProductID: "4412", ProductName: "AIL VIOLET SAF",
+ Code: "RESERVED_ZONE_NOT_EMPTY",
Message: "Corrigez le code-barres dans Odoo.", Value: "0493021012365",
}}
@@ -263,6 +264,11 @@ func TestTheTechnicalJournalAndTheImportsAreServed(t *testing.T) {
if len(got.Findings) != 1 || got.Findings[0].CSVLine != 42 {
t.Fatalf("signalements = %+v", got.Findings)
}
+ // The name travels with the finding, and the screen shows it: a list that names « 4412 »
+ // sends whoever corrects the file looking the product up in Odoo before they can start.
+ if got.Findings[0].ProductName != "AIL VIOLET SAF" {
+ t.Errorf("nom servi = %q, want AIL VIOLET SAF", got.Findings[0].ProductName)
+ }
}
// TestADecisionIsRecordedAndThenForgotten (§10.6, ADR-017).
diff --git a/internal/web/dist/admin.html b/internal/web/dist/admin.html
index 4e1b2b7..215180c 100644
--- a/internal/web/dist/admin.html
+++ b/internal/web/dist/admin.html
@@ -5,9 +5,9 @@
Administration
-
+
-
+
diff --git a/internal/web/dist/assets/admin-DszX94Lx.js b/internal/web/dist/assets/admin-DszX94Lx.js
deleted file mode 100644
index 0b5b47f..0000000
--- a/internal/web/dist/assets/admin-DszX94Lx.js
+++ /dev/null
@@ -1 +0,0 @@
-import"./app-Bm_67CUJ.js";import{mountAdmin as m}from"./mount-C-eWTYT5.js";m(document.getElementById("app"));
diff --git a/internal/web/dist/assets/admin-vulQ2X2t.js b/internal/web/dist/assets/admin-vulQ2X2t.js
new file mode 100644
index 0000000..6629d2b
--- /dev/null
+++ b/internal/web/dist/assets/admin-vulQ2X2t.js
@@ -0,0 +1 @@
+import"./app-Bm_67CUJ.js";import{mountAdmin as m}from"./mount-CEsnfFiz.js";m(document.getElementById("app"));
diff --git a/internal/web/dist/assets/index-Djcp4GbC.js b/internal/web/dist/assets/index-BBr6lCe2.js
similarity index 99%
rename from internal/web/dist/assets/index-Djcp4GbC.js
rename to internal/web/dist/assets/index-BBr6lCe2.js
index 89e28d6..865df85 100644
--- a/internal/web/dist/assets/index-Djcp4GbC.js
+++ b/internal/web/dist/assets/index-BBr6lCe2.js
@@ -1,2 +1,2 @@
-const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/mount-C-eWTYT5.js","assets/app-Bm_67CUJ.js","assets/app-DWjtdCrW.css","assets/mount-CTNNPuRa.css"])))=>i.map(i=>d[i]);
-import{e as Te,u as qe,c as Re,r as Oe,a as We,D as Be,S as He,d as le,p as ne,b as T,i as B,I as ie,t as q,f as H,g as m,h as ae,j as y,k as t,s as p,l as oe,m as ce,n as te,o as z,q as h,v as u,w as ze,x as fe,y as Fe,z as x,A as j,B as ue,C as Ue,E as Ge,F as Ne,G as Ve,H as Xe,J as Ke,K as Ze,L as Je,M as Ye,N as Qe,O as $e,P as et,Q as tt}from"./app-Bm_67CUJ.js";class be{#e=new WeakMap;#t;#n;static entries=new WeakMap;constructor(e){this.#n=e}observe(e,a){var r=this.#e.get(e)||new Set;return r.add(a),this.#e.set(e,r),this.#r().observe(e,this.#n),()=>{var i=this.#e.get(e);i.delete(a),i.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#r(){return this.#t??(this.#t=new ResizeObserver(e=>{for(var a of e){be.entries.set(a.target,a);for(var r of this.#e.get(a.target)||[])r(a)}}))}}var nt=new be({box:"border-box"});function we(n,e,a){var r=nt.observe(n,()=>a(n[e]));Te(()=>(qe(()=>a(n[e])),r))}function he(n,e){return n===e||n?.[He]===e}function Me(n={},e,a,r){var i=Re.r,c=We;return Te(()=>{var d,s;return Oe(()=>{d=s,s=[],qe(()=>{he(a(...s),n)||(e(n,...s),d&&he(a(...d),n)&&e(null,...d))})}),()=>{let f=c;for(;f!==i&&f.parent!==null&&f.parent.f&Be;)f=f.parent;const v=()=>{s&&he(a(...s),n)&&e(null,...s)},l=f.teardown;f.teardown=()=>{v(),l?.()}}}),n}const at="modulepreload",rt=function(n){return"/"+n},Ee={},st=function(e,a,r){let i=Promise.resolve();if(a&&a.length>0){let f=function(v){return Promise.all(v.map(l=>Promise.resolve(l).then(E=>({status:"fulfilled",value:E}),E=>({status:"rejected",reason:E}))))};document.getElementsByTagName("link");const d=document.querySelector("meta[property=csp-nonce]"),s=d?.nonce||d?.getAttribute("nonce");i=f(a.map(v=>{if(v=rt(v),v in Ee)return;Ee[v]=!0;const l=v.endsWith(".css"),E=l?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${v}"]${E}`))return;const A=document.createElement("link");if(A.rel=l?"stylesheet":at,l||(A.as="script"),A.crossOrigin="",A.href=v,s&&A.setAttribute("nonce",s),document.head.appendChild(A),l)return new Promise((w,M)=>{A.addEventListener("load",w),A.addEventListener("error",()=>M(new Error(`Unable to preload CSS for ${v}`)))})}))}function c(d){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=d,window.dispatchEvent(s),!s.defaultPrevented)throw d}return i.then(d=>{for(const s of d||[])s.status==="rejected"&&c(s.reason);return e().catch(c)})};var it=y('
');function ln(n,e){ne(e,!0);const a=["1","2","3","4","5","6","7","8","9","0"];var r=on(),i=u(r),c=p(u(i),2),d=u(c),s=p(i,2);fe(s,20,()=>a,w=>w,(w,M)=>{var g=sn(),k=u(g);q(()=>z(k,M)),H("click",g,()=>e.ondigit(M)),m(w,g)});var f=p(s,2),v=u(f),l=p(v,2),E=p(l,2),A=u(E);ie(A,{name:"check",size:"1.5rem"}),q(()=>z(d,e.grams===""?"0":e.grams)),H("click",v,function(...w){e.onclear?.apply(this,w)}),H("click",l,function(...w){e.oncancel?.apply(this,w)}),H("click",E,function(...w){e.onconfirm?.apply(this,w)}),m(n,r),ae()}le(["click"]);function cn(n){return n===null||n.state==="initializing"?!1:n.scale.connected&&n.printer.health!=="faulted"}const Ae=1500,un=2e3,Ie=5e3;function dn(n,e){return e?n>=Ie?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!0}:n>=un?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!1}:n>=Ae?{showWeight:!1,banner:"",mustReconnect:!1}:{showWeight:!0,banner:"",mustReconnect:!1}:{showWeight:n=Ie}}const vn=250,fn={show_grid_prices:!0,idle_timeout_s:45,reprint_window_s:60,sound:!0,show_by_unit_products:!1};class gn{#e=j(null);get state(){return t(this.#e)}set state(e){x(this.#e,e,!0)}#t=j(null);get catalog(){return t(this.#t)}set catalog(e){x(this.#t,e,!0)}#n=j(Ne({showWeight:!0,banner:"",mustReconnect:!1}));get link(){return t(this.#n)}set link(e){x(this.#n,e,!0)}#r=j("");get catalogError(){return t(this.#r)}set catalogError(e){x(this.#r,e,!0)}#s=null;#i=null;#o=Date.now();#a=!1;#l=!1;get presentation(){return this.catalog?.presentation??fn}start(){this.#u(),this.#c(),this.#i=setInterval(()=>this.#v(),vn)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null,this.#s?.close(),this.#s=null,this.#a=!1}#c(){const e=new EventSource("/api/v1/stream");this.#s=e,e.onopen=()=>{this.#a=!0,this.#o=Date.now()},e.addEventListener("state",a=>this.#d(a)),e.onerror=()=>{this.#a=!1}}#d(e){this.#o=Date.now(),this.#a=!0;const a=JSON.parse(e.data);this.state=a,this.catalog!==null&&a.catalog_count!==this.catalog.product_count&&this.#u()}#v(){const e=dn(Date.now()-this.#o,this.#a);this.link=e,e.mustReconnect&&(this.#s?.close(),this.#o=Date.now(),this.#c())}async#u(){if(!this.#l){this.#l=!0;try{this.catalog=await Ve(),this.catalogError=""}catch{this.catalog===null&&(this.catalogError="Catalogue indisponible. Prévenez un responsable.")}finally{this.#l=!1}}}}const Se="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Pe(){let n=Date.now();const e=new Array(26);for(let r=9;r>=0;r--)e[r]=Se[n%32],n=Math.floor(n/32);const a=new Uint8Array(16);crypto.getRandomValues(a);for(let r=0;r<16;r++)e[10+r]=Se[a[r]%32];return e.join("")}var hn=y('');function pn(n,e){ne(e,!0);const a=new gn;let r=j(Ne(Xe)),i=j(""),c=j(!1);const d=h(()=>t(i)!==""||t(c));let s=j(null),f=j(-1),v=j(null);const l=h(()=>a.catalog),E=h(()=>t(l)===null?[]:Qe(t(l))),A=h(()=>t(l)===null?[]:Je(t(l))),w=h(()=>Ze(t(E),t(r),t(i))),M=h(()=>{const o={};for(const U of t(l)?.categories??[])o[U.code]=U.color;return o}),g=h(()=>a.state),k=h(()=>a.presentation),O=h(()=>cn(t(g))),C=h(()=>{if(a.catalogError!=="")return{message:a.catalogError,hint:"Le poste réessaie tout seul."};if(t(E).length>0)return{message:"Aucun produit ne correspond.",hint:"Effacez des lettres ou changez de rayon."};if(t(l)!==null&&t(l).products.length>0)return{message:t(l).products.every(ve=>ve.mode==="by_unit")&&!t(k).show_by_unit_products?"Aucun produit à montrer : ce poste masque les produits vendus à l’unité, et le catalogue reçu ne contient que ceux-là.":"Aucun produit à montrer : les réglages d’affichage de ce poste masquent tout ce qu’il a reçu.",hint:"Le fichier est bien arrivé : prévenez un responsable, c’est un réglage de ce poste qui vide la grille."};const o=t(g)?.station??0;return{message:o>0?`Catalogue vide. En attente du fichier flv_${o}.csv.`:"Catalogue vide. En attente du fichier du poste.",hint:"Prévenez un responsable : aucun produit ne peut être pesé."}}),X=h(()=>t(g)?.state==="rejected"?t(g).product?.id??null:null),G=h(()=>t(g)?.state==="faulted"||t(g)?.state==="out_of_service"),I=["product_armed","weight_present","weight_stable","awaiting_stability","validating","printing"],W=h(()=>t(g)!==null&&I.includes(t(g).state)?t(g).product?.id??null:null),F=h(()=>Zt(t(l)?.updated_at??""));ue(()=>(a.start(),()=>a.stop())),ue(()=>{const o=a.state?.revision??-1;t(s)!==null&&o>t(f)&&(x(s,null),x(f,-1))});async function K(o){if(t(s)===null){x(s,o.id,!0),x(f,a.state?.revision??-1,!0);try{await Ke({product_id:o.id,tare_g:a.state?.weight.tare_g??0,units:1,manual_weight_g:0,seen_weight_g:a.state?.weight.gross_g??0,measurement_seq:a.state?.weight.seq??0,key:Pe()})}catch{x(s,null)}}}async function V(){const o=a.state?.reprint.job_id;o===void 0||o===""||await Ye(o,Pe())}function Q(){x(i,""),x(c,!1)}function _(){x(c,!0)}function P(){const o=t(w).length===1?t(w)[0]:void 0;o!==void 0&&K(o)}function S(o){if(!(o.metaKey||o.ctrlKey||o.altKey)&&!(o.target instanceof HTMLElement&&o.target.tagName==="INPUT")&&!(t(v)!==null||t(G))){if(o.key==="Escape"){o.preventDefault(),Q();return}if(o.key==="Backspace"){o.preventDefault(),x(i,t(i).slice(0,-1),!0);return}if(o.key==="Enter"){P();return}o.key.length===1&&/[a-zA-Z0-9 ]/.test(o.key)&&(o.key===" "&&t(i)===""||(o.preventDefault(),x(i,t(i)+o.key)))}}ue(()=>(window.addEventListener("keydown",S),()=>window.removeEventListener("keydown",S)));async function N(){(await st(()=>import("./mount-C-eWTYT5.js"),__vite__mapDeps([0,1,2,3]))).mountAdmin(document.body)}var Z=hn(),J=u(Z);{let o=h(()=>t(v)!==null);ct(J,{get snapshot(){return t(g)},get showWeight(){return a.link.showWeight},get linkBanner(){return a.link.banner},get taring(){return t(o)},ontare:()=>x(v,"")})}var b=p(J,2);{var D=o=>{ln(o,{get grams(){return t(v)},ondigit:U=>x(v,(t(v)??"")+U),onclear:()=>x(v,""),oncancel:()=>x(v,null),onconfirm:()=>x(v,null)})};B(b,o=>{t(v)!==null&&o(D)})}var L=p(b,2);{let o=h(()=>t(l)?.pricing.primary_code??""),U=h(()=>Object.fromEntries((t(l)?.pricing.tiers??[]).map(ye=>[ye.code,ye.abbrev]))),ve=h(()=>t(l)===null&&a.catalogError==="");Gt(L,{get products(){return t(w)},get colors(){return t(M)},get primaryCode(){return t(o)},get tierAbbrev(){return t(U)},get selectedID(){return t(W)},get rejectedID(){return t(X)},get busyID(){return t(s)},get showPrices(){return t(k).show_grid_prices},get loading(){return t(ve)},get emptyMessage(){return t(C).message},get emptyHint(){return t(C).hint},onpick:K})}var R=p(L,2);{var ee=o=>{Kt(o,{get query(){return t(i)},onquery:U=>x(i,U,!0),onclose:Q,onenter:P})};B(R,o=>{t(d)&&o(ee)})}var Y=p(R,2);_t(Y,{get chips(){return t(A)},get active(){return t(r)},get searchFieldOpen(){return t(d)},onselect:o=>x(r,o,!0),onopensearch:_});var $=p(Y,2);{let o=h(()=>t(g)?.last_label??null),U=h(()=>t(g)?.reprint.available??!1),ve=h(()=>t(l)?.app_version??"");rn($,{get label(){return t(o)},get available(){return t(U)},get catalogAt(){return t(F)},get productCount(){return t(E).length},get appVersion(){return t(ve)},get healthy(){return t(O)},onreprint:V,onadmin:N})}var re=p($,2);{var se=o=>{{let U=h(()=>t(g).message?.text??"Prévenez un responsable.");ke(o,{title:"Poste indisponible",get detail(){return t(U)},get code(){return t(g).fault_code},action:{label:"J’ai compris",run:()=>{$e()}}})}},de=o=>{ke(o,{title:"Poste hors service",detail:"Ce poste ne peut pas peser. Prévenez un responsable.",get code(){return t(g).fault_code}})};B(re,o=>{t(g)?.state==="faulted"?o(se):t(g)?.state==="out_of_service"&&o(de,1)})}m(n,Z),ae()}const _n=5;mn();document.addEventListener("contextmenu",n=>{bn(n.target)||n.preventDefault()});document.addEventListener("dragstart",n=>n.preventDefault());et(pn,{target:document.getElementById("app")});function bn(n){return n instanceof Element&&n.closest("[data-admin]")!==null}function mn(){let n=!1;const e=(a,r)=>{n||(n=!0,tt(a,r),yn(),setTimeout(()=>location.reload(),_n*1e3))};window.addEventListener("error",a=>e(a.message,a.error?.stack??"")),window.addEventListener("unhandledrejection",a=>e(String(a.reason),a.reason?.stack??""))}function yn(){const n=document.createElement("div");n.className="fatal",n.append(_e("h1","Une erreur est survenue"),_e("p","L’écran va se recharger tout seul."),_e("p","ERR-UI-01","code")),document.body.appendChild(n)}function _e(n,e,a=""){const r=document.createElement(n);return r.textContent=e,a!==""&&(r.className=a),r}
+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/mount-CEsnfFiz.js","assets/app-Bm_67CUJ.js","assets/app-DWjtdCrW.css","assets/mount-CTNNPuRa.css"])))=>i.map(i=>d[i]);
+import{e as Te,u as qe,c as Re,r as Oe,a as We,D as Be,S as He,d as le,p as ne,b as T,i as B,I as ie,t as q,f as H,g as m,h as ae,j as y,k as t,s as p,l as oe,m as ce,n as te,o as z,q as h,v as u,w as ze,x as fe,y as Fe,z as x,A as j,B as ue,C as Ue,E as Ge,F as Ne,G as Ve,H as Xe,J as Ke,K as Ze,L as Je,M as Ye,N as Qe,O as $e,P as et,Q as tt}from"./app-Bm_67CUJ.js";class be{#e=new WeakMap;#t;#n;static entries=new WeakMap;constructor(e){this.#n=e}observe(e,a){var r=this.#e.get(e)||new Set;return r.add(a),this.#e.set(e,r),this.#r().observe(e,this.#n),()=>{var i=this.#e.get(e);i.delete(a),i.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#r(){return this.#t??(this.#t=new ResizeObserver(e=>{for(var a of e){be.entries.set(a.target,a);for(var r of this.#e.get(a.target)||[])r(a)}}))}}var nt=new be({box:"border-box"});function we(n,e,a){var r=nt.observe(n,()=>a(n[e]));Te(()=>(qe(()=>a(n[e])),r))}function he(n,e){return n===e||n?.[He]===e}function Me(n={},e,a,r){var i=Re.r,c=We;return Te(()=>{var d,s;return Oe(()=>{d=s,s=[],qe(()=>{he(a(...s),n)||(e(n,...s),d&&he(a(...d),n)&&e(null,...d))})}),()=>{let f=c;for(;f!==i&&f.parent!==null&&f.parent.f&Be;)f=f.parent;const v=()=>{s&&he(a(...s),n)&&e(null,...s)},l=f.teardown;f.teardown=()=>{v(),l?.()}}}),n}const at="modulepreload",rt=function(n){return"/"+n},Ee={},st=function(e,a,r){let i=Promise.resolve();if(a&&a.length>0){let f=function(v){return Promise.all(v.map(l=>Promise.resolve(l).then(E=>({status:"fulfilled",value:E}),E=>({status:"rejected",reason:E}))))};document.getElementsByTagName("link");const d=document.querySelector("meta[property=csp-nonce]"),s=d?.nonce||d?.getAttribute("nonce");i=f(a.map(v=>{if(v=rt(v),v in Ee)return;Ee[v]=!0;const l=v.endsWith(".css"),E=l?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${v}"]${E}`))return;const A=document.createElement("link");if(A.rel=l?"stylesheet":at,l||(A.as="script"),A.crossOrigin="",A.href=v,s&&A.setAttribute("nonce",s),document.head.appendChild(A),l)return new Promise((w,M)=>{A.addEventListener("load",w),A.addEventListener("error",()=>M(new Error(`Unable to preload CSS for ${v}`)))})}))}function c(d){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=d,window.dispatchEvent(s),!s.defaultPrevented)throw d}return i.then(d=>{for(const s of d||[])s.status==="rejected"&&c(s.reason);return e().catch(c)})};var it=y('
');function ln(n,e){ne(e,!0);const a=["1","2","3","4","5","6","7","8","9","0"];var r=on(),i=u(r),c=p(u(i),2),d=u(c),s=p(i,2);fe(s,20,()=>a,w=>w,(w,M)=>{var g=sn(),k=u(g);q(()=>z(k,M)),H("click",g,()=>e.ondigit(M)),m(w,g)});var f=p(s,2),v=u(f),l=p(v,2),E=p(l,2),A=u(E);ie(A,{name:"check",size:"1.5rem"}),q(()=>z(d,e.grams===""?"0":e.grams)),H("click",v,function(...w){e.onclear?.apply(this,w)}),H("click",l,function(...w){e.oncancel?.apply(this,w)}),H("click",E,function(...w){e.onconfirm?.apply(this,w)}),m(n,r),ae()}le(["click"]);function cn(n){return n===null||n.state==="initializing"?!1:n.scale.connected&&n.printer.health!=="faulted"}const Ae=1500,un=2e3,Ie=5e3;function dn(n,e){return e?n>=Ie?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!0}:n>=un?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!1}:n>=Ae?{showWeight:!1,banner:"",mustReconnect:!1}:{showWeight:!0,banner:"",mustReconnect:!1}:{showWeight:n=Ie}}const vn=250,fn={show_grid_prices:!0,idle_timeout_s:45,reprint_window_s:60,sound:!0,show_by_unit_products:!1};class gn{#e=j(null);get state(){return t(this.#e)}set state(e){x(this.#e,e,!0)}#t=j(null);get catalog(){return t(this.#t)}set catalog(e){x(this.#t,e,!0)}#n=j(Ne({showWeight:!0,banner:"",mustReconnect:!1}));get link(){return t(this.#n)}set link(e){x(this.#n,e,!0)}#r=j("");get catalogError(){return t(this.#r)}set catalogError(e){x(this.#r,e,!0)}#s=null;#i=null;#o=Date.now();#a=!1;#l=!1;get presentation(){return this.catalog?.presentation??fn}start(){this.#u(),this.#c(),this.#i=setInterval(()=>this.#v(),vn)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null,this.#s?.close(),this.#s=null,this.#a=!1}#c(){const e=new EventSource("/api/v1/stream");this.#s=e,e.onopen=()=>{this.#a=!0,this.#o=Date.now()},e.addEventListener("state",a=>this.#d(a)),e.onerror=()=>{this.#a=!1}}#d(e){this.#o=Date.now(),this.#a=!0;const a=JSON.parse(e.data);this.state=a,this.catalog!==null&&a.catalog_count!==this.catalog.product_count&&this.#u()}#v(){const e=dn(Date.now()-this.#o,this.#a);this.link=e,e.mustReconnect&&(this.#s?.close(),this.#o=Date.now(),this.#c())}async#u(){if(!this.#l){this.#l=!0;try{this.catalog=await Ve(),this.catalogError=""}catch{this.catalog===null&&(this.catalogError="Catalogue indisponible. Prévenez un responsable.")}finally{this.#l=!1}}}}const Se="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Pe(){let n=Date.now();const e=new Array(26);for(let r=9;r>=0;r--)e[r]=Se[n%32],n=Math.floor(n/32);const a=new Uint8Array(16);crypto.getRandomValues(a);for(let r=0;r<16;r++)e[10+r]=Se[a[r]%32];return e.join("")}var hn=y('');function pn(n,e){ne(e,!0);const a=new gn;let r=j(Ne(Xe)),i=j(""),c=j(!1);const d=h(()=>t(i)!==""||t(c));let s=j(null),f=j(-1),v=j(null);const l=h(()=>a.catalog),E=h(()=>t(l)===null?[]:Qe(t(l))),A=h(()=>t(l)===null?[]:Je(t(l))),w=h(()=>Ze(t(E),t(r),t(i))),M=h(()=>{const o={};for(const U of t(l)?.categories??[])o[U.code]=U.color;return o}),g=h(()=>a.state),k=h(()=>a.presentation),O=h(()=>cn(t(g))),C=h(()=>{if(a.catalogError!=="")return{message:a.catalogError,hint:"Le poste réessaie tout seul."};if(t(E).length>0)return{message:"Aucun produit ne correspond.",hint:"Effacez des lettres ou changez de rayon."};if(t(l)!==null&&t(l).products.length>0)return{message:t(l).products.every(ve=>ve.mode==="by_unit")&&!t(k).show_by_unit_products?"Aucun produit à montrer : ce poste masque les produits vendus à l’unité, et le catalogue reçu ne contient que ceux-là.":"Aucun produit à montrer : les réglages d’affichage de ce poste masquent tout ce qu’il a reçu.",hint:"Le fichier est bien arrivé : prévenez un responsable, c’est un réglage de ce poste qui vide la grille."};const o=t(g)?.station??0;return{message:o>0?`Catalogue vide. En attente du fichier flv_${o}.csv.`:"Catalogue vide. En attente du fichier du poste.",hint:"Prévenez un responsable : aucun produit ne peut être pesé."}}),X=h(()=>t(g)?.state==="rejected"?t(g).product?.id??null:null),G=h(()=>t(g)?.state==="faulted"||t(g)?.state==="out_of_service"),I=["product_armed","weight_present","weight_stable","awaiting_stability","validating","printing"],W=h(()=>t(g)!==null&&I.includes(t(g).state)?t(g).product?.id??null:null),F=h(()=>Zt(t(l)?.updated_at??""));ue(()=>(a.start(),()=>a.stop())),ue(()=>{const o=a.state?.revision??-1;t(s)!==null&&o>t(f)&&(x(s,null),x(f,-1))});async function K(o){if(t(s)===null){x(s,o.id,!0),x(f,a.state?.revision??-1,!0);try{await Ke({product_id:o.id,tare_g:a.state?.weight.tare_g??0,units:1,manual_weight_g:0,seen_weight_g:a.state?.weight.gross_g??0,measurement_seq:a.state?.weight.seq??0,key:Pe()})}catch{x(s,null)}}}async function V(){const o=a.state?.reprint.job_id;o===void 0||o===""||await Ye(o,Pe())}function Q(){x(i,""),x(c,!1)}function _(){x(c,!0)}function P(){const o=t(w).length===1?t(w)[0]:void 0;o!==void 0&&K(o)}function S(o){if(!(o.metaKey||o.ctrlKey||o.altKey)&&!(o.target instanceof HTMLElement&&o.target.tagName==="INPUT")&&!(t(v)!==null||t(G))){if(o.key==="Escape"){o.preventDefault(),Q();return}if(o.key==="Backspace"){o.preventDefault(),x(i,t(i).slice(0,-1),!0);return}if(o.key==="Enter"){P();return}o.key.length===1&&/[a-zA-Z0-9 ]/.test(o.key)&&(o.key===" "&&t(i)===""||(o.preventDefault(),x(i,t(i)+o.key)))}}ue(()=>(window.addEventListener("keydown",S),()=>window.removeEventListener("keydown",S)));async function N(){(await st(()=>import("./mount-CEsnfFiz.js"),__vite__mapDeps([0,1,2,3]))).mountAdmin(document.body)}var Z=hn(),J=u(Z);{let o=h(()=>t(v)!==null);ct(J,{get snapshot(){return t(g)},get showWeight(){return a.link.showWeight},get linkBanner(){return a.link.banner},get taring(){return t(o)},ontare:()=>x(v,"")})}var b=p(J,2);{var D=o=>{ln(o,{get grams(){return t(v)},ondigit:U=>x(v,(t(v)??"")+U),onclear:()=>x(v,""),oncancel:()=>x(v,null),onconfirm:()=>x(v,null)})};B(b,o=>{t(v)!==null&&o(D)})}var L=p(b,2);{let o=h(()=>t(l)?.pricing.primary_code??""),U=h(()=>Object.fromEntries((t(l)?.pricing.tiers??[]).map(ye=>[ye.code,ye.abbrev]))),ve=h(()=>t(l)===null&&a.catalogError==="");Gt(L,{get products(){return t(w)},get colors(){return t(M)},get primaryCode(){return t(o)},get tierAbbrev(){return t(U)},get selectedID(){return t(W)},get rejectedID(){return t(X)},get busyID(){return t(s)},get showPrices(){return t(k).show_grid_prices},get loading(){return t(ve)},get emptyMessage(){return t(C).message},get emptyHint(){return t(C).hint},onpick:K})}var R=p(L,2);{var ee=o=>{Kt(o,{get query(){return t(i)},onquery:U=>x(i,U,!0),onclose:Q,onenter:P})};B(R,o=>{t(d)&&o(ee)})}var Y=p(R,2);_t(Y,{get chips(){return t(A)},get active(){return t(r)},get searchFieldOpen(){return t(d)},onselect:o=>x(r,o,!0),onopensearch:_});var $=p(Y,2);{let o=h(()=>t(g)?.last_label??null),U=h(()=>t(g)?.reprint.available??!1),ve=h(()=>t(l)?.app_version??"");rn($,{get label(){return t(o)},get available(){return t(U)},get catalogAt(){return t(F)},get productCount(){return t(E).length},get appVersion(){return t(ve)},get healthy(){return t(O)},onreprint:V,onadmin:N})}var re=p($,2);{var se=o=>{{let U=h(()=>t(g).message?.text??"Prévenez un responsable.");ke(o,{title:"Poste indisponible",get detail(){return t(U)},get code(){return t(g).fault_code},action:{label:"J’ai compris",run:()=>{$e()}}})}},de=o=>{ke(o,{title:"Poste hors service",detail:"Ce poste ne peut pas peser. Prévenez un responsable.",get code(){return t(g).fault_code}})};B(re,o=>{t(g)?.state==="faulted"?o(se):t(g)?.state==="out_of_service"&&o(de,1)})}m(n,Z),ae()}const _n=5;mn();document.addEventListener("contextmenu",n=>{bn(n.target)||n.preventDefault()});document.addEventListener("dragstart",n=>n.preventDefault());et(pn,{target:document.getElementById("app")});function bn(n){return n instanceof Element&&n.closest("[data-admin]")!==null}function mn(){let n=!1;const e=(a,r)=>{n||(n=!0,tt(a,r),yn(),setTimeout(()=>location.reload(),_n*1e3))};window.addEventListener("error",a=>e(a.message,a.error?.stack??"")),window.addEventListener("unhandledrejection",a=>e(String(a.reason),a.reason?.stack??""))}function yn(){const n=document.createElement("div");n.className="fatal",n.append(_e("h1","Une erreur est survenue"),_e("p","L’écran va se recharger tout seul."),_e("p","ERR-UI-01","code")),document.body.appendChild(n)}function _e(n,e,a=""){const r=document.createElement(n);return r.textContent=e,a!==""&&(r.className=a),r}
diff --git a/internal/web/dist/assets/mount-C-eWTYT5.js b/internal/web/dist/assets/mount-C-eWTYT5.js
deleted file mode 100644
index d414cf9..0000000
--- a/internal/web/dist/assets/mount-C-eWTYT5.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import{R as cr,T as dr,U as vr,V as Sa,W as ra,e as pr,X as fr,Y as gr,Z as mr,_ as Ga,u as ga,r as La,$ as hr,c as Ha,B as Xt,A as F,k as e,z as p,F as Lt,d as At,b as vt,s as r,i as g,t as v,n as Zt,l as Ue,o as i,f as Ze,g as l,v as a,j as o,p as Nt,I as _r,h as $t,q as u,w as Y,a0 as Ta,E as oa,x as at,G as Ja,a1 as la,y as na,a2 as Ut,a3 as ia,a4 as xt,C as ma,K as br,H as wr,P as yr,a5 as xr}from"./app-Bm_67CUJ.js";function Ka(n){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function kr(n,t,...s){var c=new vr(n);cr(()=>{const _=t()??null;c.ensure(_,_&&(w=>_(w,...s)))},dr)}function Xa(n,t,s=!1){if(n.multiple){if(t==null)return;if(!fr(t))return gr();for(var c of n.options)c.selected=t.includes(ua(c));return}for(c of n.options){var _=ua(c);if(mr(_,t)){c.selected=!0;return}}(!s||t!==void 0)&&(n.selectedIndex=-1)}function qr(n){var t=new MutationObserver(()=>{"__value"in n&&Xa(n,n.__value)});t.observe(n,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),Ga(()=>{t.disconnect()})}function Sr(n,t,s=t){var c=new WeakSet,_=!0;Sa(n,"change",w=>{var T=w?"[selected]":":checked",A;if(n.multiple)A=[].map.call(n.querySelectorAll(T),ua);else{var M=n.querySelector(T)??n.querySelector("option:not([disabled])");A=M&&ua(M)}s(A),n.__value=A,ra!==null&&c.add(ra)}),pr(()=>{var w=t();if(n===document.activeElement){var T=ra;if(c.has(T))return}if(Xa(n,w,_),_&&w===void 0){var A=n.querySelector(":checked");A!==null&&(w=ua(A),s(w))}n.__value=w,_=!1}),qr(n)}function ua(n){return"__value"in n?n.__value:n.value}function ca(n,t,s=t){var c=new WeakSet;Sa(n,"input",async _=>{var w=_?n.defaultValue:n.value;if(w=ba(n)?wa(w):w,s(w),ra!==null&&c.add(ra),await hr(),w!==(w=t())){var T=n.selectionStart,A=n.selectionEnd,M=n.value.length;if(n.value=w??"",A!==null){var H=n.value.length;T===A&&A===M&&H>M?(n.selectionStart=H,n.selectionEnd=H):(n.selectionStart=T,n.selectionEnd=Math.min(A,H))}}}),ga(t)==null&&n.value&&(s(ba(n)?wa(n.value):n.value),ra!==null&&c.add(ra)),La(()=>{var _=t();if(n===document.activeElement){var w=ra;if(c.has(w))return}ba(n)&&_===wa(n.value)||n.type==="date"&&!_&&!n.value||_!==n.value&&(n.value=_??"")})}function Ca(n,t,s=t){Sa(n,"change",c=>{var _=c?n.defaultChecked:n.checked;s(_)}),ga(t)==null&&s(n.checked),La(()=>{var c=t();n.checked=!!c})}function ba(n){var t=n.type;return t==="number"||t==="range"}function wa(n){return n===""?null:+n}function Oa(n,t,s,c,_){var w=()=>{c(s[n])};s.addEventListener(t,w),_?La(()=>{s[n]=_()}):w(),(s===document.body||s===window||s===document)&&Ga(()=>{s.removeEventListener(t,w)})}function Ea(n){Ha===null&&Ka(),Xt(()=>{const t=ga(n);if(typeof t=="function")return t})}function Lr(n){Ha===null&&Ka(),Ea(()=>()=>ga(n))}class Ft extends Error{constructor(t,s,c="",_=[]){super(s),this.status=t,this.code=c,this.faults=_,this.name="AdminError"}status;code;faults;get needsPassword(){return this.status===401}}function Er(){return ea("/admin/api/health")}function zr(){return gt("/admin/api/troubleshooting/reprint",{})}function jr(){return gt("/admin/api/troubleshooting/reload-catalog",{})}function Pr(n){return gt("/admin/api/troubleshooting/manual-entry",{on:n})}function Tr(){return gt("/admin/api/troubleshooting/roll-changed",{})}function Cr(n){return gt("/admin/api/troubleshooting/fallback-printer",{on:n})}function Or(){return gt("/admin/api/troubleshooting/test-scale",{})}function Rr(){return gt("/admin/api/troubleshooting/test-printer",{})}function Ar(){return gt("/admin/api/troubleshooting/test-label",{})}async function Ya(n){const t=new FormData;t.append("file",n,n.name);const s=await fetch("/admin/api/catalog/import",{method:"POST",body:t});return ja(s,"POST /admin/api/catalog/import")}const Nr="/admin/api/diagnostic.zip";function $r(n){return gt("/admin/api/session",{password:n})}async function Dr(){await fetch("/admin/api/session",{method:"DELETE"})}function Ir(n,t){return gt("/admin/api/session/recovery",{code:n,password:t})}function za(){return ea("/admin/api/config")}function Ur(n){return Za("PUT","/admin/api/config",n)}function Fr(){return gt("/admin/api/config/confirm",{})}async function Mr(){return(await ea("/admin/api/config/versions")).versions}function Br(n){return gt("/admin/api/config/restore",{version:n})}async function Vr(){return(await ea("/admin/api/ports")).ports}async function Wr(){return(await ea("/admin/api/printers")).printers}async function Gr(){return(await gt("/admin/api/printers/discover",{})).printers}function Hr(n){return gt("/admin/api/scale/detect",{port:n})}async function Ra(n,t){return(await gt("/admin/api/scale/capture",{port:n,seconds:t})).frames}function Qa(n){return gt("/admin/api/printer/test?what="+n,{})}function Jr(n,t,s,c){const _=new URLSearchParams({template:n,demo:t?"1":"0",dual:s?"1":"0"});return _.set("t",String(c)),"/admin/api/label/preview.png?"+_.toString()}async function Kr(n){const t=new URLSearchParams(n);return(await ea("/admin/api/journal?"+t)).weighings}function Xr(n){return"/admin/api/journal/export.csv?"+new URLSearchParams(n).toString()}async function Yr(n){const t=new URLSearchParams(n);return(await ea("/admin/api/technical?"+t)).entries}function Qr(n){const t=n===void 0?"":"?id="+String(n);return ea("/admin/api/imports"+t)}function Zr(){return gt("/admin/api/catalog/reload",{})}function en(){return gt("/admin/api/catalog/forget-quarantine",{})}function Aa(n,t){return gt("/admin/api/products/"+encodeURIComponent(n)+"/decision",t)}function tn(n){return gt("/admin/api/replay",{frame:n})}async function ea(n){const t=await fetch(n,{headers:{accept:"application/json"}});return ja(t,"GET "+n)}function gt(n,t){return Za("POST",n,t)}async function Za(n,t,s){const c=await fetch(t,{method:n,headers:{"content-type":"application/json"},body:JSON.stringify(s)});return ja(c,n+" "+t)}async function ja(n,t){const s=await n.text();if(!n.ok){const c=er(s);throw new Ft(n.status,rn(s,t,n.status)+an(n),c?.code??"",c?.faults??[])}if(s!=="")return JSON.parse(s)}function an(n){if(n.status!==429)return"";const t=Number(n.headers.get("Retry-After")??"");if(!Number.isFinite(t)||t<=0)return"";const s=Math.ceil(t/60);return s>1?` Réessayez dans ${String(s)} minutes.`:" Réessayez dans une minute."}function rn(n,t,s){const c=er(n);return c?.message!==void 0&&c.message!==""?c.message:`${t} a répondu ${String(s)}.`}function er(n){try{return JSON.parse(n)}catch{return null}}function nn(){return ea("/admin/api/update")}function sn(){return gt("/admin/api/update/check",{})}function ln(n){return gt("/admin/api/update/apply",{version:n})}class on{constructor(t){this.admin=t}admin;#t=F(null);get config(){return e(this.#t)}set config(t){p(this.#t,t,!0)}#r=F("");get fingerprint(){return e(this.#r)}set fingerprint(t){p(this.#r,t,!0)}#a=F(Lt([]));get retired(){return e(this.#a)}set retired(t){p(this.#a,t,!0)}#e=F(null);get pending(){return e(this.#e)}set pending(t){p(this.#e,t,!0)}#n=F(Lt([]));get faults(){return e(this.#n)}set faults(t){p(this.#n,t,!0)}#l=F(!1);get dirty(){return e(this.#l)}set dirty(t){p(this.#l,t,!0)}async load(){const t=await this.admin.load(()=>za());t!==null&&(this.config=t.config,this.fingerprint=t.config_fingerprint,this.retired=t.retired_keys??[],this.pending=t.pending_confirmation,this.faults=[],this.dirty=!1)}value(t){let s=this.config;for(const c of t.split(".")){if(s===null||typeof s!="object")return;s=s[c]}return s}text(t){const s=this.value(t);return s==null?"":String(s)}number(t){const s=this.value(t);return typeof s=="number"?s:Number(s??0)}flag(t){return this.value(t)===!0}set(t,s){if(this.config===null)return;const c=t.split("."),_=c.pop();if(_===void 0)return;let w=this.config;for(const T of c){const A=w[T];(A===null||typeof A!="object")&&(w[T]={}),w=w[T]}w[_]=s,this.dirty=!0}unset(t){const s=t.split("."),c=s.pop();if(c===void 0||this.config===null)return!1;let _=this.config;for(const w of s){if(_===null||typeof _!="object")return!1;_=_[w]}return _===null||typeof _!="object"?!1:(delete _[c],this.dirty=!0,!0)}async save(){if(this.config===null)return!1;this.faults=[];const t=this.config;let s;try{s=await Ur(t)}catch(c){if(Na(c))throw c;return this.admin.report(c),this.faults=un(this.admin),!1}return this.config=s.config,this.fingerprint=s.config_fingerprint,this.retired=s.retired_keys??[],this.pending=s.pending_confirmation,this.dirty=!1,!0}async confirm(){try{await Fr()}catch(t){if(Na(t))throw t;this.admin.report(t);return}this.admin.notice="La configuration est confirmée.",this.pending=null,await this.admin.refresh()}dropRetired(t){if(t.includes("[")){this.admin.actionError=`« ${t} » est logée dans un tableau : cet écran ne sait pas la retirer de là. Modifiez le fichier de configuration lui-même.`;return}this.unset(t)&&(this.retired=this.retired.filter(s=>s!==t))}}function un(n){return n.lastFaults}function Na(n){return n instanceof Ft&&(n.status===401||n.status===409)}const cn={"station.number":"Numéro du poste","station.name":"Nom du poste","station.coop":"Nom de la coopérative","network.listen":"Adresse d’écoute","network.admin_on_lan":"Administration accessible depuis le réseau","ui.language":"Langue","ui.sound":"Son","ui.idle_timeout_s":"Retour à l’accueil après (secondes)","ui.reprint_window_s":"Réimpression possible pendant (secondes)","ui.show_grid_prices":"Afficher les prix sur les tuiles","ui.show_by_unit_products":"Afficher les produits vendus à l’unité","scale.type":"Protocole de la balance","scale.present":"Ce poste a une balance","scale.manual_entry_allowed":"Saisie manuelle autorisée","scale.degrade_after_s":"Passer en mode dégradé après (secondes)","scale.options.port":"Port série","scale.options.baud":"Vitesse (bauds)","scale.options.bits":"Bits de données","scale.options.parity":"Parité","scale.options.stop":"Bits d’arrêt","scale.options.backoff_min_ms":"Attente minimale avant réessai (ms)","scale.options.backoff_max_ms":"Attente maximale avant réessai (ms)","printer.type":"Pilote d’impression","printer.template":"Gabarit d’étiquette","printer.options.transport":"Transport","printer.options.queue":"File d’impression","printer.options.path":"Fichier de périphérique","printer.options.address":"Adresse réseau","printer.options.darkness":"Noircissement","printer.options.speed":"Vitesse d’impression","printer.options.offset_x":"Décalage horizontal (dots)","printer.options.offset_y":"Décalage vertical (dots)","printer.options.invert_bits":"Inverser les points","printer.options.copies":"Exemplaires","printer.options.roll_capacity":"Étiquettes par rouleau","pricing.amount_rounding":"Arrondi du montant","pricing.unit_price_rounding":"Arrondi du prix au kilo","pricing.primary_code":"Tarif principal","pricing.reference_code":"Tarif de référence","barcode.verify_reference_check_digit":"Vérifier la clé de contrôle","limits.empty_max_g":"Plateau considéré vide en dessous de (g)","limits.basket_check_enabled":"Vérifier la présence du panier","limits.basket_min_g":"Poids du panier, borne basse (g)","limits.basket_max_g":"Poids du panier, borne haute (g)","limits.min_weight_g":"Poids minimum accepté","limits.max_weight_g":"Poids maximum accepté","limits.max_tare_g":"Tare maximale","limits.min_units":"Unités minimum","limits.max_units":"Unités maximum","limits.max_amount_cents":"Montant maximum (centimes)","stability.mode":"Exigence de stabilité","stability.min_duration_ms":"Durée de stabilité (ms)","stability.tolerance_g":"Tolérance de stabilité (g)","stability.timeout_ms":"Délai d’attente de stabilité (ms)","stability.on_timeout":"Au bout du délai","stability.min_latch_rate":"Taux d’accroche minimal","stability.latch_rate_window_ms":"Fenêtre de mesure du taux (ms)","stability.expiry_floor_ms":"Péremption, plancher (ms)","stability.expiry_ceiling_ms":"Péremption, plafond (ms)","stability.expiry_factor":"Péremption, facteur","catalog.type":"Où le poste va chercher le catalogue","catalog.options.directory":"Répertoire surveillé","catalog.options.url":"Adresse du serveur","catalog.options.username":"Compte","catalog.options.password":"Mot de passe","catalog.options.separator":"Séparateur du CSV","catalog.options.poll_interval_s":"Vérifier toutes les (secondes)","catalog.options.stable_polls":"Vérifications avant lecture","catalog.options.max_file_size_mb":"Taille maximale du fichier (Mo)","catalog.options.max_image_size_kb":"Taille maximale d’une image (Ko)","catalog.options.min_readable_ratio":"Part minimale de lignes lisibles","catalog.options.max_weighable_drop":"Baisse maximale des produits pesables","catalog.options.max_archives":"Archives conservées","catalog.options.archive_days":"Archives conservées (jours)","catalog.options.failures_before_reject":"Échecs avant mise en quarantaine","catalog.images.source":"Origine des photos","catalog.images.path":"Répertoire des photos","catalog.fallback_category":"Rayon par défaut","journal.max_rows":"Pesées conservées","journal.max_days":"Pesées conservées (jours)","journal.max_technical":"Événements techniques conservés","admin.session_minutes":"Durée d’une session (minutes)","admin.attempts_per_minute":"Tentatives par minute","maintenance.weekly_integrity_check":"Contrôle d’intégrité hebdomadaire","maintenance.disk_alert_mb":"Alerte disque en dessous de (Mo)"};function Gt(n){return cn[n]??n}const dn={station:"l’identité du poste",network:"le réseau",ui:"l’écran client",scale:"la balance",printer:"l’imprimante",pricing:"les tarifs",barcode:"le code-barres",limits:"les garde-fous",stability:"la stabilité",catalog:"le catalogue",journal:"le journal",maintenance:"la maintenance"};function vn(n){return dn[n]??n}const pn={scale:"balance",printer:"imprimante",catalog:"catalogue",ui:"écran",config:"configuration",http:"réseau",system:"système"};function tr(n){return pn[n]??"origine inconnue"}const xa="openscale.admin.preferences",ar="technical";class fn{#t=F(Lt(gn()));get showTechnicalNames(){return e(this.#t)}set showTechnicalNames(t){p(this.#t,t,!0)}toggleTechnicalNames(){this.showTechnicalNames=!this.showTechnicalNames,mn(this.showTechnicalNames)}}function gn(){try{return globalThis.localStorage?.getItem(xa)===ar}catch{return!1}}function mn(n){try{n?globalThis.localStorage?.setItem(xa,ar):globalThis.localStorage?.removeItem(xa)}catch{}}const Ht=new fn,hn=3e3,_n=["hardware","label","rules","catalog","journal","station","update"];function $a(n){return _n.includes(n)}function bn(n){return n instanceof Ft&&(n.status===401||n.status===409)}class wn{constructor(t=hn){this.periodMs=t}periodMs;#t=F(null);get health(){return e(this.#t)}set health(t){p(this.#t,t,!0)}#r=F("dashboard");get page(){return e(this.#r)}set page(t){p(this.#r,t,!0)}#a=F(!1);get expert(){return e(this.#a)}set expert(t){p(this.#a,t,!0)}#e=F("");get linkError(){return e(this.#e)}set linkError(t){p(this.#e,t,!0)}#n=F("");get actionError(){return e(this.#n)}set actionError(t){p(this.#n,t,!0)}#l=F(!1);get needsFirstPassword(){return e(this.#l)}set needsFirstPassword(t){p(this.#l,t,!0)}#d=F("");get notice(){return e(this.#d)}set notice(t){p(this.#d,t,!0)}#v=F(!1);get busy(){return e(this.#v)}set busy(t){p(this.#v,t,!0)}#p=F(Lt([]));get lastFaults(){return e(this.#p)}set lastFaults(t){p(this.#p,t,!0)}#f=F(null);get pending(){return e(this.#f)}set pending(t){p(this.#f,t,!0)}#i=null;#o=null;start(){this.refresh(),this.#i=setInterval(()=>{this.refresh()},this.periodMs)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null}async refresh(){try{this.health=await Er(),this.linkError=""}catch(t){this.linkError=Da(t)}}#u(){this.busy=!0,this.notice="",this.actionError="",this.needsFirstPassword=!1}#s(t){this.actionError=Da(t),this.needsFirstPassword=t instanceof Ft&&t.status===409,this.#m(t)}async run(t){this.#u();let s=null;try{s=await t(),this.notice=s.message}catch(c){this.#s(c)}finally{this.busy=!1}return await this.refresh(),s}async login(t){this.#u();try{return await $r(t),this.expert=!0,this.notice="Session d’administration ouverte.",!0}catch(s){return this.expert=!1,this.#s(s),!1}finally{this.busy=!1}}async recover(t,s){this.#u();try{const c=await Ir(t,s);return this.expert=!0,this.notice=c.warning??"Le mot de passe est remplacé et la session est ouverte.",!0}catch(c){return this.#s(c),!1}finally{this.busy=!1}}async protect(t){try{return await t()}catch(s){if(!bn(s))return this.#s(s),null;if(!await this.#g(s))return null;try{return await t()}catch(c){return this.#s(c),null}}}#g(t){return this.pending={kind:t.status===409?"first-password":"password",message:t.message},new Promise(s=>{this.#o=s})}async answerPassword(t){await this.login(t)&&this.#c(!0)}async answerRecovery(t,s){await this.recover(t,s)&&this.#c(!0)}cancelPassword(){this.#c(!1)}#c(t){this.pending=null,this.notice="";const s=this.#o;this.#o=null,s?.(t)}async logout(){await Dr(),this.expert=!1,this.page="dashboard",this.notice="Session d’administration fermée."}open(t){this.actionError="",this.notice="",this.needsFirstPassword=!1,this.page=t}async load(t){try{const s=await t();return this.actionError="",this.lastFaults=[],s}catch(s){return this.lastFaults=s instanceof Ft?s.faults:[],this.#s(s),null}}report(t){this.lastFaults=t instanceof Ft?t.faults:[],this.#s(t)}#m(t){t instanceof Ft&&t.needsPassword&&(this.expert=!1)}}function Da(n){return n instanceof Ft?n.message:n instanceof Error?"Le poste n’a pas répondu : "+n.message:"Le poste n’a pas répondu."}var yn=o('clé'),xn=o('');function tt(n,t){const s=vt(t,"kind",3,"read"),c=vt(t,"busy",3,!1),_=vt(t,"disabled",3,!1),w=vt(t,"protected",3,!1),T=vt(t,"act",3,void 0);var A=xn();let M;var H=a(A),J=r(H);{var pe=fe=>{var _e=yn();l(fe,_e)};g(J,fe=>{w()&&fe(pe)})}v(()=>{M=Zt(A,1,`act ${s()??""}`,"svelte-5tpq0o",M,{"touch-target":s()==="destructive",busy:c()}),Ue(A,"data-kind",s()),Ue(A,"data-act",T()),A.disabled=_()||c(),i(H,`${(c()?"En cours…":t.label)??""} `)}),Ze("click",A,function(...fe){t.onrun?.apply(this,fe)}),l(n,A)}At(["click"]);var kn=o(`
Le code de secours a été tiré à l’installation de ce poste et imprimé
- sur sa fiche, rangée dans le classeur du magasin. Il n’existe nulle part ailleurs.
`,1),qn=o(`
Oublié ? Le code de secours de la fiche d’installation en repose un.
- Un responsable peut aussi le faire en ligne de commande, avec openscale config password.
`,1),Sn=o('
'),Ln=o('
');function En(n,t){Nt(t,!0);const s=u(()=>t.admin.pending?.kind==="first-password");let c=F(""),_=F("");const w=8,T=u(()=>e(s)?e(_).trim().length>0&&e(c).length>=w:e(c).length>0);async function A(){!e(T)||t.admin.busy||(e(s)?await t.admin.answerRecovery(e(_),e(c)):await t.admin.answerPassword(e(c)),p(c,""),p(_,""))}var M=Ln(),H=a(M),J=a(H),pe=a(J),fe=a(pe);_r(fe,{name:"settings",size:"1.5rem"});var _e=r(pe,2),ae=a(_e),C=r(J,2),ge=a(C),Q=r(C,2);{var be=N=>{var L=kn(),q=r(Y(L),2),D=r(a(q),2);Ta(D);var X=r(q,2),me=a(X);me.textContent="Nouveau mot de passe — 8 caractères au moins";var ze=r(me,2);Ze("keydown",D,Ce=>Ce.key==="Enter"&&void A()),ca(D,()=>e(_),Ce=>p(_,Ce)),Ze("keydown",ze,Ce=>Ce.key==="Enter"&&void A()),ca(ze,()=>e(c),Ce=>p(c,Ce)),l(N,L)},se=N=>{var L=qn(),q=Y(L),D=r(a(q),2);Ta(D),Ze("keydown",D,X=>X.key==="Enter"&&void A()),ca(D,()=>e(c),X=>p(c,X)),l(N,L)};g(Q,N=>{e(s)?N(be):N(se,-1)})}var P=r(Q,2);{var ne=N=>{var L=Sn(),q=a(L);v(()=>i(q,t.admin.actionError)),l(N,L)};g(P,N=>{t.admin.actionError!==""&&N(ne)})}var V=r(P,2),B=a(V);tt(B,{label:"Annuler",onrun:()=>t.admin.cancelPassword()});var re=r(B,2);{let N=u(()=>e(s)?"Poser ce mot de passe":"Continuer"),L=u(()=>!e(T)||t.admin.busy);tt(re,{kind:"write",get label(){return e(N)},get disabled(){return e(L)},onrun:()=>{A()}})}v(()=>{i(ae,e(s)?"Ce poste n’a pas encore de mot de passe":"Mot de passe d’administration"),i(ge,t.admin.pending?.message??"")}),l(n,M),$t()}At(["keydown"]);var zn=o(''),jn=o(""),Pn=o(""),Tn=o('
'),Cn=o(""),On=o('
'),Rn=o('
');function _t(n,t){Nt(t,!0);const s=vt(t,"hint",3,""),c=vt(t,"kind",3,"text"),_=vt(t,"disabled",3,!1),w=vt(t,"fault",3,""),T=vt(t,"allowed",19,()=>[]),A=u(()=>`field-${t.path.replace(/\./gu,"-")}`);var M=Rn();let H;var J=a(M),pe=a(J),fe=a(pe),_e=r(pe,2);{var ae=V=>{var B=zn(),re=a(B);v(()=>i(re,t.path)),l(V,B)};g(_e,V=>{Ht.showTechnicalNames&&V(ae)})}var C=r(J,2),ge=r(C,2);{var Q=V=>{var B=Pn();at(B,20,T,re=>re,(re,N)=>{var L=jn(),q={};v(()=>{q!==(q=N)&&(L.value=(L.__value=N)??"")}),l(re,L)}),v(()=>Ue(B,"id",e(A)+"-allowed")),l(V,B)};g(ge,V=>{T().length>0&&V(Q)})}var be=r(ge,2);{var se=V=>{var B=Tn(),re=a(B);v(()=>i(re,s())),l(V,B)};g(be,V=>{s()!==""&&V(se)})}var P=r(be,2);{var ne=V=>{var B=On(),re=a(B),N=r(re);{var L=q=>{var D=Cn(),X=a(D);v(me=>i(X,`Valeurs acceptées : ${me??""}.`),[()=>T().join(", ")]),l(q,D)};g(N,q=>{T().length>0&&q(L)})}v(()=>i(re,`${w()??""} `)),l(V,B)};g(P,V=>{w()!==""&&V(ne)})}v(()=>{H=Zt(M,1,"field svelte-1oilv5s",null,H,{refused:w()!==""}),Ue(J,"for",e(A)),i(fe,t.label),Ue(C,"id",e(A)),Ue(C,"type",c()),oa(C,t.value),C.disabled=_(),Ue(C,"list",T().length>0?e(A)+"-allowed":void 0)}),Ze("input",C,V=>t.onchange(V.currentTarget.value)),l(n,M),$t()}At(["input"]);const Ia=1e3;function pa(n){const t=Pa(n);return t===null?"":[sa(t.getDate()),sa(t.getMonth()+1),String(t.getFullYear())].join("/")}function Mt(n){const t=Pa(n);return t===null?"":`${pa(n)} à ${sa(t.getHours())}:${sa(t.getMinutes())}`}function An(n){const t=Pa(n);return t===null?"":[sa(t.getHours()),sa(t.getMinutes()),sa(t.getSeconds())].join(":")}function ka(n){const t=n/1e6;return t<1e3?`${Math.round(t)} Mo`:`${rr(t/1e3)} Go`}function he(n){const t=String(Math.abs(Math.trunc(n))),s=[];for(let c=t.length;c>0;c-=3)s.unshift(t.slice(Math.max(0,c-3),c));return(n<0?"-":"")+s.join(" ")}function va(n){return n0&&w.push({count:Rt(n.anomalies_count),label:"anomalies",note:"à corriger dans Odoo",link:Ua(n.anomalies_count)}),n.unit_mismatches_count>0){const T=n.unit_mismatches_count;w.push({count:"+ "+Rt(T),label:T===1?"unité divergente":"unités divergentes",note:"pesable, unité à corriger",link:Ua(T)})}return{headline:`Catalogue du ${pa(n.occurred_at)} — ${Rt(s)} produits reçus`,lines:w,oneLine:Nn(n)}}function Nn(n){const t=[`${Rt(n.rows_read_count)} reçus`,`${Rt(n.weighable_count)} pesables`,`${Rt(n.not_weighable_count)} non pesables`,`${Rt(n.anomalies_count)} anomalies`],s=n.unit_mismatches_count;return s>0&&t.push(`${Rt(s)} ${s===1?"unité divergente":"unités divergentes"}`),t.join(" · ")}const $n={applied:"appliqué",unchanged:"identique au précédent",rejected:"refusé",failed:"échec"},Dn={local_drop:"dépôt local",webdav:"WebDAV",manual:"déposé sur l’écran"};function ha(n){return $n[n]??"résultat inconnu"}function sr(n){return Dn[n]??"source inconnue"}function In(n,t){const s=[n.file_name===""?"Le dernier fichier":n.file_name,ha(n.result)],c=Mt(n.occurred_at);c!==""&&s.push("le "+c),s.push("via "+sr(n.source));const _=n.reason===""?"":" "+n.reason;return`${s.join(" ")} — ${nr(n,t).oneLine}.${_}`}function Un(n){return n===""?"Aucun nouvel import enregistré à cet instant, et ce poste ne publie pas ce qu’il surveille.":"Aucun nouvel import enregistré à cet instant. Le poste surveille "+n+" : le fichier n’y était pas, ou il n’a pas encore fini d’arriver."}function Fn(n){return"Ce poste n’a pas de journal : il ne pourra rien dire de l’issue de cette relecture."+(n===""?"":" Le poste surveille "+n+".")}function Ua(n){return n===1?"voir la ligne":`voir les ${Rt(n)} lignes`}function Mn(n){return n.map(t=>`${Bn(t)} (${Rt(t.count)})`).join(", ")}function Bn(n){switch(n.code){case"PREPACKAGED_PRODUCT":return"préemballés";case"INTERNAL_CODE_NOT_WEIGHABLE":return n.value===""?"code interne":"code interne "+n.value;case"NO_BARCODE":return"sans code-barres";default:return n.code}}function Rt(n){return String(n)}var Vn=o(''),Wn=o(''),Gn=o('
Le poste y cherche le fichier , et le supprime
- une fois lu : c’est ce qui dit au producteur que la livraison est prise.
`,1),ns=o(`
Sur un serveur WebDAV, le dépôt d’un fichier CSV depuis cet écran n’est plus
- possible : le poste n’a plus de répertoire local où l’écrire. C’est le seul recours
- du jour de la mise en service.
`,1),ss=o(`
Ce poste ne déclare aucune source : choisissez-en une ci-dessus, sinon il n’ira
- chercher aucun catalogue.
`),ls=o('
',1),is=o('
Aucun import enregistré sur ce poste.
'),os=o('
'),us=o(`
« Oublier la quarantaine » fait relire un fichier que le poste avait écarté : c’est le
- seul geste de cette page qui puisse remettre en service un catalogue refusé.
`,1),cs=o(`
Un produit masqué reste vendable : la caisse lit toujours son code-barres, et une
- étiquette déjà imprimée reste valable. Ce réglage ne fait que retirer sa tuile.
`,1),ds=o('
'),vs=o(`
Déposez ici le fichier clé
Ce dépôt remplace toute la grille par le fichier apporté : il change ce que le poste
- vend, et le mot de passe est donc demandé au moment du dépôt.
`,1),ps=o('
'),fs=o('
Aucune anomalie sur le dernier import.
'),gs=o('
',1),ms=o('
'),hs=o('
Aucune unité divergente sur le dernier import.
'),_s=o('
',1),bs=o('
'),ws=o('
Aucun produit non pesable sur le dernier import.
'),ys=o('
',1),xs=o('
Aucun import enregistré : rien n’a encore pu être retiré.
'),ks=o('
Aucun produit retiré par le dernier import.
'),qs=o(`
Ils restent enregistrés avec leur historique : une étiquette déjà collée reste
- lisible en caisse, et un produit qui revient dans un prochain fichier retrouve sa
- tuile. Ce poste n’en publie encore que le nombre — leurs noms se lisent dans Odoo,
- en comparant avec l’export précédent.
`,1),Ss=o('
'),Ls=o(`
Un produit retiré de la grille ne se trouve plus ici : il se reprend dans
- « Décisions en vigueur », plus bas.
`,1),Es=o(`
Sans motif, le poste refuse la décision. Le seul acte qui s’en passe est celui qui
- EFFACE la décision en vigueur : proposer de nouveau un produit qui ne porte aucune
- dérogation.
`),zs=o(`
Produit choisi :
Ce produit est-il proposé dans la grille ?
La dérogation s’enregistre seule : elle ne remet pas dans la grille un produit
- qui en a été retiré, et retirer un produit n’efface pas sa dérogation.
`),js=o(' ',1),Ps=o('
Aucune décision locale : la grille est celle du fichier.
'),Ts=o('
'),Cs=o('
',1),Os=o('
'),Rs=o('
Aucun import dans l’historique.
'),As=o('
'),Ns=o('
Quand
Fichier
Source
Résultat
Motif
Lues
Pesables
Non pesables
Anomalies
Retirés
',1),$s=o('
');function Ds(n,t){Nt(t,!0);const s=(b,G=ia)=>{var oe=ts(),qe=a(oe),Je=r(qe,2),Se=a(Je),Pe=a(Se),j=r(Se,2);{var U=Ke=>{var Ye=es(),ue=a(Ye);v(()=>i(ue,G().path)),l(Ke,Ye)};g(j,Ke=>{Ht.showTechnicalNames&&Ke(U)})}var xe=r(j,2),Xe=a(xe);v((Ke,Ye)=>{Ue(oe,"data-flag",G().path),Ue(oe,"data-on",Ke),la(qe,Ye),i(Pe,G().label),i(Xe,G().hint)},[()=>String(t.draft.flag(G().path)),()=>t.draft.flag(G().path)]),Ze("change",qe,Ke=>t.draft.set(G().path,Ke.currentTarget.checked)),l(b,oe)},c=vt(t,"admin",7),_=20,w=50,T=20,A=["NO_BARCODE","PREPACKAGED_PRODUCT","INTERNAL_CODE_NOT_WEIGHABLE"],M={local_drop:["catalog.options.directory"],webdav:["catalog.options.url","catalog.options.username","catalog.options.password"]},H=u(()=>t.draft.text("catalog.type"));let J=F(Lt([])),pe=F(Lt([])),fe=F(Lt([])),_e=F(""),ae=F(""),C=F(""),ge=F(""),Q=F(!1),be=F(""),se=F(""),P=F("loading"),ne=F("loading");const V=u(()=>c().busy||e(se)!==""),B=u(()=>e(pe).filter(b=>b.issue==="anomaly")),re=u(()=>e(pe).filter(b=>A.includes(b.code))),N=u(()=>e(pe).filter(b=>b.code==="UNIT_MISMATCH")),L=u(()=>new Map(e(fe).map(b=>[b.id,b.name]))),q=u(()=>e(fe).filter(b=>b.mode==="by_unit").length),D=u(()=>{if(e(ne)==="loading")return"Lecture du catalogue en service…";if(e(ne)==="unread")return"Le catalogue en service n’a pas pu être lu : cet écran ne sait pas combien de produits se vendent à l’unité.";if(e(q)===0)return"Aucun produit vendu à l’unité dans le catalogue en service.";const b=e(q)>1,G=b?"produits vendus à l’unité sont":"produit vendu à l’unité est",oe=t.draft.flag("ui.show_by_unit_products")?`${b?"montrés":"montré"} dans la grille de ce poste`:`${b?"masqués":"masqué"} sur ce poste`;return`${he(e(q))} ${G} ${oe}.`}),X=u(()=>e(_e)===""?[]:br(e(fe),wr,e(_e))),me=u(()=>e(X).slice(0,_)),ze=u(()=>e(B).slice(0,w)),Ce=u(()=>e(N).slice(0,w)),Le=u(()=>e(re).slice(0,w)),Z=u(()=>[...t.health.decisions].sort((b,G)=>G.decided_at.localeCompare(b.decided_at))),y=u(()=>e(Z).slice(0,T)),I=u(()=>e(ae)===""?null:e(Z).find(b=>b.product_id===e(ae))??null),W=u(()=>e(I)===null?!0:e(I).offered),ee=u(()=>e(I)?.min_weight_g??null),ce=u(()=>e(ge).trim()===""?null:Number(e(ge))),Ve=u(()=>e(ce)!==null&&Number.isFinite(e(ce))&&e(ce)>0),S=u(()=>e(C).trim()),h=u(()=>e(S)!==""||!Ae(!1,e(ee))),k=u(()=>e(S)!==""||!Ae(!0,e(ee))),O=u(()=>e(Ve)&&(e(S)!==""||!Ae(e(W),e(ce)))),le=u(()=>e(S)!==""||!Ae(e(W),null)),Re=u(()=>t.health.catalog?.products_withdrawn_count??0),We=u(()=>e(J).find(b=>t.health.catalog!==null&&b.id`${he(e(Re))} ${e(Re)>1?"produits retirés":"produit retiré"}`+(e(We)===null?".":` depuis l’import du ${Mt(e(We).occurred_at)}.`)),d=u(()=>e(X).length>e(me).length?`${he(e(me).length)} produits affichés sur ${he(e(X).length)} trouvés — précisez votre recherche.`:`${he(e(X).length)} ${e(X).length>1?"produits trouvés":"produit trouvé"}.`),x=u(()=>`${he(e(J).length)} ${e(J).length>1?"imports affichés":"import affiché"} : le poste n’en publie jamais plus de vingt.`),$=u(()=>e(P)==="loading"?"Lecture des signalements du dernier import…":"Les signalements du dernier import n’ont pas pu être lus : cet écran ne sait pas ce qu’ils disent."),f=u(()=>e(P)==="loading"?"Lecture de l’historique des imports…":"L’historique des imports n’a pas pu être lu : cet écran ne sait pas ce qu’il contient.");let E;Xt(()=>{const b=t.health.catalog?.id??null;b!==E&&(E=b,ye())});const z=new ir;Xt(()=>{z.observe(t.health)});async function ye(){const b=await c().load(()=>Qr(t.health.catalog?.id));b===null?p(P,"unread"):(p(J,b.imports,!0),p(pe,b.findings,!0),p(P,"read"));const G=await c().load(()=>Ja());if(G===null){p(ne,"unread");return}p(fe,G.products,!0),p(ne,"read")}async function ie(b,G){p(se,b,!0),c().actionError="",c().notice="",z.forget();try{const oe=await c().protect(G);return oe===null?null:(c().notice=oe.message,await c().refresh(),oe)}finally{p(se,"")}}async function ke(){const b=await ie("reload",Zr);b!==null&&z.begin(b)}function Oe(b){t.draft.set("catalog.type",b);for(const[G,oe]of Object.entries(M))if(G!==b)for(const qe of oe)t.draft.unset(qe)}function de(b){return t.draft.faults.find(G=>G.field===b)?.message??""}function K(b){p(ae,b,!0),p(C,"");const G=e(Z).find(oe=>oe.product_id===b)?.min_weight_g??null;p(ge,G===null?"":String(G),!0)}function Ae(b,G){return!(b&&G===null)}async function Ge(b){if(e(ae)==="")return;const G=e(ae),oe=e(ee),qe=e(S);await ie("offered",()=>Aa(G,{offered:b,min_weight_g:oe,reason:qe}))!==null&&p(C,"")}async function Fe(b){if(e(ae)==="")return;const G=e(ae),oe=e(W),qe=e(S);await ie(b===null?"waiver-off":"waiver",()=>Aa(G,{offered:oe,min_weight_g:b,reason:qe}))!==null&&p(C,"")}async function De(b){if(p(Q,!1),b!=null){p(be,""),c().actionError="",c().notice="",p(se,"import");try{const G=await c().protect(()=>Ya(b));if(G===null)return;p(be,`${b.name} : ${he(G.rows_read_count)} lignes lues, ${he(G.weighable_count)} pesables. `+(G.reason===""?"Le fichier est déposé ; son résultat s’inscrira dans l’historique des imports.":G.reason)),await c().refresh()}finally{p(se,"")}}}function Me(b){b.preventDefault(),p(Q,!1);const G=b.dataTransfer?.files.item(0);if(e(V)){p(be,`${G?.name??"Ce fichier"} n’a pas été déposé : un acte est déjà en cours sur cette page. Réessayez quand il aura répondu.`);return}De(G)}function te(b){const G=b.files?.item(0);b.value="",De(G)}function Ne(b){const G=e(L).get(b);return G!==void 0?G:e(ne)==="loading"?"Lecture du nom…":e(ne)==="unread"?"Nom inconnu : le catalogue n’a pas pu être lu":"Produit absent du catalogue en service"}function ve(b,G,oe,qe){const Je=G>1?qe:oe;return b>=G?`${he(G)} ${Je}.`:`${he(b)} lignes affichées sur ${he(G)} ${Je}.`}function je(b){const G=[];return b.offered||G.push("retiré de la grille"),b.min_weight_g!==null&&G.push(`peut peser à partir de ${he(b.min_weight_g)} g`),G.length===0?"aucune restriction":G.join(" · ")}function $e(b){const G=ha(b.result);return b.code===""?G:`${G} (${b.code})`}function st(b){return sr(b.source)}var it=$s(),pt=a(it);Qe(pt,{title:"Où le poste va chercher le catalogue",children:(b,G)=>{var oe=ls(),qe=Y(oe),Je=a(qe),Se=a(Je),Pe=r(Je,2),j=a(Pe),U=r(qe,2);{var xe=ue=>{var Ie=as();l(ue,Ie)},Xe=ue=>{var Ie=rs(),Be=Y(Ie);{let ut=u(()=>Gt("catalog.options.directory")),ht=u(()=>t.draft.text("catalog.options.directory")),kt=u(()=>de("catalog.options.directory"));_t(Be,{get label(){return e(ut)},path:"catalog.options.directory",get value(){return e(ht)},hint:"Laissez vide pour le répertoire du poste, celui que le service crée lui-même. Un répertoire nommé ici doit exister : le poste ne le crée pas.",get fault(){return e(kt)},onchange:zt=>t.draft.set("catalog.options.directory",zt)})}var ct=r(Be,2),dt=r(a(ct)),ot=a(dt);v(()=>i(ot,`flv_${t.health.station??""}.csv`)),l(ue,Ie)},Ke=ue=>{var Ie=ns(),Be=Y(Ie);{let ot=u(()=>Gt("catalog.options.url")),ut=u(()=>t.draft.text("catalog.options.url")),ht=u(()=>de("catalog.options.url"));_t(Be,{get label(){return e(ot)},path:"catalog.options.url",get value(){return e(ut)},get fault(){return e(ht)},onchange:kt=>t.draft.set("catalog.options.url",kt)})}var ct=r(Be,2);{let ot=u(()=>Gt("catalog.options.username")),ut=u(()=>t.draft.text("catalog.options.username")),ht=u(()=>de("catalog.options.username"));_t(ct,{get label(){return e(ot)},path:"catalog.options.username",get value(){return e(ut)},get fault(){return e(ht)},onchange:kt=>t.draft.set("catalog.options.username",kt)})}var dt=r(ct,2);{let ot=u(()=>Gt("catalog.options.password")),ut=u(()=>de("catalog.options.password"));_t(dt,{get label(){return e(ot)},path:"catalog.options.password",kind:"password",value:"",hint:"Laissez vide : le mot de passe actuel est conservé.",get fault(){return e(ut)},onchange:ht=>t.draft.set("catalog.options.password",ht)})}l(ue,Ie)},Ye=ue=>{var Ie=ss();l(ue,Ie)};g(U,ue=>{t.draft.config===null?ue(xe):e(H)==="local_drop"?ue(Xe,1):e(H)==="webdav"?ue(Ke,2):ue(Ye,-1)})}v(()=>{la(Se,e(H)==="local_drop"),la(j,e(H)==="webdav")}),Ze("change",Se,()=>Oe("local_drop")),Ze("change",j,()=>Oe("webdav")),l(b,oe)},$$slots:{default:!0}});var Ee=r(pt,2);Qe(Ee,{title:"Dernier import",children:(b,G)=>{var oe=us(),qe=Y(oe);{var Je=ue=>{var Ie=is();l(ue,Ie)},Se=ue=>{lr(ue,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives}})};g(qe,ue=>{t.health.catalog===null?ue(Je):ue(Se,-1)})}var Pe=r(qe,2),j=a(Pe),U=r(Pe,2);{var xe=ue=>{var Ie=os(),Be=a(Ie);v(()=>i(Be,z.sentence)),l(ue,Ie)};g(U,ue=>{z.sentence!==""&&ue(xe)})}var Xe=r(U,2),Ke=a(Xe);{let ue=u(()=>e(se)==="reload");tt(Ke,{act:"reload",kind:"write",label:"Recharger le catalogue",protected:!0,get busy(){return e(ue)},get disabled(){return e(V)},onrun:()=>{ke()}})}var Ye=r(Ke,2);{let ue=u(()=>e(se)==="quarantine");tt(Ye,{act:"quarantine",kind:"destructive",label:"Oublier la quarantaine",protected:!0,get busy(){return e(ue)},get disabled(){return e(V)},onrun:()=>{ie("quarantine",en)}})}v(()=>i(j,t.health.catalog_source===null?"Aucune source de catalogue publiée par ce poste.":"Source : "+t.health.catalog_source.label)),l(b,oe)},$$slots:{default:!0}});var nt=r(Ee,2);Qe(nt,{title:"Ce que la grille montre",note:"Un réglage d’affichage : il ne change ni le fichier reçu, ni ce que le poste sait peser.",children:(b,G)=>{var oe=cs(),qe=Y(oe);s(qe,()=>({path:"ui.show_by_unit_products",label:"Afficher les produits vendus à l’unité",hint:"Décoché, leurs tuiles quittent la grille et la recherche ne les retrouve plus. Ce que le poste perd : une tuile vendue à l’unité imprime une étiquette sans jamais lire la balance, et c’est le seul geste que ce réglage retire."}));var Je=r(qe,2),Se=a(Je);v(()=>i(Se,e(D))),l(b,oe)},$$slots:{default:!0}});var et=r(nt,2);Qe(et,{title:"Déposer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(b,G)=>{var oe=vs(),qe=Y(oe);{var Je=ue=>{var Ie=ds(),Be=a(Ie);v(()=>i(Be,e(be))),l(ue,Ie)};g(qe,ue=>{e(be)!==""&&ue(Je)})}var Se=r(qe,2);let Pe;var j=a(Se),U=r(a(j)),xe=a(U),Xe=r(j,2),Ke=a(Xe),Ye=r(Ke);v(()=>{Pe=Zt(Se,1,"drop svelte-3gk7u7",null,Pe,{dropping:e(Q),working:e(se)==="import"}),i(xe,`flv_${t.health.station??""}.csv`),i(Ke,`${e(se)==="import"?"Import en cours…":"Choisir un fichier"} `),Ye.disabled=e(V)}),na("dragover",Se,ue=>{ue.preventDefault(),!e(V)&&p(Q,!0)}),na("dragleave",Se,()=>p(Q,!1)),na("drop",Se,Me),Ze("change",Ye,ue=>te(ue.currentTarget)),l(b,oe)},$$slots:{default:!0}});var mt=r(et,2);Qe(mt,{title:"Anomalies à corriger dans Odoo",note:"Chaque ligne porte son numéro dans le CSV, son motif et la valeur fautive.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=ps(),xe=a(U);v(()=>i(xe,e($))),l(j,U)},Se=j=>{var U=fs();l(j,U)},Pe=j=>{var U=gs(),xe=Y(U),Xe=a(xe),Ke=r(Xe);{var Ye=Ie=>{var Be=xt("Corrigez celles-ci dans Odoo : l’import suivant ne signalera que ce qui reste.");l(Ie,Be)};g(Ke,Ie=>{e(B).length>e(ze).length&&Ie(Ye)})}var ue=r(xe,2);ya(ue,()=>e(ze),()=>"anomalies"),v(Ie=>i(Xe,`${Ie??""} `),[()=>ve(e(ze).length,e(B).length,"anomalie","anomalies")]),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(B).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Dt=r(mt,2);Qe(Dt,{title:"Unités divergentes",note:"Le produit reste proposé : le code-barres fait foi, seul le libellé du prix est faux.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=ms(),xe=a(U);v(()=>i(xe,e($))),l(j,U)},Se=j=>{var U=hs();l(j,U)},Pe=j=>{var U=_s(),xe=Y(U),Xe=a(xe),Ke=r(xe,2);ya(Ke,()=>e(Ce),()=>"mismatches"),v(Ye=>i(Xe,Ye),[()=>ve(e(Ce).length,e(N).length,"unité divergente","unités divergentes")]),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(N).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Bt=r(Dt,2);Qe(Bt,{title:"Produits non pesables",note:"Un inventaire, pas une liste d’erreurs : ces produits portent déjà leur code-barres et n’ont aucune raison d’être pesés.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=bs(),xe=a(U);v(()=>i(xe,e($))),l(j,U)},Se=j=>{var U=ws();l(j,U)},Pe=j=>{var U=ys(),xe=Y(U),Xe=a(xe),Ke=r(xe,2);ya(Ke,()=>e(Le),()=>"not-weighable"),v(Ye=>i(Xe,Ye),[()=>ve(e(Le).length,e(re).length,"produit non pesable","produits non pesables")]),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(re).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Vt=r(Bt,2);Qe(Vt,{title:"Produits retirés depuis l’import précédent",note:"Un produit absent du nouveau fichier est marqué retiré à sa date, jamais supprimé.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=xs();l(j,U)},Se=j=>{var U=ks();l(j,U)},Pe=j=>{var U=qs(),xe=Y(U),Xe=a(xe);v(()=>i(Xe,e(we))),l(j,U)};g(qe,j=>{t.health.catalog===null?j(Je):e(Re)===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Et=r(Vt,2);Qe(Et,{title:"Décider d’un produit",note:"Retirer un produit et l’autoriser à peser moins sont deux décisions séparées : l’une n’efface pas l’autre.",children:(b,G)=>{var oe=js(),qe=r(Y(oe),2),Je=r(qe,2);{var Se=U=>{var xe=Ls(),Xe=Y(xe),Ke=a(Xe),Ye=r(Xe,2),ue=a(Ye);at(ue,21,()=>e(me),Ie=>Ie.id,(Ie,Be)=>{var ct=Ss(),dt=a(ct),ot=a(dt),ut=r(dt,2),ht=a(ut),kt=r(ut,2),zt=a(kt);v(()=>{i(ot,e(Be).name),i(ht,e(Be).id),i(zt,`${e(Be).unit_price_text??""}${e(Be).price_suffix??""}`)}),Ze("click",dt,()=>K(e(Be).id)),l(Ie,ct)}),v(()=>i(Ke,e(d))),l(U,xe)};g(Je,U=>{e(_e)!==""&&U(Se)})}var Pe=r(Je,2);{var j=U=>{var xe=zs(),Xe=a(xe),Ke=r(a(Xe)),Ye=a(Ke),ue=r(Ke),Ie=r(Xe,2),Be=a(Ie),ct=r(Ie,4),dt=r(ct,2);{var ot=rt=>{var yt=Es();l(rt,yt)};g(dt,rt=>{e(S)===""&&rt(ot)})}var ut=r(dt,2),ht=r(a(ut),2),kt=a(ht);{var zt=rt=>{{let yt=u(()=>e(se)==="offered"),Yt=u(()=>e(V)||!e(h));tt(rt,{act:"offered",kind:"destructive",label:"Ne plus proposer ce produit",protected:!0,get busy(){return e(yt)},get disabled(){return e(Yt)},onrun:()=>{Ge(!1)}})}},qt=rt=>{{let yt=u(()=>e(se)==="offered"),Yt=u(()=>e(V)||!e(k));tt(rt,{act:"offered",kind:"write",label:"Le proposer de nouveau",protected:!0,get busy(){return e(yt)},get disabled(){return e(Yt)},onrun:()=>{Ge(!0)}})}};g(kt,rt=>{e(W)?rt(zt):rt(qt,-1)})}var jt=r(ut,2),Ct=r(a(jt),2),It=r(Ct,2),Wt=a(It);{let rt=u(()=>e(se)==="waiver"),yt=u(()=>e(V)||!e(O));tt(Wt,{act:"waiver",kind:"write",label:"Enregistrer la dérogation",protected:!0,get busy(){return e(rt)},get disabled(){return e(yt)},onrun:()=>{Fe(e(ce))}})}var Jt=r(Wt,2);{var ta=rt=>{{let yt=u(()=>e(se)==="waiver-off"),Yt=u(()=>e(V)||!e(le));tt(rt,{act:"waiver-off",kind:"write",label:"Retirer la dérogation",protected:!0,get busy(){return e(yt)},get disabled(){return e(Yt)},onrun:()=>{Fe(null)}})}};g(Jt,rt=>{e(ee)!==null&&rt(ta)})}v((rt,yt)=>{Ue(xe,"data-decision",e(ae)),i(Ye,rt),i(ue,` (${e(ae)??""})`),i(Be,`En vigueur : ${yt??""}`),oa(Ct,e(ge))},[()=>Ne(e(ae)),()=>e(I)===null?"aucune décision — ce produit suit les règles générales":je(e(I))]),ca(ct,()=>e(C),rt=>p(C,rt)),Ze("input",Ct,rt=>p(ge,rt.currentTarget.value,!0)),l(U,xe)};g(Pe,U=>{e(ae)!==""&&U(j)})}ca(qe,()=>e(_e),U=>p(_e,U)),l(b,oe)},$$slots:{default:!0}});var bt=r(Et,2);Qe(bt,{title:"Décisions en vigueur",note:"Ce qu’un humain a décidé, produit par produit. C’est ici que se reprend un produit retiré de la grille.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=Pe=>{var j=Ps();l(Pe,j)},Se=Pe=>{var j=Cs(),U=Y(j),xe=a(U),Xe=r(U,2),Ke=a(Xe);at(Ke,21,()=>e(y),Ye=>Ye.product_id,(Ye,ue)=>{var Ie=Ts(),Be=a(Ie),ct=r(Be,2),dt=a(ct),ot=r(ct,2),ut=a(ot),ht=r(ot,2),kt=a(ht),zt=r(ht,2),qt=a(zt),jt=r(zt,2),Ct=a(jt);v((It,Wt,Jt)=>{i(dt,It),i(ut,e(ue).product_id),i(kt,Wt),i(qt,e(ue).reason),i(Ct,Jt)},[()=>Ne(e(ue).product_id),()=>je(e(ue)),()=>pa(e(ue).decided_at)]),Ze("click",Be,()=>K(e(ue).product_id)),l(Ye,Ie)}),v(Ye=>i(xe,Ye),[()=>ve(e(y).length,e(Z).length,"décision en vigueur","décisions en vigueur")]),l(Pe,j)};g(qe,Pe=>{e(Z).length===0?Pe(Je):Pe(Se,-1)})}l(b,oe)},$$slots:{default:!0}});var Tt=r(bt,2);Qe(Tt,{title:"Vingt derniers imports",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=Os(),xe=a(U);v(()=>i(xe,e(f))),l(j,U)},Se=j=>{var U=Rs();l(j,U)},Pe=j=>{var U=Ns(),xe=Y(U),Xe=a(xe),Ke=r(xe,2),Ye=a(Ke),ue=r(a(Ye));at(ue,21,()=>e(J),Ie=>Ie.id,(Ie,Be)=>{var ct=As(),dt=a(ct),ot=a(dt),ut=r(dt),ht=a(ut),kt=r(ut),zt=a(kt),qt=r(kt),jt=a(qt),Ct=r(qt),It=a(Ct),Wt=r(Ct),Jt=a(Wt),ta=r(Wt),rt=a(ta),yt=r(ta),Yt=a(yt),m=r(yt),R=a(m),Te=r(m),He=a(Te);v((lt,ft,wt,Pt,Ot,St,Kt,Qt)=>{i(ot,lt),i(ht,e(Be).file_name),i(zt,ft),i(jt,wt),i(It,e(Be).reason),i(Jt,Pt),i(rt,Ot),i(Yt,St),i(R,Kt),i(He,Qt)},[()=>Mt(e(Be).occurred_at),()=>st(e(Be)),()=>$e(e(Be)),()=>he(e(Be).rows_read_count),()=>he(e(Be).weighable_count),()=>he(e(Be).not_weighable_count),()=>he(e(Be).anomalies_count),()=>he(e(Be).products_withdrawn_count)]),l(Ie,ct)}),v(()=>i(Xe,e(x))),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(J).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}}),l(n,it),$t()}At(["change","click","input"]);function Is(n){switch(n){case"ok":return"OK";case"warn":return"À SURVEILLER";case"fault":return"EN PANNE";case"off":return"SANS OBJET";default:return"INCONNU"}}var Us=o('
'),Fs=o('
');function Ms(n,t){Nt(t,!0);var s=Fs(),c=a(s),_=a(c),w=r(_,2),T=a(w),A=r(w,2),M=a(A),H=r(c,2),J=a(H),pe=r(H,2);{var fe=_e=>{var ae=Us(),C=a(ae);v(()=>i(C,t.light.remedy)),l(_e,ae)};g(pe,_e=>{t.light.remedy!==""&&_e(fe)})}v(_e=>{Ue(s,"data-light",t.light.id),Ue(s,"data-level",t.light.level),Ue(_,"data-level",t.light.level),i(T,t.light.label),i(M,_e),i(J,t.light.value)},[()=>Is(t.light.level)]),l(n,s),$t()}function Bs(n){return[Vs(n),Ws(n),Hs(n),Js(n),Xs(n),Ys(n)]}function Vs(n){const t=n.state.scale,s=va(t.median_ms);return n.scale_present?t.connected?t.too_slow?{id:"scale",label:"Balance",level:"warn",value:`une mesure toutes les ${s}, plus lent que la péremption`,remedy:"À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante. Vérifiez le câble et l’adaptateur USB, puis la cadence sur la page Matériel."}:{id:"scale",label:"Balance",level:"ok",value:Qs(t.provisional,`une mesure toutes les ${s}`),remedy:""}:{id:"scale",label:"Balance",level:"fault",value:"elle ne répond plus",remedy:"Vérifiez le câble et l’alimentation de la balance, puis touchez « Tester la balance ». En attendant, « Basculer en saisie manuelle » permet de continuer à servir."}:{id:"scale",label:"Balance",level:"off",value:"ce poste est déclaré sans balance",remedy:""}}function Ws(n){const t=n.state.printer;switch(t.health){case"faulted":return{id:"printer",label:"Imprimante",level:"fault",value:t.detail===""?"elle ne peut pas imprimer":t.detail,remedy:Gs(n)};case"consumable":return{id:"printer",label:"Imprimante",level:"warn",value:"elle imprime, mais le rouleau arrive en fin de vie",remedy:"Changez le rouleau quand vous passez derrière le comptoir, puis touchez « J’ai changé le rouleau ». Le poste continue de servir en attendant."};case"ready":return{id:"printer",label:"Imprimante",level:"ok",value:"elle répond et n’a rien à signaler",remedy:""};default:return{id:"printer",label:"Imprimante",level:"unknown",value:"elle prend les étiquettes et ne dit rien en retour",remedy:"C’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne. Pour savoir si elle imprime, touchez « Imprimer une étiquette de test »."}}}function Gs(n){const t="Regardez le capot, le rouleau et le câble, puis touchez « Tester l’imprimante ».";return n.printing?.fallback_available===!0?t+" Le poste peut servir en attendant : « Imprimer sur l’imprimante du poste voisin ».":t}function Hs(n){const t=n.roll;return t===null?{id:"roll",label:"Rouleau",level:"unknown",value:"aucun compteur d’étiquettes sur ce poste",remedy:`Sans imprimante construite, il n’y a pas de rouleau à compter : vérifiez le « ${Gt("printer.type")} » et ses réglages sur la page Matériel.`}:t.known?t.level==="warn"?{id:"roll",label:"Rouleau",level:"warn",value:t.message,remedy:"Changez le rouleau, puis touchez « J’ai changé le rouleau »."}:{id:"roll",label:"Rouleau",level:"ok",value:t.message,remedy:""}:{id:"roll",label:"Rouleau",level:"unknown",value:t.message,remedy:"Touchez « J’ai changé le rouleau » en mettant un rouleau neuf : c’est le seul geste qui dise quelque chose de vrai du papier."}}function Js(n){const t=n.catalog_source?.label??"";if(n.state.catalog_count===0)return{id:"catalog",label:"Catalogue",level:"fault",value:"aucun produit dans la grille",remedy:Ks(t)};const s=n.catalog;return s!==null&&s.result!=="applied"&&s.result!=="unchanged"?{id:"catalog",label:"Catalogue",level:"warn",value:`dernier fichier refusé : ${s.reason===""?s.result:s.reason}`,remedy:"La grille tourne toujours sur le catalogue précédent. Corrigez le fichier dans Odoo, ou touchez « Oublier la quarantaine » puis redéposez-le."}:{id:"catalog",label:"Catalogue",level:"ok",value:`${he(n.state.catalog_count)} produits dans la grille`,remedy:""}}function Ks(n){return"Déposez le fichier du catalogue là où le poste le guette, ou utilisez « Importer un catalogue » ci-contre pour le glisser depuis une clé USB."+(n===""?"":` Ce poste surveille : ${n}.`)}function Xs(n){const t=n.disk;if(t===null)return{id:"disk",label:"Disque",level:"unknown",value:"la place libre n’a pas pu être mesurée",remedy:"Téléchargez le fichier de diagnostic : il porte ce que le poste a pu lire du volume, et c’est la pièce qu’un support demandera."};const s=ka(t.free_bytes),c=`seuil ${he(t.alert_mb)} Mo`;return t.free_bytes===0?{id:"disk",label:"Disque",level:"fault",value:`plus un octet libre sur ${t.path}`,remedy:"Le journal ne peut plus rien écrire, alors que les étiquettes continuent de sortir. Faites de la place sur le disque, puis rechargez cette page."}:t.alert_mb>0&&t.free_bytes0?{id:"journal",label:"Journal",level:"fault",value:`${he(t)} pesées imprimées mais non enregistrées`,remedy:"Les étiquettes sortent, les ventes sont bonnes, c’est la trace qui manque. Téléchargez le fichier de diagnostic et prévenez le support : ne redémarrez rien."}:n.counters.journal_rows_count<0?{id:"journal",label:"Journal",level:"unknown",value:"ce poste n’a pas de journal ouvert",remedy:"Le poste pèse et imprime quand même. Téléchargez le fichier de diagnostic : il dit pourquoi la base n’a pas pu être ouverte."}:{id:"journal",label:"Journal",level:"ok",value:`${he(n.counters.journal_rows_count)} pesées enregistrées`,remedy:""}}function Qs(n,t){return n?t+" (cadence encore provisoire)":t}function Zs(n){return n===null||!n.known?"unknown":n.configured?"ok":"missing"}var el=o("Une mesure toutes les ",1),tl=o(''),al=o("Source : ",1),rl=o(`
Aucun import enregistré sur ce poste : le catalogue n’est jamais arrivé, ou le
- journal ne le porte pas.
`),nl=o('
',1),sl=o(' ',1),ll=o('
Aucune décision locale : le catalogue est proposé tel qu’il arrive.
'),il=o('
'),ol=o(''),ul=o('
'),cl=o('
',1),dl=o(`INCONNU — la question n’a pas pu être posée à ce système. Ce
- n’est pas « non configuré » : personne ne sait encore.`,1),vl=o('OK ',1),pl=o('NON CONFIGURÉ ',1),fl=o('
'),gl=o(' ',1),ml=o('
Rien à signaler depuis le démarrage du poste.
'),hl=o(''),_l=o('
'),bl=o('
'),wl=o(''),yl=o('
'),xl=o('
Numéro de poste
Nom
Coopérative
Version
Empreinte de configuration
',1),kl=o('
');function ql(n,t){Nt(t,!0);const s=u(()=>t.health.new_version),c=u(()=>Bs(t.health)),_=u(()=>t.health.state.scale),w=u(()=>t.health.unattended_restart),T=u(()=>t.health.decisions),A=5,M=u(()=>e(T).slice(0,A));var H=kl(),J=a(H);at(J,21,()=>e(c),Q=>Q.id,(Q,be)=>{Ms(Q,{get light(){return e(be)}})});var pe=r(J,2);Qe(pe,{title:"Cadence de la balance",children:(Q,be)=>{var se=tl(),P=a(se);{var ne=N=>{var L=xt("Ce poste est déclaré sans balance : le poids est saisi à la main.");l(N,L)},V=N=>{var L=xt(`La balance ne répond pas : aucune cadence n’est mesurable tant qu’aucune trame
- n’arrive.`);l(N,L)},B=N=>{var L=xt(`Aucun intervalle n’a encore été mesuré : la cadence apparaîtra dès les premières
- trames.`);l(N,L)},re=N=>{var L=el(),q=r(Y(L)),D=a(q),X=r(q),me=r(X);{var ze=Z=>{var y=xt("C’est encore une valeur d’attente : moins de huit intervalles ont été observés.");l(Z,y)};g(me,Z=>{e(_).provisional&&Z(ze)})}var Ce=r(me,2);{var Le=Z=>{var y=xt("À cette cadence, un poids serait périmé avant l’arrivée de la mesure suivante.");l(Z,y)};g(Ce,Z=>{e(_).too_slow&&Z(Le)})}v((Z,y)=>{i(D,Z),i(X,`, médiane
- observée sur ${y??""} intervalles. `)},[()=>va(e(_).median_ms),()=>he(e(_).observations_count)]),l(N,L)};g(P,N=>{t.health.scale_present?e(_).connected?e(_).observations_count===0?N(B,2):N(re,-1):N(V,1):N(ne)})}l(Q,se)},$$slots:{default:!0}});var fe=r(pe,2);Qe(fe,{title:"Catalogue",children:(Q,be)=>{var se=sl(),P=Y(se),ne=a(P);{var V=q=>{var D=xt("Aucune source de catalogue n’est publiée par ce poste.");l(q,D)},B=q=>{var D=al(),X=r(Y(D)),me=a(X);v(()=>i(me,t.health.catalog_source.label)),l(q,D)};g(ne,q=>{t.health.catalog_source===null?q(V):q(B,-1)})}var re=r(P,2);{var N=q=>{var D=rl();l(q,D)},L=q=>{var D=nl(),X=Y(D),me=a(X),ze=r(X,2);lr(ze,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives},get onshowrows(){return t.onshowrows}}),v((Ce,Le)=>i(me,`Dernier essai : ${Ce??""} —
- ${Le??""}${t.health.catalog.file_name===""?"":" ("+t.health.catalog.file_name+")"}${t.health.catalog.reason===""?"":" : "+t.health.catalog.reason}`),[()=>Mt(t.health.catalog.occurred_at),()=>ha(t.health.catalog.result)]),l(q,D)};g(re,q=>{t.health.catalog===null?q(N):q(L,-1)})}l(Q,se)},$$slots:{default:!0}});var _e=r(fe,2);Qe(_e,{title:"Décisions locales en vigueur",note:"Ce qu’un humain a décidé de ce catalogue, avec son motif et sa date.",children:(Q,be)=>{var se=Ut(),P=Y(se);{var ne=B=>{var re=ll();l(B,re)},V=B=>{var re=cl(),N=Y(re);{var L=D=>{var X=il(),me=a(X);v(ze=>i(me,`${ze??""} décisions en vigueur — les
- 5 plus récentes ci-dessous. La liste entière est sur la page
- Catalogue.`),[()=>he(e(T).length)]),l(D,X)};g(N,D=>{e(T).length>A&&D(L)})}var q=r(N,2);at(q,21,()=>e(M),D=>D.product_id,(D,X)=>{var me=ul(),ze=a(me),Ce=a(ze),Le=r(ze,2);{var Z=ce=>{var Ve=ol(),S=a(Ve);v(h=>i(S,`peut peser à partir de ${h??""} g`),[()=>he(e(X).min_weight_g)]),l(ce,Ve)};g(Le,ce=>{e(X).min_weight_g!==null&&ce(Z)})}var y=r(Le,2),I=a(y),W=r(y,2),ee=a(W);v(ce=>{i(Ce,`${e(X).offered?"Dérogation de poids":"Produit retiré"} — ${e(X).product_id??""}`),i(I,e(X).reason),i(ee,`${ce??""}, ${e(X).decided_by??""}`)},[()=>pa(e(X).decided_at)]),l(D,me)}),l(B,re)};g(P,B=>{e(T).length===0?B(ne):B(V,-1)})}l(Q,se)},$$slots:{default:!0}});var ae=r(_e,2);Qe(ae,{title:"Redémarrage sans intervention",children:(Q,be)=>{var se=gl(),P=Y(se),ne=a(P);{var V=q=>{var D=dl();l(q,D)},B=q=>{var D=vl(),X=r(Y(D));v(()=>i(X,` — après une coupure de courant, ce poste revient seul sur
- l’écran client. ${e(w).detail??""}`)),l(q,D)},re=q=>{var D=pl(),X=r(Y(D));v(()=>i(X,` — ${e(w).detail??""}`)),l(q,D)};g(ne,q=>{e(w)===null||!e(w).known?q(V):e(w).configured?q(B,1):q(re,-1)})}var N=r(P,2);{var L=q=>{var D=fl(),X=a(D);v(()=>i(X,e(w).remedy)),l(q,D)};g(N,q=>{e(w)!==null&&e(w).known&&!e(w).configured&&e(w).remedy!==""&&q(L)})}v(q=>Ue(P,"data-verdict",q),[()=>Zs(e(w))]),l(Q,se)},$$slots:{default:!0}});var C=r(ae,2);Qe(C,{title:"Dix derniers événements",children:(Q,be)=>{var se=Ut(),P=Y(se);{var ne=B=>{var re=ml();l(B,re)},V=B=>{var re=bl();at(re,21,()=>t.health.events,N=>N.id,(N,L)=>{var q=_l(),D=a(q),X=a(D),me=r(D,2),ze=a(me),Ce=r(me,2);{var Le=I=>{var W=hl(),ee=a(W);v(()=>i(ee,e(L).code)),l(I,W)};g(Ce,I=>{e(L).code!==""&&Ht.showTechnicalNames&&I(Le)})}var Z=r(Ce,2),y=a(Z);v((I,W)=>{Ue(q,"data-level",e(L).level),i(X,I),i(ze,W),i(y,e(L).message)},[()=>An(e(L).occurred_at),()=>tr(e(L).source)]),l(N,q)}),l(B,re)};g(P,B=>{t.health.events.length===0?B(ne):B(V,-1)})}l(Q,se)},$$slots:{default:!0}});var ge=r(C,2);Qe(ge,{title:"Ce poste",children:(Q,be)=>{var se=xl(),P=Y(se),ne=r(a(P),2),V=a(ne),B=r(ne,4),re=a(B),N=r(B,4),L=a(N),q=r(N,4),D=a(q),X=r(q,4),me=a(X),ze=r(P,2);{var Ce=Le=>{var Z=yl(),y=a(Z),I=r(y);{var W=ee=>{var ce=wl();Ze("click",ce,function(...Ve){t.onshowupdate?.apply(this,Ve)}),l(ee,ce)};g(I,ee=>{t.onshowupdate!==void 0&&ee(W)})}v(()=>{Ue(Z,"data-update-available",e(s)),i(y,`Version ${e(s)??""} disponible. `)}),l(Le,Z)};g(ze,Le=>{e(s)!==""&&Le(Ce)})}v(()=>{i(V,t.health.station),i(re,t.health.station_name===""?"non renseigné":t.health.station_name),i(L,t.health.coop),i(D,t.health.version),i(me,t.health.config_fingerprint)}),l(Q,se)},$$slots:{default:!0}}),l(n,H),$t()}At(["click"]);var Sl=o(''),Ll=o(`
Lecture de la configuration en cours… tant qu’elle n’est pas arrivée, cette page ne
- déclare rien de ce poste.
`),Fa=o('
'),El=o('
'),zl=o('
',1),jl=o('
'),Pl=o('
',1),Tl=o('
'),Cl=o('
'),Ol=o(''),Rl=o('
',1),Al=o('
Balance
L’état et la cadence viennent de ce que le poste observe vraiment, jamais d’un réglage.
Réglages série de la balance
Imprimante
Réglages de l’imprimante
');function Nl(n,t){Nt(t,!0);const s=vt(t,"admin",7),c=20,_=3,w=250,T=50,A=_*1e3+w+1e3,M=12;let H=F(Lt([])),J=F(Lt([])),pe=F(Lt([])),fe=F(Lt([])),_e=F(""),ae=F(!1),C=F(""),ge=F(0),Q=F(0),be=F(!1),se=F(null),P=F(!1),ne=F(!1),V=!1;const B=u(()=>t.health.state.scale),re=u(()=>t.health.state.printer),N=u(()=>t.draft.config!==null),L=u(()=>e(N)&&!t.draft.flag("scale.present")),q=u(()=>t.draft.text("scale.options.port")),D=u(()=>e(se)!==null&&e(se).port===e(q)?e(se).reason:""),X=u(()=>e(_e)===e(q)?e(fe):[]),me=u($),ze=u(E),Ce=u(ie),Le=u(de),Z=u(()=>Me("scale.type")!==""||Me("scale.options.port")!==""),y=u(()=>Me("printer.type")!==""||Me("printer.options.transport")!==""||Me("printer.options.queue")!=="");Xt(()=>{e(Z)&&p(P,!0)}),Xt(()=>{e(y)&&p(ne,!0)}),Lr(()=>{V=!0}),Ea(()=>{k("ports",O)}),Xt(()=>{e(be)||!I(e(q))||ee(e(q))});function I(m){return!V&&e(N)&&!e(L)&&m!==""&&m===e(q)&&W(m)&&e(D)===""&&e(C)===""}function W(m){return e(H).some(R=>R.name===m)}async function ee(m){p(be,!0);try{for(;I(m);){try{Ve(m,await Ra(m,_))}catch(R){p(se,{port:m,reason:Ne(R)},!0);return}await S()}}finally{p(be,!1)}}async function ce(){const m=e(q),R=await s().protect(()=>Ra(m,_));R!==null&&(Ve(m,R),p(se,null))}function Ve(m,R){if(V||R.length===0)return;const Te=m===e(_e)?e(fe):[];p(_e,m,!0),p(fe,[...Te,...R].slice(-c),!0)}function S(m=w){return new Promise(R=>setTimeout(R,m))}async function h(){const m=Date.now()+A;for(;e(be)&&Date.now()Qa(m));R!==null&&(s().notice=R.message,await s().refresh())}async function we(){p(ge,0),p(Q,0),e(ae)||await O();const m=e(H);if(m.length===0){p(pe,[],!0);return}await s().protect(()=>d(m))}async function d(m){p(pe,[],!0),p(Q,m.length,!0);for(const[R,Te]of m.entries()){p(ge,R+1);try{const He=await Hr(Te.name);p(pe,[...e(pe),{port:Te.name,message:He.message,refused:!1}],!0)}catch(He){if(ve(He))throw He;p(pe,[...e(pe),{port:Te.name,message:Ne(He),refused:!0}],!0)}}}function x(){return e(C)!=="detect"?"Détecter automatiquement":e(be)?"Détection : le port se libère…":e(Q)===0?"Détection : énumération des ports…":`Détection : port ${he(e(ge))} sur ${he(e(Q))}…`}function $(){return t.health.scale_present?e(B).connected?e(B).too_slow?{level:"warn",word:"Trop lente",detail:f()+" À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante."}:{level:"ok",word:"Connectée",detail:"Elle répond. "+f()}:{level:"fault",word:"Sans réponse",detail:"Elle ne répond plus. Vérifiez le câble et l’alimentation, puis « Tester la balance » sur la page Dépannage."}:{level:"off",word:"Sans balance",detail:"Ce poste est déclaré sans balance : le feu est éteint et le poids se saisit à la main."}}function f(){return e(B).observations_count===0?"Aucun intervalle n’a encore été mesuré : la cadence sera connue dès les premières trames.":`Une mesure toutes les ${va(e(B).median_ms)} sur ${he(e(B).observations_count)} intervalles`+(e(B).provisional?", cadence encore provisoire.":".")}function E(){const m=z[e(re).health]??{level:"unknown",word:"État inconnu",detail:"Le poste a répondu un état que cet écran ne sait pas nommer."};return{...m,detail:e(re).detail===""?m.detail:e(re).detail}}const z={ready:{level:"ok",word:"Prête",detail:"Elle répond et n’a rien à signaler."},consumable:{level:"warn",word:"Rouleau en fin de vie",detail:"Elle imprime, mais le rouleau arrive en fin de vie."},faulted:{level:"fault",word:"En panne",detail:"Elle ne peut pas imprimer."},unknown:{level:"unknown",word:"Silencieuse",detail:"Elle prend les étiquettes et ne dit rien en retour : c’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne."}};function ye(){const m=e(re).observed_at===""?"Jamais observée depuis le démarrage":`Observée le ${Mt(e(re).observed_at)}`,R=e(re).pending_jobs_count;return`${m}, ${he(R)} ${R>1?"travaux":"travail"} en attente.`}function ie(){const m=Oe();return m===""?ke():`${ke()} ${m}`}function ke(){return e(N)?e(L)?"Ce poste est déclaré sans balance : aucun port n’est écouté.":e(q)===""?"Aucun port n’est indiqué : choisissez-en un dans la liste ci-dessus pour écouter les trames.":e(ae)?W(e(q))?e(D)!==""?`L’écoute de ${e(q)} est arrêtée.`:e(C)!==""?`L’écoute de ${e(q)} est suspendue le temps de l’acte en cours.`:`Écoute de ${e(q)}.`:`${e(q)} n’est pas visible depuis ce poste : rien n’est écouté en continu.`:e(C)==="ports"?`Énumération des ports en cours : l’écoute de ${e(q)} démarre dès qu’il est vu.`:`Les ports de ce poste n’ont pas été énumérés : « Lister les ports » dira si ${e(q)} existe.`:"Lecture de la configuration en cours : le port à écouter n’est pas encore connu."}function Oe(){return e(X).length===0?"Aucune trame reçue pour l’instant.":e(X).length===1?`Une seule trame reçue — ${he(c)} au plus sont gardées.`:`Les ${he(e(X).length)} dernières trames — ${he(c)} au plus, la plus récente en bas.`}function de(){return!e(N)||e(L)||e(q)===""?"":e(D)!==""?"Reprendre l’écoute":e(ae)&&!W(e(q))?"Écouter ce port une fois":""}function K(m,R,Te){const He=`${he(Te)} ${Te>1?R:m}`;return Te<=M?He+".":`${He} — seules les ${he(M)} premières lignes sont affichées.`}function Ae(m,R){const Te=te(m);return Te.length>0?Te:R.slice(0,M)}function Ge(m){return[...new TextEncoder().encode(m)].map(R=>R.toString(16).toUpperCase().padStart(2,"0")).join(" ")}const Fe={0:"NUL",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",9:"TAB",10:"LF",13:"CR",21:"NAK",27:"ESC",127:"DEL"};function De(m){let R="";for(const Te of m){const He=Te.codePointAt(0)??0;if(He>=32&&He!==127){R+=Te;continue}R+=`⟨${Fe[He]??He.toString(16).toUpperCase().padStart(2,"0")}⟩`}return R}function Me(m){return t.draft.faults.find(R=>R.field===m)?.message??""}function te(m){return t.draft.faults.find(R=>R.field===m)?.allowed??[]}function Ne(m){return m instanceof Ft?m.message:m instanceof Error?"Le poste n’a pas répondu : "+m.message:"Le poste n’a pas répondu."}function ve(m){return m instanceof Ft&&(m.status===401||m.status===409)}const je=[{what:"label",name:"étiquette"},{what:"alignment",name:"alignement"},{what:"ruler",name:"réglette"}];var $e=Al(),st=a($e),it=a(st),pt=r(a(it),2),Ee=r(a(pt),1,!0),nt=r(it,2),et=a(nt),mt=r(nt,4);{var Dt=m=>{var R=Sl(),Te=a(R);v(()=>la(Te,e(L))),Ze("change",Te,He=>t.draft.set("scale.present",!He.currentTarget.checked)),l(m,R)},Bt=m=>{var R=Ll();l(m,R)};g(mt,m=>{e(N)?m(Dt):m(Bt,-1)})}var Vt=r(mt,2),Et=a(Vt);{let m=u(()=>e(C)==="ports"),R=u(()=>e(C)!=="");tt(Et,{label:"Lister les ports",get busy(){return e(m)},get disabled(){return e(R)},onrun:()=>{k("ports",O)}})}var bt=r(Et,2);{let m=u(x),R=u(()=>e(C)!=="");tt(bt,{act:"detect",get label(){return e(m)},protected:!0,get disabled(){return e(R)},onrun:()=>{k("detect",we)}})}var Tt=r(Vt,2);{var b=m=>{var R=zl(),Te=Y(R),He=a(Te),lt=r(Te,2);{var ft=wt=>{var Pt=El();at(Pt,21,()=>e(H).slice(0,M),Ot=>Ot.name,(Ot,St)=>{var Kt=Fa(),Qt=a(Kt),_a=a(Qt),or=r(Qt,2),ur=a(or);v(()=>{i(_a,e(St).name),i(ur,`${(e(St).description===""?"aucune description USB":e(St).description)??""}
- ${e(St).vid===""?"":` — VID ${e(St).vid} PID ${e(St).pid}`}`)}),Ze("click",Qt,()=>t.draft.set("scale.options.port",e(St).name)),l(Ot,Kt)}),l(wt,Pt)};g(lt,wt=>{e(H).length>0&&wt(ft)})}v(wt=>i(He,wt),[()=>e(H).length===0?"Aucun port série n’est visible depuis ce poste.":K("port détecté","ports détectés",e(H).length)]),l(m,R)};g(Tt,m=>{e(ae)&&m(b)})}var G=r(Tt,2);{var oe=m=>{var R=Pl(),Te=Y(R),He=a(Te),lt=r(Te,2);at(lt,21,()=>e(pe).slice(0,M),ft=>ft.port,(ft,wt)=>{var Pt=jl();let Ot;var St=a(Pt),Kt=a(St),Qt=r(St,2),_a=a(Qt);v(()=>{Ot=Zt(Pt,1,"svelte-1p4i0xm",null,Ot,{refused:e(wt).refused}),i(Kt,e(wt).port),i(_a,e(wt).message)}),l(ft,Pt)}),v(ft=>i(He,ft),[()=>K("port interrogé","ports interrogés",e(pe).length)]),l(m,R)};g(G,m=>{e(pe).length>0&&m(oe)})}var qe=r(G,2),Je=r(a(qe),2),Se=a(Je);{let m=u(()=>t.draft.text("scale.type")),R=u(()=>Me("scale.type")),Te=u(()=>te("scale.type")),He=u(()=>!e(N));_t(Se,{label:"Protocole",path:"scale.type",get value(){return e(m)},hint:"Les valeurs acceptées apparaissent ici si l’enregistrement est refusé.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("scale.type",lt)})}var Pe=r(Se,2);{let m=u(()=>t.draft.text("scale.options.port")),R=u(()=>Me("scale.options.port")),Te=u(()=>Ae("scale.options.port",e(H).map(lt=>lt.name))),He=u(()=>!e(N));_t(Pe,{label:"Port série",path:"scale.options.port",get value(){return e(m)},hint:"Choisissez-le dans la liste détectée ci-dessus plutôt que de le taper : l’écoute permanente ne suit que des ports détectés.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("scale.options.port",lt)})}var j=r(qe,2),U=a(j),xe=a(U),Xe=r(U,2);{var Ke=m=>{var R=Tl(),Te=a(R),He=r(Te);{var lt=ft=>{{let wt=u(()=>e(C)==="listen"),Pt=u(()=>e(C)!=="");tt(ft,{act:"listen",get label(){return e(Le)},protected:!0,get busy(){return e(wt)},get disabled(){return e(Pt)},onrun:()=>{k("listen",ce)}})}};g(He,ft=>{e(Le)!==""&&ft(lt)})}v(()=>i(Te,`${e(D)??""} `)),l(m,R)};g(Xe,m=>{(e(D)!==""||e(Le)!=="")&&m(Ke)})}var Ye=r(Xe,2);{var ue=m=>{var R=Ol();at(R,21,()=>e(X),ma,(Te,He)=>{var lt=Cl(),ft=a(lt),wt=a(ft),Pt=r(ft,2),Ot=a(Pt);v((St,Kt)=>{i(wt,St),i(Ot,Kt)},[()=>Ge(e(He)),()=>De(e(He))]),l(Te,lt)}),l(m,R)};g(Ye,m=>{e(X).length>0&&m(ue)})}var Ie=r(st,2),Be=a(Ie),ct=r(a(Be),2),dt=r(a(ct),1,!0),ot=r(Be,2),ut=a(ot),ht=r(ot,2),kt=a(ht),zt=r(ht,2),qt=a(zt);{let m=u(()=>e(C)==="printers"),R=u(()=>e(C)!=="");tt(qt,{label:"Lister les files",get busy(){return e(m)},get disabled(){return e(R)},onrun:()=>{k("printers",le)}})}var jt=r(qt,2);{let m=u(()=>e(C)==="discover"),R=u(()=>e(C)!=="");tt(jt,{label:"Rechercher l’imprimante",protected:!0,get busy(){return e(m)},get disabled(){return e(R)},onrun:()=>{k("discover",Re)}})}var Ct=r(jt,2);at(Ct,17,()=>je,m=>m.what,(m,R)=>{{let Te=u(()=>`Auto-test : ${e(R).name}${e(C)===e(R).what?" — en cours…":""}`),He=u(()=>e(C)!=="");tt(m,{get act(){return e(R).what},get label(){return e(Te)},protected:!0,get disabled(){return e(He)},onrun:()=>{k(e(R).what,()=>We(e(R).what))}})}});var It=r(zt,2);{var Wt=m=>{var R=Rl(),Te=Y(R),He=a(Te),lt=r(Te,2);at(lt,21,()=>e(J).slice(0,M),ft=>ft.name,(ft,wt)=>{var Pt=Fa(),Ot=a(Pt),St=a(Ot),Kt=r(Ot,2),Qt=a(Kt);v(()=>{i(St,e(wt).name),i(Qt,`${e(wt).detail??""}${e(wt).default?" — file par défaut du système":""}`)}),Ze("click",Ot,()=>t.draft.set("printer.options.queue",e(wt).name)),l(ft,Pt)}),v(ft=>i(He,ft),[()=>K("file d’impression","files d’impression",e(J).length)]),l(m,R)};g(It,m=>{e(J).length>0&&m(Wt)})}var Jt=r(It,2),ta=r(a(Jt),2),rt=a(ta);{let m=u(()=>t.draft.text("printer.type")),R=u(()=>Me("printer.type")),Te=u(()=>te("printer.type")),He=u(()=>!e(N));_t(rt,{label:"Driver",path:"printer.type",get value(){return e(m)},hint:"Gardez le driver raster : c’est celui que les postes en service utilisent.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("printer.type",lt)})}var yt=r(rt,2);{let m=u(()=>t.draft.text("printer.options.transport")),R=u(()=>Me("printer.options.transport")),Te=u(()=>te("printer.options.transport")),He=u(()=>!e(N));_t(yt,{label:"Transport",path:"printer.options.transport",get value(){return e(m)},hint:"Local par défaut : une file Windows ou un fichier de périphérique de ce poste.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("printer.options.transport",lt)})}var Yt=r(yt,2);{let m=u(()=>t.draft.text("printer.options.queue")),R=u(()=>Me("printer.options.queue")),Te=u(()=>Ae("printer.options.queue",e(J).map(lt=>lt.name))),He=u(()=>!e(N));_t(Yt,{label:"File ou chemin",path:"printer.options.queue",get value(){return e(m)},hint:"Choisissez-la dans la liste ci-dessus : une file mal orthographiée ne s’imprime pas.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("printer.options.queue",lt)})}v(m=>{Ue(pt,"data-level",e(me).level),i(Ee,e(me).word),i(et,e(me).detail),i(xe,e(Ce)),Ue(ct,"data-level",e(ze).level),i(dt,e(ze).word),i(ut,e(ze).detail),i(kt,m)},[()=>ye()]),Oa("open","toggle",qe,m=>p(P,m),()=>e(P)),Oa("open","toggle",Jt,m=>p(ne,m),()=>e(ne)),l(n,$e),$t()}At(["change","click"]);var $l=o(""),Dl=o('L’export sera proposé quand le journal aura répondu.'),Il=o('L’export n’est pas proposé : ce poste n’a pas répondu à la lecture du journal.'),Ul=o('Exporter en CSV'),Fl=o('
'),Ml=o(''),Bl=o(`
Aucune trame brute n’a été enregistrée pour cette pesée : il n’y a
- rien à rejouer.
`),Vl=o(`
La trame repart dans le décodeur du poste EN SERVICE : le poids
- affiché au client change, et rien ne le remet comme il était. C’est
- ce qui fait d’un refus inexpliqué un test permanent, sans
- déplacement au magasin et sans balance.
`,1),Wl=o('
Produit
Référence
Vente
Brut / tare / net
Stabilité
Origine du poids
Résultat
Trame brute
'),Gl=o('
',1),Hl=o('
Quand
Produit
Net
Code-barres
Résultat
Durée
Détail
',1),Jl=o('
',1),Kl=o(''),Xl=o(''),Yl=o(''),Ql=o('
'),Zl=o('
',1),ei=o('
');function ti(n,t){Nt(t,!0);const s=vt(t,"admin",7),c=200,_=5e3,w=50,T=500,A=7,M={sent:"envoyée à l’imprimante",rejected:"refusée",failed:"en échec",reprint:"réimpression"},H=[{value:"",label:"toutes"},{value:"sent",label:"envoyées à l’imprimante"},{value:"rejected",label:"refusées"},{value:"failed",label:"en échec"},{value:"reprint",label:"réimpressions"}],J={scale:"balance",manual:"saisie manuelle",replay:"trame rejouée"},pe={stable:"stable",unstable:"instable",unknown:"non déclarée par la balance",not_applicable:"sans objet — saisie manuelle"},fe={by_weight:"au poids",by_unit:"à l’unité"},_e={debug:"mise au point",info:"information",warn:"avertissement",error:"erreur",critical:"critique"};let ae=F(Lt([])),C=F(Lt([])),ge=F(""),Q=F(null),be=F(!1),se=F(!1),P=F("loading"),ne=F("loading"),V=F(""),B=F("");const re=u(()=>({limit:String(c),...e(ge)===""?{}:{result:e(ge)}})),N=u(()=>({...e(re),limit:String(_)})),L=u(()=>e(ae).find(W=>W.id===e(Q))??null),q=u(()=>H.find(W=>W.value===e(ge))?.label??""),D=u(()=>Ce(e(ae).length,c,"pesée","pesées",`L’export CSV en emporte jusqu’à ${he(_)}.`)),X=u(()=>Ce(e(C).length,w,"ligne","lignes",`Le fichier de diagnostic emporte les ${he(T)} dernières.`));me();async function me(){p(Q,null),p(se,!0),p(P,"loading"),p(ne,"loading"),p(ae,[],!0),p(C,[],!0),p(V,""),p(B,"");try{const W=await s().load(()=>Kr(e(re)));p(P,W===null?"unread":"read",!0),p(V,W===null?s().actionError:"",!0),p(ae,W??[],!0);const ee=await s().load(()=>Yr({limit:String(w)}));p(ne,ee===null?"unread":"read",!0),p(B,ee===null?s().actionError:"",!0),p(C,ee??[],!0)}finally{p(se,!1),s().actionError=""}}async function ze(W){s().notice="",s().actionError="",p(be,!0);try{const ee=await s().protect(()=>tn(W));ee!==null&&(s().notice=ee.message)}finally{p(be,!1)}}function Ce(W,ee,ce,Ve,S){const h=W>1?Ve:ce;return W{var ce=Jl(),Ve=Y(ce),S=r(a(Ve),2);at(S,21,()=>H,f=>f.value,(f,E)=>{var z=$l(),ye=a(z),ie={};v(()=>{i(ye,e(E).label),ie!==(ie=e(E).value)&&(z.value=(z.__value=e(E).value)??"")}),l(f,z)});var h=r(S,2);tt(h,{label:"Rafraîchir",get busy(){return e(se)},onrun:()=>{me()}});var k=r(h,2);{var O=f=>{var E=Dl();l(f,E)},le=f=>{var E=Il();l(f,E)},Re=f=>{var E=Ul();v(z=>Ue(E,"href",z),[()=>Xr(e(N))]),l(f,E)};g(k,f=>{e(P)==="loading"?f(O):e(P)==="unread"?f(le,1):f(Re,-1)})}var We=r(Ve,2);{var we=f=>{var E=Fl(),z=a(E);v(ye=>i(z,`L’export emporte le même filtre que le tableau, mais pas son plafond : il descend
- jusqu’à ${ye??""} pesées, en point-virgule et en UTF-8 — il s’ouvre
- tel quel dans le tableur d’un Windows français. Il ne demande aucun mot de passe : la
- lecture du journal n’en demande pas non plus, et le fichier de diagnostic emporte déjà
- les deux cents dernières pesées.`),[()=>he(_)]),l(f,E)};g(We,f=>{e(P)==="read"&&f(we)})}var d=r(We,2);{var x=f=>{var E=Ml(),z=a(E);{var ye=de=>{var K=xt("Lecture du journal…");l(de,K)},ie=de=>{var K=xt();v(()=>i(K,`Le journal n’a pas pu être lu : ${e(V)??""} Ce n’est pas « aucune pesée ».`)),l(de,K)},ke=de=>{var K=xt("Le journal ne contient aucune pesée.");l(de,K)},Oe=de=>{var K=xt();v(()=>i(K,`Aucune pesée ne correspond au filtre « ${e(q)??""} ».`)),l(de,K)};g(z,de=>{e(P)==="loading"?de(ye):e(P)==="unread"?de(ie,1):e(ge)===""?de(ke,2):de(Oe,-1)})}l(f,E)},$=f=>{var E=Hl(),z=Y(E),ye=a(z),ie=r(z,2),ke=a(ie),Oe=r(a(ke));at(Oe,21,()=>e(ae),de=>de.id,(de,K)=>{var Ae=Gl(),Ge=Y(Ae);let Fe;var De=a(Ge),Me=a(De),te=r(De),Ne=a(te),ve=r(te),je=a(ve),$e=r(ve),st=a($e),it=r($e),pt=a(it),Ee=r(it),nt=a(Ee),et=r(Ee),mt=a(et),Dt=a(mt),Bt=r(Ge,2);{var Vt=Et=>{var bt=Wl(),Tt=a(bt);Ue(Tt,"colspan",A);var b=a(Tt),G=a(b),oe=r(b,2),qe=r(a(oe),2),Je=a(qe),Se=r(qe,4),Pe=a(Se),j=r(Se,4),U=a(j),xe=r(j,4),Xe=a(xe),Ke=r(xe,4),Ye=a(Ke),ue=r(Ke,4),Ie=a(ue),Be=r(ue,4),ct=a(Be),dt=r(Be,4),ot=a(dt),ut=a(ot),ht=r(oe,2);{var kt=qt=>{var jt=Bl();l(qt,jt)},zt=qt=>{var jt=Vl(),Ct=Y(jt),It=a(Ct);tt(It,{kind:"destructive",label:"Rejouer cette trame",protected:!0,get busy(){return e(be)},onrun:()=>{ze(e(L).frame)}}),l(qt,jt)};g(ht,qt=>{e(L).frame===""?qt(kt):qt(zt,-1)})}v((qt,jt,Ct,It,Wt,Jt,ta,rt,yt)=>{Ue(bt,"data-detail",e(K).id),i(G,`Pesée ${e(L).id??""}`),i(Je,`${e(L).product_name??""} (${e(L).product_id??""})`),i(Pe,e(L).reference),i(U,`${qt??""} —
- ${jt??""}
- ${e(L).quantity>1?"unités":"unité"}`),i(Xe,`${Ct??""} g / ${It??""} g /
- ${Wt??""} g`),i(Ye,`${Jt??""} — cadence
- médiane ${ta??""}`),i(Ie,rt),i(ct,`${yt??""}${e(L).detail===""?"":` — ${e(L).detail}`}`),i(ut,e(L).frame===""?"aucune trame enregistrée":e(L).frame)},[()=>Le(fe,e(L).mode,"mode de vente inconnu"),()=>he(e(L).quantity),()=>he(e(L).gross_g),()=>he(e(L).tare_g),()=>he(e(L).net_g),()=>Le(pe,e(L).stability,"stabilité inconnue"),()=>va(e(L).rate_ms),()=>Le(J,e(L).source,"origine inconnue"),()=>Le(M,e(L).result,"résultat inconnu")]),l(Et,bt)};g(Bt,Et=>{e(Q)===e(K).id&&e(L)!==null&&Et(Vt)})}v((Et,bt,Tt,b)=>{Fe=Zt(Ge,1,"svelte-86o8oz",null,Fe,{open:e(Q)===e(K).id}),i(Me,Et),i(Ne,e(K).product_name),i(je,`${bt??""} g`),i(st,e(K).barcode),i(pt,Tt),i(nt,b),Ue(mt,"aria-expanded",e(Q)===e(K).id),i(Dt,e(Q)===e(K).id?"fermer":"détail")},[()=>Mt(e(K).occurred_at),()=>he(e(K).net_g),()=>Le(M,e(K).result,"résultat inconnu"),()=>va(e(K).duration_ms)]),Ze("click",mt,()=>p(Q,e(Q)===e(K).id?null:e(K).id,!0)),l(de,Ae)}),v(()=>i(ye,e(D))),l(f,E)};g(d,f=>{e(ae).length===0?f(x):f($,-1)})}Ze("change",S,()=>{me()}),Sr(S,()=>e(ge),f=>p(ge,f)),l(W,ce)},$$slots:{default:!0}});var I=r(y,2);Qe(I,{title:"Journal technique",children:(W,ee)=>{var ce=Ut(),Ve=Y(ce);{var S=k=>{var O=Kl(),le=a(O);{var Re=d=>{var x=xt("Lecture du journal technique…");l(d,x)},We=d=>{var x=xt();v(()=>i(x,`Le journal technique n’a pas pu être lu : ${e(B)??""} Ce n’est pas « aucune
- ligne ».`)),l(d,x)},we=d=>{var x=xt("Aucune ligne technique.");l(d,x)};g(le,d=>{e(ne)==="loading"?d(Re):e(ne)==="unread"?d(We,1):d(we,-1)})}l(k,O)},h=k=>{var O=Zl(),le=Y(O),Re=a(le),We=r(le,2),we=a(We);at(we,21,()=>e(C),d=>d.id,(d,x)=>{var $=Ql(),f=a($),E=a(f),z=r(f,2),ye=a(z),ie=r(z,2),ke=a(ie),Oe=r(ie,2);{var de=De=>{var Me=Xl(),te=a(Me);v(()=>i(te,e(x).code)),l(De,Me)};g(Oe,De=>{e(x).code!==""&&Ht.showTechnicalNames&&De(de)})}var K=r(Oe,2),Ae=a(K),Ge=r(K,2);{var Fe=De=>{var Me=Yl(),te=a(Me);v(()=>i(te,e(x).detail)),l(De,Me)};g(Ge,De=>{e(x).detail!==""&&De(Fe)})}v((De,Me,te)=>{Ue($,"data-level",e(x).level),i(E,De),i(ye,Me),i(ke,te),i(Ae,e(x).message)},[()=>Mt(e(x).occurred_at),()=>Le(_e,e(x).level,"niveau inconnu"),()=>tr(e(x).source)]),l(d,$)}),v(()=>i(Re,e(X))),l(k,O)};g(Ve,k=>{e(C).length===0?k(S):k(h,-1)})}l(W,ce)},$$slots:{default:!0}}),l(n,Z),$t()}At(["change","click"]);var ai=o('
'),ri=o('
'),ni=o('
'),si=o('
'),li=o('
Lecture de la configuration en cours… les flèches attendent qu’elle soit arrivée.
'),ii=o(`
Décalage horizontal
Décalage vertical
Le décalage ne descend pas sous zéro : le poste refuse un décalage négatif quel que
- soit le gabarit. Le maximum, lui, dépend de la géométrie du gabarit ; il est annoncé
- ici si l’enregistrement le dépasse.
Le symbole code-barres est volontairement tronqué : un symbole conforme n’entre pas
- sur 40 × 25 mm avec les cinq champs texte. Ce n’est pas un défaut de rendu et il n’y
- a rien à corriger.
Le détail chiffré du symbole — largeur de module, modules rendus, modules attendus —
- n’est pas servi par ce poste. Cet écran n’affiche pas un chiffre qu’il aurait deviné.
`,1),oi=o(`
Chaque appui sort une étiquette pour de bon : le mot de passe est demandé au moment
- d’imprimer, et l’impression repart d’elle-même une fois la session ouverte.
`,1),ui=o('
');function ci(n,t){Nt(t,!0);const s=vt(t,"admin",7),c="printer.template",_="printer.options.offset_x",w="printer.options.offset_y",T=0,A="L’aperçu n’a pas pu être rendu par le poste.",M="Le poste a rendu l’aperçu, mais le navigateur ne l’a pas affiché.",H=[{what:"alignment",label:"Imprimer la mire d’alignement"},{what:"ruler",label:"Imprimer la réglette"}];let J=F(1),pe=F(!0),fe=F(!1),_e=F(""),ae=F(null),C=F(""),ge=null,Q=!1;const be=u(()=>t.draft.text(c)),se=u(()=>Jr(e(be),e(pe),e(fe),e(J))),P=u(()=>t.draft.config!==null),ne=u(()=>t.draft.number(_)),V=u(()=>t.draft.number(w)),B=u(()=>I(_)),re=u(()=>I(w)),N=u(Le),L=u(()=>s().busy||e(C)!=="");Xt(()=>{if(t.draft.config===null)return;if(t.draft.dirty){ge===null&&q();return}const S={x:t.draft.number(_),y:t.draft.number(w)};if(ge!==null&&ge.x===S.x&&ge.y===S.y)return;const h=ge===null;ge=S,p(ae,S,!0),h||me()});async function q(){if(!Q){Q=!0;try{const S=await za(),h={x:D(S.config,_),y:D(S.config,w)};ge=h,p(ae,h,!0)}catch{}finally{Q=!1}}}function D(S,h){let k=S;for(const O of h.split(".")){if(k===null||typeof k!="object")return 0;k=k[O]}return typeof k=="number"?k:0}function X(S,h){const k=t.draft.number(S)+h;k=2?"s":""}`}function y(S,h){const k=Number(h);h.trim()===""||Number.isNaN(k)||t.draft.set(S,k)}function I(S){return t.draft.faults.find(h=>h.field===S)?.message??""}async function W(S){if(!e(L)){p(C,S,!0),s().actionError="",s().notice="";try{const h=await s().protect(()=>Qa(S));if(h===null)return;s().notice=h.message,await s().refresh()}finally{p(C,"")}}}var ee=ui(),ce=a(ee);Qe(ce,{title:"Aperçu de l’étiquette",note:"Le même moteur que l’impression (A2) : le décalage se voit parce qu’il est cuit dans le bitmap. L’image porte le gabarit en cours d’édition, mais le décalage ENREGISTRÉ — jamais celui que les flèches sont en train de régler.",children:(S,h)=>{var k=ii(),O=Y(k);{var le=Ee=>{var nt=ai(),et=a(nt);v(()=>i(et,e(N))),l(Ee,nt)};g(O,Ee=>{e(N)!==""&&Ee(le)})}var Re=r(O,2),We=a(Re),we=a(We),d=r(we,2);{var x=Ee=>{var nt=ri(),et=a(nt);v(()=>i(et,e(_e))),l(Ee,nt)};g(d,Ee=>{e(_e)!==""&&Ee(x)})}var $=r(We,2),f=a($),E=r(a(f),2),z=a(E),ye=r(f,2),ie=a(ye);{let Ee=u(()=>!e(P)||e(ne)<=T);tt(ie,{label:"← 1 dot",get disabled(){return e(Ee)},onrun:()=>X(_,-1)})}var ke=r(ie,2);{let Ee=u(()=>!e(P));tt(ke,{label:"1 dot →",get disabled(){return e(Ee)},onrun:()=>X(_,1)})}var Oe=r(ye,2);{var de=Ee=>{var nt=ni(),et=a(nt);v(()=>i(et,e(B))),l(Ee,nt)};g(Oe,Ee=>{e(B)!==""&&Ee(de)})}var K=r(Oe,2),Ae=r(a(K),2),Ge=a(Ae),Fe=r(K,2),De=a(Fe);{let Ee=u(()=>!e(P)||e(V)<=T);tt(De,{label:"↑ 1 dot",get disabled(){return e(Ee)},onrun:()=>X(w,-1)})}var Me=r(De,2);{let Ee=u(()=>!e(P));tt(Me,{label:"1 dot ↓",get disabled(){return e(Ee)},onrun:()=>X(w,1)})}var te=r(Fe,2);{var Ne=Ee=>{var nt=si(),et=a(nt);v(()=>i(et,e(re))),l(Ee,nt)};g(te,Ee=>{e(re)!==""&&Ee(Ne)})}var ve=r(te,4),je=a(ve),$e=r(ve,2),st=a($e),it=r($e,2);{var pt=Ee=>{var nt=li();l(Ee,nt)};g(it,Ee=>{e(P)||Ee(pt)})}v((Ee,nt)=>{Ue(we,"src",e(se)),i(z,Ee),i(Ge,nt)},[()=>Z(e(ne)),()=>Z(e(V))]),na("load",we,()=>p(_e,"")),na("error",we,()=>{ze(e(se))}),Ze("change",je,()=>me()),Ca(je,()=>e(pe),Ee=>p(pe,Ee)),Ze("change",st,()=>me()),Ca(st,()=>e(fe),Ee=>p(fe,Ee)),l(S,k)},$$slots:{default:!0}});var Ve=r(ce,2);Qe(Ve,{title:"Gabarit et impression",children:(S,h)=>{var k=oi(),O=Y(k);{let d=u(()=>I(c)),x=u(()=>!e(P));_t(O,{label:"Gabarit",path:c,get value(){return e(be)},hint:"Le gabarit reproduit à l’identique s’appelle weighing_identical (A1).",get fault(){return e(d)},get disabled(){return e(x)},onchange:$=>{t.draft.set(c,$),me()}})}var le=r(O,2);{let d=u(()=>t.draft.text("printer.options.darkness")),x=u(()=>I("printer.options.darkness")),$=u(()=>!e(P));_t(le,{label:"Noircissement",path:"printer.options.darkness",kind:"number",get value(){return e(d)},hint:"Trop bas, l’étiquette pâlit au soleil ; trop haut, elle bave et le scanner refuse.",get fault(){return e(x)},get disabled(){return e($)},onchange:f=>y("printer.options.darkness",f)})}var Re=r(le,2);{let d=u(()=>t.draft.text("printer.options.speed")),x=u(()=>I("printer.options.speed")),$=u(()=>!e(P));_t(Re,{label:"Vitesse",path:"printer.options.speed",kind:"number",get value(){return e(d)},get fault(){return e(x)},get disabled(){return e($)},onchange:f=>y("printer.options.speed",f)})}var We=r(Re,2);{let d=u(()=>t.draft.text("printer.options.copies")),x=u(()=>I("printer.options.copies")),$=u(()=>!e(P));_t(We,{label:"Exemplaires",path:"printer.options.copies",kind:"number",get value(){return e(d)},hint:"Un client repart avec une étiquette : deux exemplaires se justifient, pas se devinent.",get fault(){return e(x)},get disabled(){return e($)},onchange:f=>y("printer.options.copies",f)})}var we=r(We,2);at(we,21,()=>H,d=>d.what,(d,x)=>{{let $=u(()=>e(C)===e(x).what);tt(d,{get act(){return e(x).what},get label(){return e(x).label},protected:!0,get busy(){return e($)},get disabled(){return e(L)},onrun:()=>{W(e(x).what)}})}}),l(S,k)},$$slots:{default:!0}}),l(n,ee),$t()}At(["change"]);var di=o(""),vi=o(''),pi=o(''),fi=o('
Aucun tarif déclaré dans la configuration lue.
'),gi=o('Prix du catalogue Odoo — pas de remise'),Ma=o(''),mi=o(' % ',1),hi=o('
'),_i=o(`
Code
Libellé
Abrégé
Remise
Ordre
Un champ vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la
- retrouve dès qu’on quitte le champ. Une remise effacée serait le plein tarif pour
- tous les adhérents.
`,1),bi=o(" ",1),wi=o(`
Les deux arrondis sont distincts parce qu’ils tombent à des endroits différents du
- calcul : arrondir le prix au kilo puis le multiplier, ou multiplier puis arrondir, ne
- donne pas le même centime sur une étiquette. Le poste applique l’arrondi commercial,
- et l’écart se voit au centime près sur la même pesée.
`,1),yi=o('
Rien ne s’affiche au client : c’est une information.
'),xi=o('
'),ki=o(''),qi=o('
'),Si=o('
'),Li=o(`
Aucun verdict en ce moment : ces lignes portent sur la pesée EN COURS, et le poste
- est au repos. Elles apparaissent le temps d’un cycle, quand un client pose son sac
- et touche une tuile.
`),Ei=o(''),zi=o('',1),ji=o('
'),Pi=o('
',1),Ti=o(`
L’ordre compte : le premier verdict bloquant décide de ce que le client lit. Les
- garde-fous 1 à 7 portent sur l’état de la balance, c’est-à-dire le poids brut ; les
- garde-fous 8 à 14 portent sur la vente, c’est-à-dire le net. Le code en gris est
- celui du journal, celui qu’on lit au téléphone.
Un seuil vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la retrouve
- dès qu’on quitte le champ. Pour changer un seuil, on tape l’autre valeur.
Ce que les garde-fous disent de la pesée en cours
`,1),Ci=o(`
Le plan de numérotation n’est PAS ici, et c’est tout l’intérêt : préfixes, largeur de
- la référence, largeur de la charge utile, décimales et mode de vente sont une
- constante du binaire, indexée par préfixe et vérifiée au démarrage.
- Un champ qui change le SENS du code lu par la caisse n’est pas un réglage, c’est un
- contrat externe : il change avec une version du binaire, relue et testée, jamais
- depuis l’écran d’un poste.
`,1),Oi=o('
Aucune dérogation : la limite générale s’applique à tous les produits.
'),Ri=o(`
Les noms de produits n’ont pas pu être lus : le catalogue en service n’a pas
- répondu. Les identifiants Odoo restent affichés.
`),Ai=o(`Produit retiré : le garde-fou 14 refuse le produit avant que cette
- dérogation ait un sens.`),Ni=o('
'),$i=o('
',1),Di=o('
');function Ii(n,t){Nt(t,!0);const s=(y,I=ia,W=ia,ee=ia)=>{var ce=pi(),Ve=a(ce),S=r(Ve,2),h=a(S),k=a(h),O=r(h,2);{var le=we=>{var d=di(),x=a(d);v(()=>i(x,I())),l(we,d)};g(O,we=>{Ht.showTechnicalNames&&we(le)})}var Re=r(O,2);{var We=we=>{var d=vi(),x=a(d);v(()=>i(x,ee())),l(we,d)};g(Re,we=>{ee()!==""&&we(We)})}v((we,d)=>{Ue(ce,"data-flag",I()),Ue(ce,"data-on",we),la(Ve,d),i(k,W())},[()=>String(t.draft.flag(I())),()=>t.draft.flag(I())]),Ze("change",Ve,we=>t.draft.set(I(),we.currentTarget.checked)),l(y,ce)},c=20,_=u(()=>pe(t.draft)),w=u(()=>t.health.state.diagnostics),T=u(()=>t.health.decisions.filter(y=>y.min_weight_g!==null)),A=u(()=>e(T).filter(y=>!y.offered)),M=u(()=>e(T).slice(0,c));let H=F(Lt({})),J=F("loading");se();function pe(y){const I=y.value("pricing.tiers");return Array.isArray(I)?I.map(W=>{const ee=W??{},ce=ee.discount_percent;return{code:String(ee.code??""),label:String(ee.label??""),abbrev:String(ee.abbrev??""),written:ce===void 0?null:String(ce),discount:fe(ce)?ce:null,rank:Number(ee.rank??0)}}):[]}function fe(y){return typeof y!="number"||!Number.isFinite(y)||y<0||y>100?!1:Math.abs(y*10-Math.round(y*10))<1e-9}const _e=u(()=>String(t.draft.value("pricing.reference_code")??""));function ae(y){return y===null?"":String(y).replace(".",",")}function C(y){const I=1e3-Math.round(y*10);return`${String(Math.trunc(I/100))},${String(I%100).padStart(2,"0")}`}function ge(y,I){const W=Number(I);I.trim()===""||Number.isNaN(W)||t.draft.set(y,W)}function Q(y,I){const W=I.trim().replace(",",".");if(!/^\d{1,3}(\.\d)?$/u.test(W))return;const ee=Number(W);ee>100||t.draft.set(y,ee)}function be(y,I){!(y instanceof HTMLInputElement)||y.value===I||(y.value=I)}async function se(){try{const y=await Ja();p(H,Object.fromEntries(y.products.map(I=>[I.id,I.name])),!0),p(J,"read")}catch{p(J,"unread")}}function P(y){const I=e(H)[y];return I!==void 0?I:e(J)==="loading"?"Lecture du nom…":e(J)==="read"?"Produit absent du catalogue en service":"Nom non lu"}function ne(y){return y<=1?`${String(y)} tarif déclaré`:`${String(y)} tarifs déclarés`}const V=u(()=>`${he(e(w).length)} ${e(w).length>1?"verdicts":"verdict"} sur les quatorze garde-fous.`),B=u(()=>`${he(e(M).length)} ${e(M).length>1?"lignes affichées":"ligne affichée"} sur ${he(e(T).length)} `+(e(A).length===0?`${e(T).length>1?"dérogations en vigueur":"dérogation en vigueur"}.`:`${e(T).length>1?"dérogations enregistrées":"dérogation enregistrée"}, dont ${he(e(A).length)} sur un produit retiré, sans effet : le garde-fou 14 refuse le produit avant que le 8 ait un sens.`)+(e(T).length>e(M).length?" Les autres se lisent produit par produit depuis l’onglet Catalogue.":""));function re(y){return`plancher ${he(y)} g : refusé à ${he(y)} g et en dessous`}const N=[{rank:1,code:"OVERLOAD",label:"Surcharge",when:"La balance annonce elle-même OL, ou le poids brut dépasse la capacité.",severity:"Bloquant",blocking:!0,message:"La balance est en surcharge. Retirez votre article.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la capacité, réglée au garde-fou 9 sous « ${Gt("limits.max_weight_g")} ».`},{rank:2,code:"MEASUREMENT_EXPIRED",label:"Poids périmé",when:"La mesure est plus vieille que la péremption, dans les deux modes de stabilité.",severity:"Bloquant",blocking:!0,message:"Poids indisponible. Patientez ou appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil à régler : le poste calcule lui-même à partir du rythme de la balance."},{rank:3,code:"BASKET_MISSING",label:"Panier absent",when:"Le poids brut tombe dans la fenêtre négative du panier : il a été soulevé.",severity:"Bloquant",blocking:!0,message:"Le panier n'est pas sur la balance. Reposez-le.",thresholds:[{path:"limits.basket_min_g",label:"Bas de la fenêtre du panier",hint:"En grammes, NÉGATIF : c’est le poids du panier que la balance a perdu."},{path:"limits.basket_max_g",label:"Haut de la fenêtre du panier",hint:"Négatif lui aussi, et plus proche de zéro que le bas."}],switchPath:"limits.basket_check_enabled",switchLabel:"Ce poste travaille avec un panier taré",note:"La règle s’active ou non, en bloc : il n’y a pas de demi-mesure à régler."},{rank:4,code:"SCALE_EMPTY",label:"Plateau vide",when:"Le poids brut ne sort pas de la bande « il n’y a rien sur le plateau ».",severity:"Bloquant — un filet, hors parcours nominal",blocking:!0,message:"Posez votre produit.",thresholds:[{path:"limits.empty_max_g",label:"Plateau considéré vide",hint:"En dessous, le poste considère qu’il n’y a rien sur le plateau."}],switchPath:"",switchLabel:"",note:"Toucher une tuile sur un plateau vide ARME la sélection au lieu d’être refusé : la règle reste évaluée pour la saisie manuelle et les chemins dérivés."},{rank:5,code:"TARE_REQUIRED",label:"Remise à zéro nécessaire",when:"Le brut est sous la bande du plateau vide, et hors de la fenêtre du panier.",severity:"Bloquant",blocking:!0,message:"La balance doit être remise à zéro.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la valeur négative de celui du garde-fou 4, « ${Gt("limits.empty_max_g")} ».`},{rank:6,code:"WEIGHT_UNSTABLE",label:"Pesée instable",when:"La trame déclare la mesure instable.",severity:"Information par défaut (A3)",blocking:!1,message:"Pesée en cours…",thresholds:[],switchPath:"",switchLabel:"",note:"La sévérité suit l’exigence de stabilité : elle passe à Bloquant quand celle-ci est réglée sur « blocking ». L’impression n’est jamais bloquée par défaut."},{rank:7,code:"TARE_INVALID",label:"Emballage incohérent",when:"Une tare a été saisie, et elle atteint la pesée ou dépasse le maximum.",severity:"Bloquant",blocking:!0,message:"Le poids de l'emballage est supérieur ou égal à la pesée.",thresholds:[{path:"limits.max_tare_g",label:"Tare maximum",hint:"Une tare plus lourde que le maximum est une faute de frappe."}],switchPath:"",switchLabel:"",note:""},{rank:8,code:"WEIGHT_TOO_LOW",label:"Poids trop faible",when:"Vente au poids : le NET est strictement positif et ne dépasse pas le plancher.",severity:"Bloquant",blocking:!0,message:"La balance doit être retarée, ou l'emballage est trop lourd.",thresholds:[{path:"limits.min_weight_g",label:"Poids minimum",hint:"Une dérogation par produit existe, dans l’onglet Catalogue."}],switchPath:"",switchLabel:"",note:""},{rank:9,code:"WEIGHT_TOO_HIGH",label:"Poids trop élevé",when:"Le NET dépasse la capacité — strictement, pour que la capacité reste atteignable.",severity:"Bloquant",blocking:!0,message:"{{.Weight}} kg, ça paraît un peu lourd !",thresholds:[{path:"limits.max_weight_g",label:"Poids maximum",hint:"C’est la capacité du champ NNDDD du code-barres, pas un seuil de vraisemblance."}],switchPath:"",switchLabel:"",note:""},{rank:10,code:"UNITS_OUT_OF_RANGE",label:"Nombre d’unités hors plage",when:"Vente à l’unité : la quantité sort de la plage.",severity:"Bloquant",blocking:!0,message:"{{.Quantity}} unités, ça paraît un peu beaucoup !",thresholds:[{path:"limits.min_units",label:"Unités minimum",hint:""},{path:"limits.max_units",label:"Unités maximum",hint:""}],switchPath:"",switchLabel:"",note:""},{rank:11,code:"AMOUNT_OUT_OF_CAPACITY",label:"Montant hors capacité du code-barres",when:"La charge utile encode un PRIX, et il dépasse ce que le champ peut porter.",severity:"Bloquant",blocking:!0,message:"Prix trop élevé pour le code-barres.",thresholds:[{path:"limits.max_amount_cents",label:"Montant maximum",hint:"En centimes. Aucun préfixe du plan livré n’encode un prix : la règle est éprouvée sans qu’aucun produit puisse l’atteindre."}],switchPath:"",switchLabel:"",note:""},{rank:12,code:"ZERO_PRICE",label:"Prix nul",when:"Le montant du tarif imprimé en grand vaut zéro.",severity:"Bloquant",blocking:!0,message:"Prix nul. Appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : un produit à 0 € est une anomalie sans nuance."},{rank:13,code:"LIGHT_PRODUCT_ALLOWED",label:"Produit léger autorisé",when:"Le garde-fou 8 n’a pas déclenché grâce à la dérogation du produit.",severity:"Information",blocking:!1,message:"",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil général : c’est la dérogation par produit, listée plus bas et posée depuis l’onglet Catalogue. Rien ne s’affiche au client ; l’id du produit est journalisé."},{rank:14,code:"PRODUCT_WITHDRAWN",label:"Produit retiré",when:"Quelqu’un a décidé de ne plus proposer ce produit.",severity:"Bloquant",blocking:!0,message:"Ce produit n'est pas disponible.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : c’est une décision humaine, prise depuis l’onglet Catalogue. Aucune règle d’import ne peut la déduire."}];function L(y){return N.find(I=>I.code===y)?.label??"Garde-fou inconnu de cet écran"}const q=["{{.Weight}}","{{.Quantity}}"];function D(y){return y==="blocking"?"Bloquant":y==="info"?"Information":"Sévérité inconnue de cet écran"}var X=Di(),me=a(X);Qe(me,{title:"Grille de tarifs",note:"Un second tarif n’est pas une case à cocher : c’est une ligne de plus dans cette grille.",children:(y,I)=>{var W=bi(),ee=Y(W);{var ce=k=>{var O=fi();l(k,O)},Ve=k=>{var O=_i(),le=Y(O),Re=a(le),We=r(le,2),we=a(We),d=r(a(we));at(d,21,()=>e(_),ma,(x,$,f)=>{var E=hi(),z=a(E),ye=a(z),ie=r(z),ke=a(ie);Ue(ke,"aria-label",`Libellé du tarif ${f+1}`);var Oe=r(ie),de=a(Oe);Ue(de,"aria-label",`Abrégé du tarif ${f+1}`);var K=r(Oe),Ae=a(K);{var Ge=ve=>{var je=gi();l(ve,je)},Fe=ve=>{var je=Ma(),$e=a(je);v(()=>i($e,`${e($).written??""} — le tarif de référence est le prix du catalogue : il
- ne peut pas porter de remise, et l’enregistrement la refusera.`)),l(ve,je)},De=ve=>{var je=Ma(),$e=a(je);v(()=>i($e,`${e($).written??""} — une remise s’écrit au dixième de point ; celle-ci se
- change dans le fichier de configuration.`)),l(ve,je)},Me=ve=>{var je=mi(),$e=Y(je);Ue($e,"aria-label",`Remise du tarif ${f+1}`);var st=r($e,2),it=a(st);v((pt,Ee)=>{oa($e,pt),i(it,`un produit à 10,00 €/kg s’affiche ${Ee??""} €/kg`)},[()=>ae(e($).discount??0),()=>C(e($).discount??0)]),Ze("input",$e,pt=>Q(`pricing.tiers.${String(f)}.discount_percent`,pt.currentTarget.value)),Ze("focusout",$e,pt=>be(pt.currentTarget,ae(e($).discount??0))),l(ve,je)};g(Ae,ve=>{e($).code===e(_e)&&e($).written===null?ve(Ge):e($).code===e(_e)?ve(Fe,1):e($).written!==null&&e($).discount===null?ve(De,2):ve(Me,-1)})}var te=r(K),Ne=a(te);v(()=>{i(ye,e($).code),oa(ke,e($).label),oa(de,e($).abbrev),i(Ne,e($).rank)}),Ze("input",ke,ve=>t.draft.set(`pricing.tiers.${String(f)}.label`,ve.currentTarget.value)),Ze("input",de,ve=>t.draft.set(`pricing.tiers.${String(f)}.abbrev`,ve.currentTarget.value)),l(x,E)}),v(x=>i(Re,`${x??""}.`),[()=>ne(e(_).length)]),l(k,O)};g(ee,k=>{e(_).length===0?k(ce):k(Ve,-1)})}var S=r(ee,2);{let k=u(()=>t.draft.text("pricing.primary_code"));_t(S,{label:"Tarif imprimé en grand",path:"pricing.primary_code",get value(){return e(k)},hint:"Le prix que le client lit sur l’étiquette (A7).",onchange:O=>t.draft.set("pricing.primary_code",O)})}var h=r(S,2);{let k=u(()=>t.draft.text("pricing.reference_code"));_t(h,{label:"Tarif qui serait encodé si le code-barres portait un prix",path:"pricing.reference_code",get value(){return e(k)},hint:"Le plan livré ne porte pas de prix, mais un poids ou un nombre d’unités : la caisse retrouve le prix par la référence, dans Odoo, et ne doit jamais sous-facturer.",onchange:O=>t.draft.set("pricing.reference_code",O)})}l(y,W)},$$slots:{default:!0}});var ze=r(me,2);Qe(ze,{title:"Les deux arrondis",children:(y,I)=>{var W=wi(),ee=Y(W);{let Ve=u(()=>t.draft.text("pricing.unit_price_rounding"));_t(ee,{label:"Arrondi du prix unitaire dérivé",path:"pricing.unit_price_rounding",get value(){return e(Ve)},hint:"Il s’applique au prix au kilo calculé par la remise.",onchange:S=>t.draft.set("pricing.unit_price_rounding",S)})}var ce=r(ee,2);{let Ve=u(()=>t.draft.text("pricing.amount_rounding"));_t(ce,{label:"Arrondi du montant",path:"pricing.amount_rounding",get value(){return e(Ve)},hint:"Il s’applique au montant de l’étiquette.",onchange:S=>t.draft.set("pricing.amount_rounding",S)})}l(y,W)},$$slots:{default:!0}});var Ce=r(ze,2);Qe(Ce,{title:"Les quatorze garde-fous, dans l’ordre d’évaluation",note:"Le seuil se modifie ici. La sévérité ne se règle pas : elle dit si le poste refuse ou avertit, et cela ne dépend pas du magasin. Le message affiché au client n’est pas encore modifiable depuis cet écran.",children:(y,I)=>{var W=Ti(),ee=r(Y(W),2);at(ee,21,()=>N,O=>O.code,(O,le)=>{var Re=Si(),We=a(Re),we=a(We),d=a(we),x=r(we,2),$=a(x),f=r(x,2),E=a(f),z=r(f,2),ye=a(z),ie=r(We,2),ke=a(ie),Oe=r(ie,2);{var de=te=>{var Ne=yi();l(te,Ne)},K=te=>{var Ne=xi(),ve=a(Ne);v(()=>i(ve,`« ${e(le).message??""} »`)),l(te,Ne)};g(Oe,te=>{e(le).message===""?te(de):te(K,-1)})}var Ae=r(Oe,2);{var Ge=te=>{s(te,()=>e(le).switchPath,()=>e(le).switchLabel,()=>"")};g(Ae,te=>{e(le).switchPath!==""&&te(Ge)})}var Fe=r(Ae,2);at(Fe,17,()=>e(le).thresholds,te=>te.path,(te,Ne)=>{var ve=ki(),je=a(ve);{let $e=u(()=>t.draft.text(e(Ne).path));_t(je,{get label(){return e(Ne).label},get path(){return e(Ne).path},kind:"number",get value(){return e($e)},get hint(){return e(Ne).hint},onchange:st=>ge(e(Ne).path,st)})}Ze("focusout",ve,$e=>be($e.target,t.draft.text(e(Ne).path))),l(te,ve)});var De=r(Fe,2);{var Me=te=>{var Ne=qi(),ve=a(Ne);v(()=>i(ve,e(le).note)),l(te,Ne)};g(De,te=>{e(le).note!==""&&te(Me)})}v(te=>{Ue(Re,"data-code",e(le).code),i(d,e(le).rank),i($,e(le).label),i(E,e(le).code),Ue(z,"data-blocking",te),i(ye,e(le).severity),i(ke,e(le).when)},[()=>String(e(le).blocking)]),l(O,Re)});var ce=r(ee,4),Ve=a(ce),S=r(ce,4);{var h=O=>{var le=Li();l(O,le)},k=O=>{var le=Pi(),Re=Y(le),We=a(Re),we=r(Re,2);at(we,21,()=>e(w),d=>d.code,(d,x)=>{var $=ji(),f=a($),E=a(f),z=r(f,2),ye=a(z),ie=r(z,2),ke=a(ie),Oe=r(ie,2);{var de=Ge=>{var Fe=Ei(),De=a(Fe);v(()=>i(De,`« ${e(x).message??""} »`)),l(Ge,Fe)};g(Oe,Ge=>{e(x).message!==""&&Ge(de)})}var K=r(Oe,2);{var Ae=Ge=>{var Fe=zi(),De=Y(Fe),Me=a(De),te=r(De,2),Ne=a(te);v(ve=>{i(Me,ve),i(Ne,e(x).product_id)},[()=>P(e(x).product_id)]),l(Ge,Fe)};g(K,Ge=>{e(x).product_id!==""&&Ge(Ae)})}v((Ge,Fe,De)=>{Ue($,"data-blocking",Ge),i(E,Fe),i(ye,e(x).code),i(ke,De)},[()=>String(e(x).blocking),()=>L(e(x).code),()=>D(e(x).severity)]),l(d,$)}),v(d=>{Ue(Re,"data-verdicts",d),i(We,e(V))},[()=>String(e(w).length)]),l(O,le)};g(S,O=>{e(w).length===0?O(h):O(k,-1)})}v(O=>i(Ve,`Les marqueurs ${O??""} sont remplacés par les valeurs de la pesée au
- moment où le message s’affiche.`),[()=>q.join(" et ")]),l(y,W)},$$slots:{default:!0}});var Le=r(Ce,2);Qe(Le,{title:"Code-barres",note:"Un seul réglage, et c’est voulu : le reste du plan de numérotation n’est pas de la configuration.",children:(y,I)=>{var W=Ci(),ee=Y(W);s(ee,()=>"barcode.verify_reference_check_digit",()=>"Refuser une référence dont la clé de contrôle est fausse",()=>"Décoché, le poste recalcule une clé juste sur une référence fausse, en silence — et la caisse encaisse un autre article."),l(y,W)},$$slots:{default:!0}});var Z=r(Le,2);Qe(Z,{title:"Dérogations de poids minimum",note:"En lecture ici ; elles se modifient depuis l’onglet Catalogue, là où se trouve le produit.",children:(y,I)=>{var W=Ut(),ee=Y(W);{var ce=S=>{var h=Oi();l(S,h)},Ve=S=>{var h=$i(),k=Y(h),O=a(k),le=r(k,2);{var Re=we=>{var d=Ri();l(we,d)};g(le,we=>{e(J)==="unread"&&we(Re)})}var We=r(le,2);at(We,21,()=>e(M),we=>we.product_id,(we,d)=>{var x=Ni(),$=a(x),f=a($),E=r($,2),z=a(E),ye=r(E,2),ie=a(ye),ke=r(ye,2),Oe=a(ke),de=r(ke,2),K=a(de),Ae=r(de,2);{var Ge=Fe=>{var De=Ai();l(Fe,De)};g(Ae,Fe=>{e(d).offered||Fe(Ge)})}v((Fe,De,Me,te)=>{Ue(x,"data-withdrawn",Fe),i(f,De),i(z,e(d).product_id),i(ie,Me),i(Oe,e(d).reason),i(K,`${te??""}, ${e(d).decided_by??""}`)},[()=>String(!e(d).offered),()=>P(e(d).product_id),()=>re(e(d).min_weight_g??0),()=>pa(e(d).decided_at)]),l(we,x)}),v(()=>i(O,e(B))),l(S,h)};g(ee,S=>{e(T).length===0?S(ce):S(Ve,-1)})}l(y,W)},$$slots:{default:!0}}),l(n,X),$t()}At(["change","input","focusout"]);function Ui(n,t){const s=[];for(const c of[...qa(n),...qa(t)]){if(s.some(T=>T.path===c))continue;const _=Ba(fa(n,c)),w=Ba(fa(t,c));_!==w&&s.push({path:c,before:_,after:w})}return s}function fa(n,t){let s=n;for(const c of t.split(".")){if(s===null||typeof s!="object")return;s=s[c]}return s}function qa(n,t=""){if(n===null||typeof n!="object"||Array.isArray(n))return t===""?[]:[t];const s=[];for(const[c,_]of Object.entries(n))s.push(...qa(_,t===""?c:`${t}.${c}`));return s}function Ba(n){return n===void 0?"—":n===null?"vide":typeof n=="boolean"?n?"oui":"non":typeof n=="object"?JSON.stringify(n):String(n)}var Fi=o('
Empreinte de la configuration en service
Version du binaire
Répertoire de données
Espace disque
',1),Mi=o('
'),Va=o(""),Bi=o(''),Vi=o("
"),Wi=o(`
Recopier reste possible : les valeurs entrent dans le brouillon, où elles se
- corrigent champ par champ avant l’enregistrement.
`),Gi=o(`
Ce fichier n’a été comparé à rien : la configuration en service n’a pas pu être
- lue. Ni « identique », ni « n champs changent » — ce que ce fichier changerait
- sur ce poste reste inconnu.
`),Hi=o(`
Ce fichier décrit la même configuration que celle en service : il n’y a rien à
- recopier. C’est ce qu’on veut lire à la fin d’un clonage. Deux champs ne sont pas
- comparés : la date du dernier enregistrement, que chaque poste écrit lui-même, et
- le mot de passe du catalogue, qu’aucun des deux ne porte en clair.
`),Ji=o('
'),Ki=o('
'),Xi=o(`
Champ
En service
Dans le fichier
Recopier n’applique rien : les valeurs entrent dans le brouillon, et c’est
- « Enregistrer » qui les met en service.
`,1),Yi=o('
',1),Qi=o(`
L’export emporte encore l’empreinte du mot de passe : c’est la seule lecture que le
- poste garde derrière la clé. L’import, lui, est lu PAR LE POSTE, qui écarte les deux
- secrets et le numéro de poste avant de dire ce qui changerait.
`,1),Zi=o('
Lecture des versions enregistrées…
'),eo=o(`
Les versions enregistrées n’ont pas pu être lues : cette liste ne dit donc rien de
- ce que ce poste garde. Ce n’est pas « aucune version ».
`),to=o('
Aucune version enregistrée : ce poste n’a jamais été reconfiguré.
'),ao=o('
'),ro=o(`
Remettre une version en service remplace la configuration du poste sur-le-champ, et
- ce qui n’a pas été enregistré est perdu : c’est le seul geste de cette page qui
- change le poste tout de suite, et le seul qui garde ses 72 px.
`,1),no=o('
');function so(n,t){Nt(t,!0);const s=vt(t,"admin",7),c=40,_=20,w=5,T=new Set(["modified_at","catalog.options.password"]),A=[{path:"station.name",name:"le nom du poste"},{path:"network.listen",name:"l’adresse d’écoute"},{path:"scale.options.port",name:"le port de la balance"},{path:"printer.options.queue",name:"la file d’impression"},{path:"catalog.options.url",name:"l’adresse du partage"},{path:"catalog.options.username",name:"le compte du partage"},{path:"catalog.images.path",name:"le chemin des images"}];let M=F(Lt([])),H=F("reading"),J=F(null),pe=F(""),fe=F(null),_e=F(""),ae=F(Lt([])),C=F(""),ge="";const Q=u(()=>s().busy||e(C)!==""),be=u(()=>D(e(J),e(fe))),se=u(()=>e(J)!==null&&e(fe)!==null),P=u(()=>X(e(J),e(fe))),ne=u(()=>e(be).slice(0,c)),V=u(()=>e(ae).slice(0,_)),B=u(()=>e(M).slice(0,w)),re=u(()=>We(e(ne).length,e(be).length,"champ qui change","champs qui changent")),N=u(()=>We(e(V).length,e(ae).length,"contrôle refuse une clé","contrôles refusent une clé")),L=u(()=>We(e(B).length,e(M).length,"version enregistrée","versions enregistrées")),q=u(()=>e(be).length>1?`Recopier ces ${he(e(be).length)} champs dans le brouillon`:"Recopier ce champ dans le brouillon");Ce();function D(f,E){return f===null||E===null?[]:Ui(f,E).filter(z=>!T.has(z.path))}function X(f,E){if(f===null||E===null)return[];const z=f,ye=E;return A.filter(ie=>me(ye,ie.path)&&!me(z,ie.path))}function me(f,E){const z=fa(f,E);return z==null||z===""?!0:typeof z!="object"||Array.isArray(z)?!1:Object.keys(z).length===0}function ze(f){return f.length<2?f.join(""):`${f.slice(0,-1).join(", ")} et ${f[f.length-1]??""}`}Xt(()=>{const f=t.health.config_fingerprint;f!==ge&&(ge=f,Le())});async function Ce(){const f=await s().load(Mr);if(f===null){p(H,"unreadable");return}p(M,f,!0),p(H,"read")}async function Le(){try{p(J,(await za()).config,!0),p(pe,"")}catch(f){p(J,null),p(pe,f instanceof Error?f.message:"Le poste n’a pas répondu.",!0)}}async function Z(f){p(C,f?"export-all":"export-clone",!0),s().notice="",s().actionError="";try{const E=await s().protect(()=>y(f));if(E===null)return;ee(E.name,E.blob),s().notice=`${E.name} est remis au navigateur : c’est lui qui l’enregistre, voyez ses téléchargements.`}finally{p(C,"")}}async function y(f){const z=await fetch(`/admin/api/config/export?hardware=${f?"1":"0"}`,{headers:{accept:"application/json"}});if(!z.ok)throw new Ft(z.status,O(await z.text(),"L’export"));return{name:I(f),blob:await z.blob()}}function I(f){const E=f?"":"-sans-materiel";return`config-poste${String(t.health.station)}${E}-${W()}.json`}function W(){const f=new Date,E=String(f.getMonth()+1).padStart(2,"0"),z=String(f.getDate()).padStart(2,"0");return`${String(f.getFullYear())}-${E}-${z}`}function ee(f,E){const z=URL.createObjectURL(E),ye=document.createElement("a");ye.href=z,ye.download=f,document.body.appendChild(ye),ye.click(),ye.remove(),setTimeout(()=>{URL.revokeObjectURL(z)},0)}async function ce(f){const E=await fetch("/admin/api/config/import",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(f)}),z=await E.text();if(!E.ok)throw new Ft(E.status,O(z,"L’import"));return JSON.parse(z)}async function Ve(f){if(f==null)return;s().notice="",s().actionError="";let E;try{E=JSON.parse(await f.text())}catch{s().actionError=`${f.name} n’est pas un fichier JSON lisible.`;return}p(C,"import");try{const z=await s().protect(()=>ce(E));if(z===null)return;p(fe,z.config,!0),p(_e,f.name,!0),p(ae,z.faults,!0),await Le(),s().notice=S(f.name)}finally{p(C,"")}}function S(f){return e(se)?e(be).length===0?`${f} décrit la même configuration que celle en service.`:`${f} est lu. Rien n’est appliqué : relisez le tableau.`:`${f} est lu, mais la configuration en service ne l’est pas : rien ne peut être comparé.`}async function h(){if(e(fe)===null)return;const f=e(fe),E=[...e(be)];for(const z of E)t.draft.set(z.path,fa(f,z.path));s().actionError="",s().notice="Le fichier est recopié dans le brouillon. Rien n’est appliqué avant « Enregistrer ».",await Le()}async function k(f){p(C,`restore-${String(f)}`),s().notice="",s().actionError="";try{if(await s().protect(()=>Br(f))===null)return;await t.draft.load(),await Ce(),await Le(),await s().refresh(),s().notice=`La version ${String(f)} est remise en service.`}finally{p(C,"")}}function O(f,E){try{const z=JSON.parse(f);if(typeof z?.message=="string"&&z.message!=="")return z.message}catch{}return`${E} a été refusé par le poste.`}function le(f){return t.draft.faults.find(E=>E.field===f)?.message??""}function Re(f){return t.draft.faults.find(E=>E.field===f)?.allowed??[]}function We(f,E,z,ye){const ie=E>1?ye:z;return f>=E?`${he(E)} ${ie}.`:`${he(f)} lignes affichées sur ${he(E)} ${ie}.`}var we=no(),d=a(we);Qe(d,{title:"Identité du poste",children:(f,E)=>{var z=Fi(),ye=Y(z);{let je=u(()=>t.draft.text("station.number")),$e=u(()=>le("station.number")),st=u(()=>Re("station.number"));_t(ye,{label:"Numéro du poste",path:"station.number",kind:"number",get value(){return e(je)},get fault(){return e($e)},get allowed(){return e(st)},hint:"C’est de lui que dérive le nom du fichier de catalogue attendu, flv_.csv.",onchange:it=>t.draft.set("station.number",Number(it))})}var ie=r(ye,2);{let je=u(()=>t.draft.text("station.name")),$e=u(()=>le("station.name")),st=u(()=>Re("station.name"));_t(ie,{label:"Nom du poste",path:"station.name",get value(){return e(je)},get fault(){return e($e)},get allowed(){return e(st)},hint:"Ce que lit un bénévole : « Poste 2 — fruits ».",onchange:it=>t.draft.set("station.name",it)})}var ke=r(ie,2);{let je=u(()=>t.draft.text("station.coop")),$e=u(()=>le("station.coop")),st=u(()=>Re("station.coop"));_t(ke,{label:"Coopérative",path:"station.coop",get value(){return e(je)},get fault(){return e($e)},get allowed(){return e(st)},onchange:it=>t.draft.set("station.coop",it)})}var Oe=r(ke,2),de=r(a(Oe),2),K=a(de),Ae=r(de,4),Ge=a(Ae),Fe=r(Ae,4),De=a(Fe),Me=r(Fe,4),te=a(Me);{var Ne=je=>{var $e=xt("non mesuré");l(je,$e)},ve=je=>{var $e=xt();v((st,it,pt)=>i($e,`${st??""} libres sur
- ${it??""} — seuil d’alerte
- ${pt??""} Mo`),[()=>ka(t.health.disk.free_bytes),()=>ka(t.health.disk.total_bytes),()=>he(t.health.disk.alert_mb)]),l(je,$e)};g(te,je=>{t.health.disk===null?je(Ne):je(ve,-1)})}v(()=>{i(K,t.health.config_fingerprint),i(Ge,t.health.version),i(De,t.health.disk===null?"non publié par ce poste":t.health.disk.path)}),l(f,z)},$$slots:{default:!0}});var x=r(d,2);Qe(x,{title:"Exporter, importer",note:"Pour installer un autre poste : ce fichier emporte les tarifs, les garde-fous, l’étiquette, les catégories, et les réglages du matériel que les quatre postes partagent — le décalage d’étiquette, le noircissement, la vitesse, le débit de la balance. Reste ici ce qui désigne ce poste-ci ou ce magasin — le mot de passe, le code de secours, le numéro et le nom du poste, le port de la balance, la file d’impression, l’adresse du partage et son compte, le chemin des images et le réseau.",children:(f,E)=>{var z=Qi(),ye=Y(z),ie=a(ye);{let te=u(()=>e(C)==="export-all");tt(ie,{label:"Exporter tout",protected:!0,get busy(){return e(te)},get disabled(){return e(Q)},onrun:()=>{Z(!0)}})}var ke=r(ie,2);{let te=u(()=>e(C)==="export-clone");tt(ke,{label:"Exporter sans le matériel",protected:!0,get busy(){return e(te)},get disabled(){return e(Q)},onrun:()=>{Z(!1)}})}var Oe=r(ke,2);let de;var K=a(Oe),Ae=r(K,3),Ge=r(ye,4);{var Fe=te=>{var Ne=Mi(),ve=a(Ne);v(()=>i(ve,`La configuration en service n’a pas pu être lue : ${e(pe)??""} La colonne
- « En service » ne peut donc rien affirmer.`)),l(te,Ne)};g(Ge,te=>{e(pe)!==""&&te(Fe)})}var De=r(Ge,2);{var Me=te=>{var Ne=Yi(),ve=Y(Ne),je=a(ve),$e=r(ve,2);{var st=et=>{var mt=Wi(),Dt=a(mt),Bt=a(Dt),Vt=r(Dt,2);at(Vt,21,()=>e(V),ma,(Et,bt)=>{var Tt=Vi(),b=a(Tt),G=a(b),oe=r(b,2);{var qe=j=>{var U=Va(),xe=a(U);v(()=>i(xe,e(bt).field)),l(j,U)};g(oe,j=>{Ht.showTechnicalNames&&j(qe)})}var Je=r(oe),Se=r(Je);{var Pe=j=>{var U=Bi(),xe=a(U);v(Xe=>i(xe,`Valeurs acceptées : ${Xe??""}.`),[()=>e(bt).allowed.join(", ")]),l(j,U)};g(Se,j=>{e(bt).allowed!==void 0&&e(bt).allowed.length>0&&j(Pe)})}v(j=>{i(G,j),i(Je,` ${e(bt).message??""} `)},[()=>Gt(e(bt).field)]),l(Et,Tt)}),v(()=>i(Bt,`Ce fichier serait refusé en l’état : ${e(N)??""}`)),l(et,mt)};g($e,et=>{e(ae).length>0&&et(st)})}var it=r($e,2);{var pt=et=>{var mt=Gi();l(et,mt)},Ee=et=>{var mt=Hi();l(et,mt)},nt=et=>{var mt=Xi(),Dt=Y(mt),Bt=a(Dt),Vt=r(Bt);{var Et=Se=>{var Pe=xt("Les autres sont dans le fichier, et « Recopier » les prend tous.");l(Se,Pe)};g(Vt,Se=>{e(be).length>e(ne).length&&Se(Et)})}var bt=r(Dt,2),Tt=a(bt),b=r(a(Tt));at(b,21,()=>e(ne),Se=>Se.path,(Se,Pe)=>{const j=u(()=>Gt(e(Pe).path));var U=Ji(),xe=a(U),Xe=a(xe),Ke=r(Xe);{var Ye=dt=>{var ot=Va(),ut=a(ot);v(()=>i(ut,e(Pe).path)),l(dt,ot)};g(Ke,dt=>{Ht.showTechnicalNames&&e(j)!==e(Pe).path&&dt(Ye)})}var ue=r(xe),Ie=a(ue),Be=r(ue),ct=a(Be);v(()=>{Ue(U,"data-path",e(Pe).path),i(Xe,`${e(j)??""} `),i(Ie,e(Pe).before),i(ct,e(Pe).after)}),l(Se,U)});var G=r(bt,2);{var oe=Se=>{var Pe=Ki(),j=a(Pe);v(U=>i(j,`Ce fichier ne porte pas ${U??""} :
- l’export sans le matériel les retire, et l’import ne remet que le numéro du
- poste. Les lignes correspondantes sont VIDES ci-dessus, et
- « Recopier » recopie ce vide dans le brouillon.`),[()=>ze(e(P).map(U=>U.name))]),l(Se,Pe)};g(G,Se=>{e(P).length>0&&Se(oe)})}var qe=r(G,2),Je=a(qe);tt(Je,{kind:"write",get label(){return e(q)},get disabled(){return e(Q)},onrun:()=>{h()}}),v(()=>i(Bt,`${e(re)??""} `)),l(et,mt)};g(it,et=>{e(se)?e(be).length===0?et(Ee,1):et(nt,-1):et(pt)})}v(()=>i(je,`Fichier lu : ${e(_e)??""}`)),l(te,Ne)};g(De,te=>{e(fe)!==null&&te(Me)})}v(()=>{de=Zt(Oe,1,"choose svelte-18wbxwu",null,de,{working:e(C)==="import",off:e(Q)}),i(K,`${e(C)==="import"?"Lecture du fichier…":"Importer un fichier"} `),Ae.disabled=e(Q)}),Ze("change",Ae,te=>{Ve(te.currentTarget.files?.item(0))}),l(f,z)},$$slots:{default:!0}});var $=r(x,2);Qe($,{title:"Cinq versions restaurables",note:"Chaque enregistrement fait tourner les versions : la plus récente est la 1.",children:(f,E)=>{var z=Ut(),ye=Y(z);{var ie=K=>{var Ae=Zi();l(K,Ae)},ke=K=>{var Ae=eo();l(K,Ae)},Oe=K=>{var Ae=to();l(K,Ae)},de=K=>{var Ae=ro(),Ge=Y(Ae),Fe=a(Ge),De=r(Ge,2),Me=a(De);at(Me,21,()=>e(B),te=>te.version,(te,Ne)=>{var ve=ao(),je=a(ve),$e=a(je),st=r(je,2),it=a(st),pt=r(st,2),Ee=a(pt),nt=r(pt,2);{let et=u(()=>e(C)===`restore-${String(e(Ne).version)}`);tt(nt,{kind:"destructive",label:"Remettre cette version en service",protected:!0,get busy(){return e(et)},get disabled(){return e(Q)},onrun:()=>{k(e(Ne).version)}})}v((et,mt)=>{i($e,`version ${et??""}`),i(it,mt),i(Ee,e(Ne).config_fingerprint)},[()=>he(e(Ne).version),()=>Mt(e(Ne).modified_at)]),l(te,ve)}),v(()=>i(Fe,`${e(L)??""} Le poste n’en garde jamais plus de cinq.`)),l(K,Ae)};g(ye,K=>{e(H)==="reading"?K(ie):e(H)==="unreadable"?K(ke,1):e(M).length===0?K(Oe,2):K(de,-1)})}l(f,z)},$$slots:{default:!0}}),l(n,we),$t()}At(["change"]);var lo=o('clé'),io=o(''),oo=o('');function aa(n,t){const s=vt(t,"kind",3,"read"),c=vt(t,"hint",3,""),_=vt(t,"disabled",3,!1),w=vt(t,"engaged",3,!1),T=vt(t,"busy",3,!1),A=vt(t,"protected",3,!1);var M=oo();let H;var J=a(M),pe=a(J),fe=r(pe);{var _e=ge=>{var Q=lo();l(ge,Q)};g(fe,ge=>{A()&&ge(_e)})}var ae=r(J,2);{var C=ge=>{var Q=io(),be=a(Q);v(()=>i(be,T()?"En cours…":c())),l(ge,Q)};g(ae,ge=>{c()!==""&&ge(C)})}v(()=>{H=Zt(M,1,`big touch-target ${s()??""}`,"svelte-15nlt6i",H,{engaged:w(),busy:T()}),Ue(M,"data-kind",s()),M.disabled=_()||T(),i(pe,`${t.label??""} `)}),Ze("click",M,function(...ge){t.onrun?.apply(this,ge)}),l(n,M)}At(["click"]);const uo="ERR-SCL-09";var co=o('
');function ho(n,t){Nt(t,!0);const s=vt(t,"admin",7);let c=F("");const _=u(()=>t.health.state.degraded?.code===uo),w=u(()=>t.health.printing),T=u(()=>e(w)?.on_fallback===!0);let A=F(!1),M=F("");const H=u(()=>s().busy||e(M)!==""),J=new ir;Xt(()=>{J.observe(t.health)});const pe=u(()=>t.health.catalog_source===null?"Ce poste ne publie pas la source de son catalogue.":"Catalogue surveillé : "+t.health.catalog_source.label),fe=u(()=>t.health.catalog!==null&&(t.health.catalog.result==="rejected"||t.health.catalog.result==="failed")?t.health.catalog:null);function _e(h){p(c,""),J.forget(),p(M,h,!0)}async function ae(h,k){_e(h);try{await s().run(k)}finally{p(M,"")}}async function C(){_e("reload");try{const h=await s().run(jr);h!==null&&J.begin(h)}finally{p(M,"")}}async function ge(h,k){_e(h);try{const O=await s().load(k);O!==null&&p(c,O.message,!0)}finally{p(M,"")}}async function Q(h,k){_e(h);try{const O=await s().protect(k);O!==null&&(s().notice=O.message,await s().refresh())}finally{p(M,"")}}async function be(h){if(p(A,!1),h!=null){_e("import");try{const k=await s().protect(()=>Ya(h));if(k===null)return;p(c,k.result==="rejected"||k.result==="failed"?`${h.name} : REFUSÉ${k.reason===""?"":" — "+k.reason}. Le catalogue en service n’a pas changé.`:`${h.name} : ${String(k.rows_read_count)} lignes lues, ${String(k.weighable_count)} pesables. La veille l’appliquera dans la seconde.`,!0),await s().refresh()}finally{p(M,"")}}}var se=mo(),P=a(se);{var ne=h=>{var k=co(),O=a(k);v(()=>i(O,e(c))),l(h,k)};g(P,h=>{e(c)!==""&&h(ne)})}var V=r(P,2);{var B=h=>{var k=vo(),O=a(k);v(()=>i(O,J.sentence)),l(h,k)};g(V,h=>{J.sentence!==""&&h(B)})}var re=r(V,2);{var N=h=>{var k=po(),O=a(k);v(()=>i(O,e(w).banner)),l(h,k)};g(re,h=>{e(w)!==null&&e(w).banner!==""&&h(N)})}var L=r(re,2),q=a(L),D=r(L,2),X=a(D);{let h=u(()=>e(M)==="scale");aa(X,{label:"Tester la balance",hint:"Ce que le poste a déjà observé — le port n’est pas rouvert.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{ge("scale",Or)}})}var me=r(X,2);{let h=u(()=>e(M)==="printer");aa(me,{label:"Tester l’imprimante",hint:"Ce que le superviseur a vu il y a moins d’une seconde.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{ge("printer",Rr)}})}var ze=r(me,2);{let h=u(()=>e(M)==="label");aa(ze,{label:"Imprimer une étiquette de test",hint:"Une étiquette de démonstration sort de l’imprimante du poste.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{ae("label",Ar)}})}var Ce=r(ze,2);{let h=u(()=>e(M)==="reprint");aa(Ce,{label:"Réimprimer la dernière",hint:"La dernière étiquette imprimée sort une seconde fois.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{ae("reprint",zr)}})}var Le=r(Ce,2);{let h=u(()=>e(M)==="reload");aa(Le,{label:"Recharger le catalogue",kind:"write",hint:"La veille refait tout de suite le contrôle qu’elle fait toutes les cinq secondes.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{C()}})}var Z=r(Le,2);{let h=u(()=>e(_)?"Revenir à la balance":"Basculer en saisie manuelle"),k=u(()=>e(_)?"Le poids sera de nouveau lu sur la balance.":"Le poids se tape à la main : le poste continue de servir sans balance."),O=u(()=>e(M)==="manual");aa(Z,{get label(){return e(h)},kind:"destructive",get hint(){return e(k)},get engaged(){return e(_)},protected:!0,get busy(){return e(O)},get disabled(){return e(H)},onrun:()=>{Q("manual",()=>Pr(!e(_)))}})}var y=r(Z,2);{let h=u(()=>e(M)==="roll");aa(y,{label:"J’ai changé le rouleau",kind:"write",hint:"Le compteur d’étiquettes repart à zéro. C’est le seul geste qui dise quelque chose de vrai du papier.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{ae("roll",Tr)}})}var I=r(y,2);{var W=h=>{{let k=u(()=>e(T)?"Revenir à l’imprimante du poste":"Imprimer sur l’imprimante du poste voisin"),O=u(()=>e(T)?"Les étiquettes repartiront sur l’imprimante de ce poste.":"Les étiquettes sortiront sur l’imprimante voisine, pour cette session seulement."),le=u(()=>e(M)==="fallback");aa(h,{get label(){return e(k)},kind:"write",get hint(){return e(O)},get engaged(){return e(T)},get busy(){return e(le)},get disabled(){return e(H)},onrun:()=>{ae("fallback",()=>Cr(!e(T)))}})}};g(I,h=>{e(w)!==null&&e(w).fallback_available&&h(W)})}var ee=r(I,2),ce=r(D,2);Qe(ce,{title:"Importer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(h,k)=>{var O=fo();let le;var Re=a(O),We=r(a(Re)),we=a(We),d=r(Re,2),x=r(a(d));v(()=>{le=Zt(O,1,"drop svelte-1yd0nzg",null,le,{dropping:e(A)}),i(we,`flv_${t.health.station??""}.csv`)}),na("dragover",O,$=>{$.preventDefault(),p(A,!0)}),na("dragleave",O,()=>p(A,!1)),na("drop",O,$=>{$.preventDefault(),be($.dataTransfer?.files.item(0))}),Ze("change",x,$=>{be($.currentTarget.files?.item(0))}),l(h,O)},$$slots:{default:!0}});var Ve=r(ce,2);{var S=h=>{Qe(h,{title:"Le dernier fichier n’a pas pris service",children:(k,O)=>{var le=go(),Re=Y(le),We=a(Re),we=r(Re,2),d=a(we);v((x,$)=>{i(We,`${x??""} —
- ${(e(fe).reason===""?"aucun motif enregistré.":e(fe).reason)??""}
- Le catalogue en service n’a pas changé.`),i(d,`Dernier essai : ${$??""}.`)},[()=>ha(e(fe).result),()=>Mt(e(fe).occurred_at)]),l(k,le)},$$slots:{default:!0}})};g(Ve,h=>{e(fe)!==null&&h(S)})}v(()=>{i(q,e(pe)),Ue(ee,"href",Nr)}),l(n,se),$t()}At(["change"]);const _o={succeeded:"La dernière mise à jour a réussi.","rolled-back":"La dernière mise à jour a échoué. La version précédente a été remise et le poste fonctionne.","rolled-back-unhealthy":"La dernière mise à jour a échoué et le poste n’a pas redémarré. Appelez le support.","not-started":"La dernière mise à jour n’a pas démarré : rien n’a été remplacé. Vous pouvez réessayer."},bo=2e3,wo=300*1e3;var yo=o('
'),Wa=o('
'),xo=o("
Dernière vérification
",1),ko=o(`
La mise à jour depuis cet écran n’existe que sur les postes Windows. Sur les
- autres, elle se fait à la main — voir la notice d’installation.
Le poste va s’arrêter environ une minute. L’écran client s’éteindra puis reviendra
- tout seul. Si la nouvelle version ne démarre pas, la précédente sera remise
- automatiquement — mais les données enregistrées, elles, ne reviendront pas en
- arrière.
`),jo=o('
Mise à jour en cours. Le poste redémarre, ne le débranchez pas.
'),Po=o('
',1),To=o("
Terminée le
",1),Co=o("
Raison
",1),Oo=o('
Depuis
Vers
',1),Ro=o(" ",1);function Ao(n,t){Nt(t,!0);let s=F(null),c=F(""),_=F(""),w=F(!1),T=F("");const A=u(()=>e(s)?.outcome??null),M=u(()=>e(s)?.supported===!0&&e(s).available),H=u(()=>e(s)?.latest??"");Xt(()=>{J()});async function J(){try{p(s,await nn(),!0),p(c,"")}catch(P){p(c,P instanceof Error?P.message:"Lecture impossible.",!0)}}async function pe(){p(_,"check");try{const P=await t.admin.protect(()=>sn());P!==null&&(p(s,P,!0),p(c,""))}finally{p(_,"")}}async function fe(P){p(_,"apply"),p(T,"");try{if(await t.admin.protect(()=>ln(P))===null)return;p(w,!0),p(T,await _e()?"":"Le poste n’a pas répondu dans les cinq minutes. Allez le voir.",!0),await J()}finally{p(_,""),p(w,!1)}}async function _e(){const P=Date.now()+wo;for(;Date.now()
');function Qo(n,t){Nt(t,!0);const s=new wn,c=new on(s),_=[{title:"Au quotidien",pages:[{id:"dashboard",label:"Tableau de bord"},{id:"troubleshooting",label:"Dépannage"}]},{title:"Réglages",pages:[{id:"hardware",label:"Matériel"},{id:"label",label:"Étiquette"},{id:"rules",label:"Règles"},{id:"catalog",label:"Catalogue"},{id:"journal",label:"Journal"},{id:"station",label:"Poste"},{id:"update",label:"Mise à jour"}]}],w=u(()=>_.flatMap(d=>d.pages).find(d=>d.id===s.page)?.label??""),T=u(()=>s.health===null||s.health.station_name===""?`Poste ${String(s.health?.station??"")}`:s.health.station_name),A=u(()=>(c.pending?.changed_blocks??[]).map(d=>vn(d)).join(", ")),M=u(()=>(c.pending?.changed_blocks??[]).join(", "));Ea(()=>(s.start(),()=>s.stop()));async function H(d){s.open(d),$a(d)&&c.config===null&&await c.load()}async function J(){await s.protect(()=>c.save())===!0&&(s.notice="La configuration est enregistrée et appliquée.")}var pe=Yo(),fe=a(pe),_e=r(a(fe),2);at(_e,17,()=>_,d=>d.title,(d,x)=>{var $=$o(),f=Y($),E=a(f),z=r(f,2);at(z,17,()=>e(x).pages,ye=>ye.id,(ye,ie)=>{var ke=No();let Oe;var de=a(ke);v(()=>{Oe=Zt(ke,1,"entry svelte-9b2mjq",null,Oe,{current:s.page===e(ie).id}),Ue(ke,"aria-current",s.page===e(ie).id?"page":void 0),i(de,e(ie).label)}),Ze("click",ke,()=>{H(e(ie).id)}),l(ye,ke)}),v(()=>i(E,e(x).title)),l(d,$)});var ae=r(_e,2),C=a(ae);{var ge=d=>{var x=Do(),$=Y(x),f=a($),E=r($,2),z=a(E),ye=r(E,2),ie=a(ye);v(()=>{i(f,e(T)),i(z,`${s.health.coop??""} · version ${s.health.version??""}`),i(ie,`configuration ${s.health.config_fingerprint??""}`)}),l(d,x)};g(C,d=>{s.health!==null&&d(ge)})}var Q=r(C,2),be=a(Q),se=r(Q,2);{var P=d=>{var x=Io();Ze("click",x,function(...$){t.onclose?.apply(this,$)}),l(d,x)};g(se,d=>{t.onclose!==void 0&&d(P)})}var ne=r(fe,2),V=a(ne);{var B=d=>{var x=Uo(),$=a(x);v(()=>i($,s.notice)),l(d,x)};g(V,d=>{s.notice!==""&&d(B)})}var re=r(V,2);{var N=d=>{var x=Fo(),$=a(x);v(()=>i($,s.linkError)),l(d,x)};g(re,d=>{s.linkError!==""&&d(N)})}var L=r(re,2);{var q=d=>{var x=Mo(),$=a(x);v(()=>i($,s.actionError)),l(d,x)};g(L,d=>{s.actionError!==""&&d(q)})}var D=r(L,2);{var X=d=>{var x=Vo(),$=a(x),f=a($),E=r(f);{var z=ke=>{var Oe=Bo(),de=a(Oe);v(()=>i(de,e(M))),l(ke,Oe)};g(E,ke=>{Ht.showTechnicalNames&&ke(z)})}var ye=r(E),ie=r($,2);tt(ie,{kind:"write",label:"Tout fonctionne : confirmer",onrun:()=>{s.protect(()=>c.confirm())}}),v(()=>{i(f,`Configuration appliquée mais NON CONFIRMÉE. Ce qui a changé : ${e(A)??""}. `),i(ye,` Le poste reviendra tout seul à la version précédente dans
- ${c.pending.seconds_left??""} secondes si personne ne confirme.`)}),l(d,x)};g(D,d=>{c.pending!==null&&d(X)})}var me=r(D,2),ze=a(me),Ce=a(ze),Le=r(ze,2);{var Z=d=>{var x=Wo();l(d,x)},y=d=>{ql(d,{get health(){return s.health},onshowrows:()=>{H("catalog")},onshowupdate:()=>{H("update")}})},I=d=>{ho(d,{get admin(){return s},get health(){return s.health}})},W=d=>{Nl(d,{get admin(){return s},get draft(){return c},get health(){return s.health}})},ee=d=>{ci(d,{get admin(){return s},get draft(){return c}})},ce=d=>{Ii(d,{get draft(){return c},get health(){return s.health}})},Ve=d=>{Ds(d,{get admin(){return s},get draft(){return c},get health(){return s.health}})},S=d=>{ti(d,{get admin(){return s}})},h=d=>{so(d,{get admin(){return s},get draft(){return c},get health(){return s.health}})},k=d=>{Ao(d,{get admin(){return s}})};g(Le,d=>{s.health===null?d(Z):s.page==="dashboard"?d(y,1):s.page==="troubleshooting"?d(I,2):s.page==="hardware"?d(W,3):s.page==="label"?d(ee,4):s.page==="rules"?d(ce,5):s.page==="catalog"?d(Ve,6):s.page==="journal"?d(S,7):s.page==="station"?d(h,8):s.page==="update"&&d(k,9)})}var O=r(me,2);{var le=d=>{var x=Xo(),$=a(x);{var f=ie=>{var ke=Go(),Oe=a(ke),de=r(Oe);at(de,16,()=>c.retired,K=>K,(K,Ae)=>{{let Ge=u(()=>`retirer ${Ae}`);tt(K,{kind:"write",get label(){return e(Ge)},onrun:()=>c.dropRetired(Ae)})}}),v(K=>i(Oe,`Ce fichier porte des réglages que cette version du poste ne connaît plus :
- ${K??""}. `),[()=>c.retired.map(K=>Gt(K)).join(", ")]),l(ie,ke)};g($,ie=>{c.retired.length>0&&ie(f)})}var E=r($,2);{var z=ie=>{var ke=Ko();at(ke,21,()=>c.faults,Oe=>Oe.field,(Oe,de)=>{var K=Jo(),Ae=a(K),Ge=a(Ae),Fe=r(Ae,2);{var De=ve=>{var je=Ho(),$e=a(je);v(()=>i($e,e(de).field)),l(ve,je)};g(Fe,ve=>{Ht.showTechnicalNames&&ve(De)})}var Me=r(Fe),te=r(Me);{var Ne=ve=>{var je=xt();v($e=>i(je,`— valeurs acceptées : ${$e??""}`),[()=>e(de).allowed.join(", ")]),l(ve,je)};g(te,ve=>{e(de).allowed!==void 0&&e(de).allowed.length>0&&ve(Ne)})}v(ve=>{i(Ge,ve),i(Me,` ${e(de).message??""} `)},[()=>Gt(e(de).field)]),l(Oe,K)}),l(ie,ke)};g(E,ie=>{c.faults.length>0&&ie(z)})}var ye=r(E,2);{let ie=u(()=>c.dirty?"Enregistrer la configuration":"Aucune modification à enregistrer"),ke=u(()=>!c.dirty||s.busy);tt(ye,{kind:"write",get label(){return e(ie)},get disabled(){return e(ke)},onrun:()=>{J()}})}l(d,x)},Re=u(()=>$a(s.page)&&c.config!==null);g(O,d=>{e(Re)&&d(le)})}var We=r(ne,2);{var we=d=>{En(d,{get admin(){return s}})};g(We,d=>{s.pending!==null&&d(we)})}v(()=>{la(be,Ht.showTechnicalNames),i(Ce,e(w))}),Ze("change",be,()=>Ht.toggleTechnicalNames()),l(n,pe),$t()}At(["click","change"]);let da=null;function tu(n){if(da!==null||n.querySelector("[data-admin]")!==null)return;const t=document.createElement("div");n.appendChild(t),da={component:yr(Qo,{target:t,props:{onclose:Zo}}),host:t}}function Zo(){if(da===null)return;const{component:n,host:t}=da;da=null,xr(n),t.remove()}export{Zo as closeAdmin,tu as mountAdmin};
diff --git a/internal/web/dist/assets/mount-CEsnfFiz.js b/internal/web/dist/assets/mount-CEsnfFiz.js
new file mode 100644
index 0000000..46ab559
--- /dev/null
+++ b/internal/web/dist/assets/mount-CEsnfFiz.js
@@ -0,0 +1,95 @@
+import{R as cr,T as dr,U as vr,V as Sa,W as ra,e as pr,X as fr,Y as gr,Z as mr,_ as Ga,u as ga,r as La,$ as hr,c as Ha,B as Xt,A as F,k as e,z as p,F as Lt,d as At,b as vt,s as r,i as g,t as v,n as Zt,l as Ue,o as i,f as Ze,g as l,v as a,j as o,p as Nt,I as _r,h as $t,q as u,w as Y,a0 as Ta,E as oa,x as at,G as Ja,a1 as la,y as na,a2 as Ut,a3 as ia,a4 as xt,C as ma,K as br,H as wr,P as yr,a5 as xr}from"./app-Bm_67CUJ.js";function Ka(n){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function kr(n,t,...s){var c=new vr(n);cr(()=>{const _=t()??null;c.ensure(_,_&&(w=>_(w,...s)))},dr)}function Xa(n,t,s=!1){if(n.multiple){if(t==null)return;if(!fr(t))return gr();for(var c of n.options)c.selected=t.includes(ua(c));return}for(c of n.options){var _=ua(c);if(mr(_,t)){c.selected=!0;return}}(!s||t!==void 0)&&(n.selectedIndex=-1)}function qr(n){var t=new MutationObserver(()=>{"__value"in n&&Xa(n,n.__value)});t.observe(n,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),Ga(()=>{t.disconnect()})}function Sr(n,t,s=t){var c=new WeakSet,_=!0;Sa(n,"change",w=>{var T=w?"[selected]":":checked",A;if(n.multiple)A=[].map.call(n.querySelectorAll(T),ua);else{var M=n.querySelector(T)??n.querySelector("option:not([disabled])");A=M&&ua(M)}s(A),n.__value=A,ra!==null&&c.add(ra)}),pr(()=>{var w=t();if(n===document.activeElement){var T=ra;if(c.has(T))return}if(Xa(n,w,_),_&&w===void 0){var A=n.querySelector(":checked");A!==null&&(w=ua(A),s(w))}n.__value=w,_=!1}),qr(n)}function ua(n){return"__value"in n?n.__value:n.value}function ca(n,t,s=t){var c=new WeakSet;Sa(n,"input",async _=>{var w=_?n.defaultValue:n.value;if(w=ba(n)?wa(w):w,s(w),ra!==null&&c.add(ra),await hr(),w!==(w=t())){var T=n.selectionStart,A=n.selectionEnd,M=n.value.length;if(n.value=w??"",A!==null){var H=n.value.length;T===A&&A===M&&H>M?(n.selectionStart=H,n.selectionEnd=H):(n.selectionStart=T,n.selectionEnd=Math.min(A,H))}}}),ga(t)==null&&n.value&&(s(ba(n)?wa(n.value):n.value),ra!==null&&c.add(ra)),La(()=>{var _=t();if(n===document.activeElement){var w=ra;if(c.has(w))return}ba(n)&&_===wa(n.value)||n.type==="date"&&!_&&!n.value||_!==n.value&&(n.value=_??"")})}function Ca(n,t,s=t){Sa(n,"change",c=>{var _=c?n.defaultChecked:n.checked;s(_)}),ga(t)==null&&s(n.checked),La(()=>{var c=t();n.checked=!!c})}function ba(n){var t=n.type;return t==="number"||t==="range"}function wa(n){return n===""?null:+n}function Oa(n,t,s,c,_){var w=()=>{c(s[n])};s.addEventListener(t,w),_?La(()=>{s[n]=_()}):w(),(s===document.body||s===window||s===document)&&Ga(()=>{s.removeEventListener(t,w)})}function Ea(n){Ha===null&&Ka(),Xt(()=>{const t=ga(n);if(typeof t=="function")return t})}function Lr(n){Ha===null&&Ka(),Ea(()=>()=>ga(n))}class Ft extends Error{constructor(t,s,c="",_=[]){super(s),this.status=t,this.code=c,this.faults=_,this.name="AdminError"}status;code;faults;get needsPassword(){return this.status===401}}function Er(){return ea("/admin/api/health")}function zr(){return gt("/admin/api/troubleshooting/reprint",{})}function jr(){return gt("/admin/api/troubleshooting/reload-catalog",{})}function Pr(n){return gt("/admin/api/troubleshooting/manual-entry",{on:n})}function Tr(){return gt("/admin/api/troubleshooting/roll-changed",{})}function Cr(n){return gt("/admin/api/troubleshooting/fallback-printer",{on:n})}function Or(){return gt("/admin/api/troubleshooting/test-scale",{})}function Rr(){return gt("/admin/api/troubleshooting/test-printer",{})}function Ar(){return gt("/admin/api/troubleshooting/test-label",{})}async function Ya(n){const t=new FormData;t.append("file",n,n.name);const s=await fetch("/admin/api/catalog/import",{method:"POST",body:t});return ja(s,"POST /admin/api/catalog/import")}const Nr="/admin/api/diagnostic.zip";function $r(n){return gt("/admin/api/session",{password:n})}async function Dr(){await fetch("/admin/api/session",{method:"DELETE"})}function Ir(n,t){return gt("/admin/api/session/recovery",{code:n,password:t})}function za(){return ea("/admin/api/config")}function Ur(n){return Za("PUT","/admin/api/config",n)}function Fr(){return gt("/admin/api/config/confirm",{})}async function Mr(){return(await ea("/admin/api/config/versions")).versions}function Br(n){return gt("/admin/api/config/restore",{version:n})}async function Vr(){return(await ea("/admin/api/ports")).ports}async function Wr(){return(await ea("/admin/api/printers")).printers}async function Gr(){return(await gt("/admin/api/printers/discover",{})).printers}function Hr(n){return gt("/admin/api/scale/detect",{port:n})}async function Ra(n,t){return(await gt("/admin/api/scale/capture",{port:n,seconds:t})).frames}function Qa(n){return gt("/admin/api/printer/test?what="+n,{})}function Jr(n,t,s,c){const _=new URLSearchParams({template:n,demo:t?"1":"0",dual:s?"1":"0"});return _.set("t",String(c)),"/admin/api/label/preview.png?"+_.toString()}async function Kr(n){const t=new URLSearchParams(n);return(await ea("/admin/api/journal?"+t)).weighings}function Xr(n){return"/admin/api/journal/export.csv?"+new URLSearchParams(n).toString()}async function Yr(n){const t=new URLSearchParams(n);return(await ea("/admin/api/technical?"+t)).entries}function Qr(n){const t=n===void 0?"":"?id="+String(n);return ea("/admin/api/imports"+t)}function Zr(){return gt("/admin/api/catalog/reload",{})}function en(){return gt("/admin/api/catalog/forget-quarantine",{})}function Aa(n,t){return gt("/admin/api/products/"+encodeURIComponent(n)+"/decision",t)}function tn(n){return gt("/admin/api/replay",{frame:n})}async function ea(n){const t=await fetch(n,{headers:{accept:"application/json"}});return ja(t,"GET "+n)}function gt(n,t){return Za("POST",n,t)}async function Za(n,t,s){const c=await fetch(t,{method:n,headers:{"content-type":"application/json"},body:JSON.stringify(s)});return ja(c,n+" "+t)}async function ja(n,t){const s=await n.text();if(!n.ok){const c=er(s);throw new Ft(n.status,rn(s,t,n.status)+an(n),c?.code??"",c?.faults??[])}if(s!=="")return JSON.parse(s)}function an(n){if(n.status!==429)return"";const t=Number(n.headers.get("Retry-After")??"");if(!Number.isFinite(t)||t<=0)return"";const s=Math.ceil(t/60);return s>1?` Réessayez dans ${String(s)} minutes.`:" Réessayez dans une minute."}function rn(n,t,s){const c=er(n);return c?.message!==void 0&&c.message!==""?c.message:`${t} a répondu ${String(s)}.`}function er(n){try{return JSON.parse(n)}catch{return null}}function nn(){return ea("/admin/api/update")}function sn(){return gt("/admin/api/update/check",{})}function ln(n){return gt("/admin/api/update/apply",{version:n})}class on{constructor(t){this.admin=t}admin;#t=F(null);get config(){return e(this.#t)}set config(t){p(this.#t,t,!0)}#r=F("");get fingerprint(){return e(this.#r)}set fingerprint(t){p(this.#r,t,!0)}#a=F(Lt([]));get retired(){return e(this.#a)}set retired(t){p(this.#a,t,!0)}#e=F(null);get pending(){return e(this.#e)}set pending(t){p(this.#e,t,!0)}#n=F(Lt([]));get faults(){return e(this.#n)}set faults(t){p(this.#n,t,!0)}#l=F(!1);get dirty(){return e(this.#l)}set dirty(t){p(this.#l,t,!0)}async load(){const t=await this.admin.load(()=>za());t!==null&&(this.config=t.config,this.fingerprint=t.config_fingerprint,this.retired=t.retired_keys??[],this.pending=t.pending_confirmation,this.faults=[],this.dirty=!1)}value(t){let s=this.config;for(const c of t.split(".")){if(s===null||typeof s!="object")return;s=s[c]}return s}text(t){const s=this.value(t);return s==null?"":String(s)}number(t){const s=this.value(t);return typeof s=="number"?s:Number(s??0)}flag(t){return this.value(t)===!0}set(t,s){if(this.config===null)return;const c=t.split("."),_=c.pop();if(_===void 0)return;let w=this.config;for(const T of c){const A=w[T];(A===null||typeof A!="object")&&(w[T]={}),w=w[T]}w[_]=s,this.dirty=!0}unset(t){const s=t.split("."),c=s.pop();if(c===void 0||this.config===null)return!1;let _=this.config;for(const w of s){if(_===null||typeof _!="object")return!1;_=_[w]}return _===null||typeof _!="object"?!1:(delete _[c],this.dirty=!0,!0)}async save(){if(this.config===null)return!1;this.faults=[];const t=this.config;let s;try{s=await Ur(t)}catch(c){if(Na(c))throw c;return this.admin.report(c),this.faults=un(this.admin),!1}return this.config=s.config,this.fingerprint=s.config_fingerprint,this.retired=s.retired_keys??[],this.pending=s.pending_confirmation,this.dirty=!1,!0}async confirm(){try{await Fr()}catch(t){if(Na(t))throw t;this.admin.report(t);return}this.admin.notice="La configuration est confirmée.",this.pending=null,await this.admin.refresh()}dropRetired(t){if(t.includes("[")){this.admin.actionError=`« ${t} » est logée dans un tableau : cet écran ne sait pas la retirer de là. Modifiez le fichier de configuration lui-même.`;return}this.unset(t)&&(this.retired=this.retired.filter(s=>s!==t))}}function un(n){return n.lastFaults}function Na(n){return n instanceof Ft&&(n.status===401||n.status===409)}const cn={"station.number":"Numéro du poste","station.name":"Nom du poste","station.coop":"Nom de la coopérative","network.listen":"Adresse d’écoute","network.admin_on_lan":"Administration accessible depuis le réseau","ui.language":"Langue","ui.sound":"Son","ui.idle_timeout_s":"Retour à l’accueil après (secondes)","ui.reprint_window_s":"Réimpression possible pendant (secondes)","ui.show_grid_prices":"Afficher les prix sur les tuiles","ui.show_by_unit_products":"Afficher les produits vendus à l’unité","scale.type":"Protocole de la balance","scale.present":"Ce poste a une balance","scale.manual_entry_allowed":"Saisie manuelle autorisée","scale.degrade_after_s":"Passer en mode dégradé après (secondes)","scale.options.port":"Port série","scale.options.baud":"Vitesse (bauds)","scale.options.bits":"Bits de données","scale.options.parity":"Parité","scale.options.stop":"Bits d’arrêt","scale.options.backoff_min_ms":"Attente minimale avant réessai (ms)","scale.options.backoff_max_ms":"Attente maximale avant réessai (ms)","printer.type":"Pilote d’impression","printer.template":"Gabarit d’étiquette","printer.options.transport":"Transport","printer.options.queue":"File d’impression","printer.options.path":"Fichier de périphérique","printer.options.address":"Adresse réseau","printer.options.darkness":"Noircissement","printer.options.speed":"Vitesse d’impression","printer.options.offset_x":"Décalage horizontal (dots)","printer.options.offset_y":"Décalage vertical (dots)","printer.options.invert_bits":"Inverser les points","printer.options.copies":"Exemplaires","printer.options.roll_capacity":"Étiquettes par rouleau","pricing.amount_rounding":"Arrondi du montant","pricing.unit_price_rounding":"Arrondi du prix au kilo","pricing.primary_code":"Tarif principal","pricing.reference_code":"Tarif de référence","barcode.verify_reference_check_digit":"Vérifier la clé de contrôle","limits.empty_max_g":"Plateau considéré vide en dessous de (g)","limits.basket_check_enabled":"Vérifier la présence du panier","limits.basket_min_g":"Poids du panier, borne basse (g)","limits.basket_max_g":"Poids du panier, borne haute (g)","limits.min_weight_g":"Poids minimum accepté","limits.max_weight_g":"Poids maximum accepté","limits.max_tare_g":"Tare maximale","limits.min_units":"Unités minimum","limits.max_units":"Unités maximum","limits.max_amount_cents":"Montant maximum (centimes)","stability.mode":"Exigence de stabilité","stability.min_duration_ms":"Durée de stabilité (ms)","stability.tolerance_g":"Tolérance de stabilité (g)","stability.timeout_ms":"Délai d’attente de stabilité (ms)","stability.on_timeout":"Au bout du délai","stability.min_latch_rate":"Taux d’accroche minimal","stability.latch_rate_window_ms":"Fenêtre de mesure du taux (ms)","stability.expiry_floor_ms":"Péremption, plancher (ms)","stability.expiry_ceiling_ms":"Péremption, plafond (ms)","stability.expiry_factor":"Péremption, facteur","catalog.type":"Où le poste va chercher le catalogue","catalog.options.directory":"Répertoire surveillé","catalog.options.url":"Adresse du serveur","catalog.options.username":"Compte","catalog.options.password":"Mot de passe","catalog.options.separator":"Séparateur du CSV","catalog.options.poll_interval_s":"Vérifier toutes les (secondes)","catalog.options.stable_polls":"Vérifications avant lecture","catalog.options.max_file_size_mb":"Taille maximale du fichier (Mo)","catalog.options.max_image_size_kb":"Taille maximale d’une image (Ko)","catalog.options.min_readable_ratio":"Part minimale de lignes lisibles","catalog.options.max_weighable_drop":"Baisse maximale des produits pesables","catalog.options.max_archives":"Archives conservées","catalog.options.archive_days":"Archives conservées (jours)","catalog.options.failures_before_reject":"Échecs avant mise en quarantaine","catalog.images.source":"Origine des photos","catalog.images.path":"Répertoire des photos","catalog.fallback_category":"Rayon par défaut","journal.max_rows":"Pesées conservées","journal.max_days":"Pesées conservées (jours)","journal.max_technical":"Événements techniques conservés","admin.session_minutes":"Durée d’une session (minutes)","admin.attempts_per_minute":"Tentatives par minute","maintenance.weekly_integrity_check":"Contrôle d’intégrité hebdomadaire","maintenance.disk_alert_mb":"Alerte disque en dessous de (Mo)"};function Gt(n){return cn[n]??n}const dn={station:"l’identité du poste",network:"le réseau",ui:"l’écran client",scale:"la balance",printer:"l’imprimante",pricing:"les tarifs",barcode:"le code-barres",limits:"les garde-fous",stability:"la stabilité",catalog:"le catalogue",journal:"le journal",maintenance:"la maintenance"};function vn(n){return dn[n]??n}const pn={scale:"balance",printer:"imprimante",catalog:"catalogue",ui:"écran",config:"configuration",http:"réseau",system:"système"};function tr(n){return pn[n]??"origine inconnue"}const xa="openscale.admin.preferences",ar="technical";class fn{#t=F(Lt(gn()));get showTechnicalNames(){return e(this.#t)}set showTechnicalNames(t){p(this.#t,t,!0)}toggleTechnicalNames(){this.showTechnicalNames=!this.showTechnicalNames,mn(this.showTechnicalNames)}}function gn(){try{return globalThis.localStorage?.getItem(xa)===ar}catch{return!1}}function mn(n){try{n?globalThis.localStorage?.setItem(xa,ar):globalThis.localStorage?.removeItem(xa)}catch{}}const Ht=new fn,hn=3e3,_n=["hardware","label","rules","catalog","journal","station","update"];function $a(n){return _n.includes(n)}function bn(n){return n instanceof Ft&&(n.status===401||n.status===409)}class wn{constructor(t=hn){this.periodMs=t}periodMs;#t=F(null);get health(){return e(this.#t)}set health(t){p(this.#t,t,!0)}#r=F("dashboard");get page(){return e(this.#r)}set page(t){p(this.#r,t,!0)}#a=F(!1);get expert(){return e(this.#a)}set expert(t){p(this.#a,t,!0)}#e=F("");get linkError(){return e(this.#e)}set linkError(t){p(this.#e,t,!0)}#n=F("");get actionError(){return e(this.#n)}set actionError(t){p(this.#n,t,!0)}#l=F(!1);get needsFirstPassword(){return e(this.#l)}set needsFirstPassword(t){p(this.#l,t,!0)}#d=F("");get notice(){return e(this.#d)}set notice(t){p(this.#d,t,!0)}#v=F(!1);get busy(){return e(this.#v)}set busy(t){p(this.#v,t,!0)}#p=F(Lt([]));get lastFaults(){return e(this.#p)}set lastFaults(t){p(this.#p,t,!0)}#f=F(null);get pending(){return e(this.#f)}set pending(t){p(this.#f,t,!0)}#i=null;#o=null;start(){this.refresh(),this.#i=setInterval(()=>{this.refresh()},this.periodMs)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null}async refresh(){try{this.health=await Er(),this.linkError=""}catch(t){this.linkError=Da(t)}}#u(){this.busy=!0,this.notice="",this.actionError="",this.needsFirstPassword=!1}#s(t){this.actionError=Da(t),this.needsFirstPassword=t instanceof Ft&&t.status===409,this.#m(t)}async run(t){this.#u();let s=null;try{s=await t(),this.notice=s.message}catch(c){this.#s(c)}finally{this.busy=!1}return await this.refresh(),s}async login(t){this.#u();try{return await $r(t),this.expert=!0,this.notice="Session d’administration ouverte.",!0}catch(s){return this.expert=!1,this.#s(s),!1}finally{this.busy=!1}}async recover(t,s){this.#u();try{const c=await Ir(t,s);return this.expert=!0,this.notice=c.warning??"Le mot de passe est remplacé et la session est ouverte.",!0}catch(c){return this.#s(c),!1}finally{this.busy=!1}}async protect(t){try{return await t()}catch(s){if(!bn(s))return this.#s(s),null;if(!await this.#g(s))return null;try{return await t()}catch(c){return this.#s(c),null}}}#g(t){return this.pending={kind:t.status===409?"first-password":"password",message:t.message},new Promise(s=>{this.#o=s})}async answerPassword(t){await this.login(t)&&this.#c(!0)}async answerRecovery(t,s){await this.recover(t,s)&&this.#c(!0)}cancelPassword(){this.#c(!1)}#c(t){this.pending=null,this.notice="";const s=this.#o;this.#o=null,s?.(t)}async logout(){await Dr(),this.expert=!1,this.page="dashboard",this.notice="Session d’administration fermée."}open(t){this.actionError="",this.notice="",this.needsFirstPassword=!1,this.page=t}async load(t){try{const s=await t();return this.actionError="",this.lastFaults=[],s}catch(s){return this.lastFaults=s instanceof Ft?s.faults:[],this.#s(s),null}}report(t){this.lastFaults=t instanceof Ft?t.faults:[],this.#s(t)}#m(t){t instanceof Ft&&t.needsPassword&&(this.expert=!1)}}function Da(n){return n instanceof Ft?n.message:n instanceof Error?"Le poste n’a pas répondu : "+n.message:"Le poste n’a pas répondu."}var yn=o('clé'),xn=o('');function tt(n,t){const s=vt(t,"kind",3,"read"),c=vt(t,"busy",3,!1),_=vt(t,"disabled",3,!1),w=vt(t,"protected",3,!1),T=vt(t,"act",3,void 0);var A=xn();let M;var H=a(A),J=r(H);{var fe=ge=>{var be=yn();l(ge,be)};g(J,ge=>{w()&&ge(fe)})}v(()=>{M=Zt(A,1,`act ${s()??""}`,"svelte-5tpq0o",M,{"touch-target":s()==="destructive",busy:c()}),Ue(A,"data-kind",s()),Ue(A,"data-act",T()),A.disabled=_()||c(),i(H,`${(c()?"En cours…":t.label)??""} `)}),Ze("click",A,function(...ge){t.onrun?.apply(this,ge)}),l(n,A)}At(["click"]);var kn=o(`
Le code de secours a été tiré à l’installation de ce poste et imprimé
+ sur sa fiche, rangée dans le classeur du magasin. Il n’existe nulle part ailleurs.
`,1),qn=o(`
Oublié ? Le code de secours de la fiche d’installation en repose un.
+ Un responsable peut aussi le faire en ligne de commande, avec openscale config password.
`,1),Sn=o('
'),Ln=o('
');function En(n,t){Nt(t,!0);const s=u(()=>t.admin.pending?.kind==="first-password");let c=F(""),_=F("");const w=8,T=u(()=>e(s)?e(_).trim().length>0&&e(c).length>=w:e(c).length>0);async function A(){!e(T)||t.admin.busy||(e(s)?await t.admin.answerRecovery(e(_),e(c)):await t.admin.answerPassword(e(c)),p(c,""),p(_,""))}var M=Ln(),H=a(M),J=a(H),fe=a(J),ge=a(fe);_r(ge,{name:"settings",size:"1.5rem"});var be=r(fe,2),Z=a(be),C=r(J,2),me=a(C),Q=r(C,2);{var ue=N=>{var L=kn(),q=r(Y(L),2),D=r(a(q),2);Ta(D);var X=r(q,2),he=a(X);he.textContent="Nouveau mot de passe — 8 caractères au moins";var ze=r(he,2);Ze("keydown",D,Ce=>Ce.key==="Enter"&&void A()),ca(D,()=>e(_),Ce=>p(_,Ce)),Ze("keydown",ze,Ce=>Ce.key==="Enter"&&void A()),ca(ze,()=>e(c),Ce=>p(c,Ce)),l(N,L)},re=N=>{var L=qn(),q=Y(L),D=r(a(q),2);Ta(D),Ze("keydown",D,X=>X.key==="Enter"&&void A()),ca(D,()=>e(c),X=>p(c,X)),l(N,L)};g(Q,N=>{e(s)?N(ue):N(re,-1)})}var P=r(Q,2);{var se=N=>{var L=Sn(),q=a(L);v(()=>i(q,t.admin.actionError)),l(N,L)};g(P,N=>{t.admin.actionError!==""&&N(se)})}var V=r(P,2),B=a(V);tt(B,{label:"Annuler",onrun:()=>t.admin.cancelPassword()});var ne=r(B,2);{let N=u(()=>e(s)?"Poser ce mot de passe":"Continuer"),L=u(()=>!e(T)||t.admin.busy);tt(ne,{kind:"write",get label(){return e(N)},get disabled(){return e(L)},onrun:()=>{A()}})}v(()=>{i(Z,e(s)?"Ce poste n’a pas encore de mot de passe":"Mot de passe d’administration"),i(me,t.admin.pending?.message??"")}),l(n,M),$t()}At(["keydown"]);var zn=o(''),jn=o(""),Pn=o(""),Tn=o('
'),Cn=o(""),On=o('
'),Rn=o('
');function _t(n,t){Nt(t,!0);const s=vt(t,"hint",3,""),c=vt(t,"kind",3,"text"),_=vt(t,"disabled",3,!1),w=vt(t,"fault",3,""),T=vt(t,"allowed",19,()=>[]),A=u(()=>`field-${t.path.replace(/\./gu,"-")}`);var M=Rn();let H;var J=a(M),fe=a(J),ge=a(fe),be=r(fe,2);{var Z=V=>{var B=zn(),ne=a(B);v(()=>i(ne,t.path)),l(V,B)};g(be,V=>{Ht.showTechnicalNames&&V(Z)})}var C=r(J,2),me=r(C,2);{var Q=V=>{var B=Pn();at(B,20,T,ne=>ne,(ne,N)=>{var L=jn(),q={};v(()=>{q!==(q=N)&&(L.value=(L.__value=N)??"")}),l(ne,L)}),v(()=>Ue(B,"id",e(A)+"-allowed")),l(V,B)};g(me,V=>{T().length>0&&V(Q)})}var ue=r(me,2);{var re=V=>{var B=Tn(),ne=a(B);v(()=>i(ne,s())),l(V,B)};g(ue,V=>{s()!==""&&V(re)})}var P=r(ue,2);{var se=V=>{var B=On(),ne=a(B),N=r(ne);{var L=q=>{var D=Cn(),X=a(D);v(he=>i(X,`Valeurs acceptées : ${he??""}.`),[()=>T().join(", ")]),l(q,D)};g(N,q=>{T().length>0&&q(L)})}v(()=>i(ne,`${w()??""} `)),l(V,B)};g(P,V=>{w()!==""&&V(se)})}v(()=>{H=Zt(M,1,"field svelte-1oilv5s",null,H,{refused:w()!==""}),Ue(J,"for",e(A)),i(ge,t.label),Ue(C,"id",e(A)),Ue(C,"type",c()),oa(C,t.value),C.disabled=_(),Ue(C,"list",T().length>0?e(A)+"-allowed":void 0)}),Ze("input",C,V=>t.onchange(V.currentTarget.value)),l(n,M),$t()}At(["input"]);const Ia=1e3;function pa(n){const t=Pa(n);return t===null?"":[sa(t.getDate()),sa(t.getMonth()+1),String(t.getFullYear())].join("/")}function Mt(n){const t=Pa(n);return t===null?"":`${pa(n)} à ${sa(t.getHours())}:${sa(t.getMinutes())}`}function An(n){const t=Pa(n);return t===null?"":[sa(t.getHours()),sa(t.getMinutes()),sa(t.getSeconds())].join(":")}function ka(n){const t=n/1e6;return t<1e3?`${Math.round(t)} Mo`:`${rr(t/1e3)} Go`}function _e(n){const t=String(Math.abs(Math.trunc(n))),s=[];for(let c=t.length;c>0;c-=3)s.unshift(t.slice(Math.max(0,c-3),c));return(n<0?"-":"")+s.join(" ")}function va(n){return n0&&w.push({count:Rt(n.anomalies_count),label:"anomalies",note:"à corriger dans Odoo",link:Ua(n.anomalies_count)}),n.unit_mismatches_count>0){const T=n.unit_mismatches_count;w.push({count:"+ "+Rt(T),label:T===1?"unité divergente":"unités divergentes",note:"pesable, unité à corriger",link:Ua(T)})}return{headline:`Catalogue du ${pa(n.occurred_at)} — ${Rt(s)} produits reçus`,lines:w,oneLine:Nn(n)}}function Nn(n){const t=[`${Rt(n.rows_read_count)} reçus`,`${Rt(n.weighable_count)} pesables`,`${Rt(n.not_weighable_count)} non pesables`,`${Rt(n.anomalies_count)} anomalies`],s=n.unit_mismatches_count;return s>0&&t.push(`${Rt(s)} ${s===1?"unité divergente":"unités divergentes"}`),t.join(" · ")}const $n={applied:"appliqué",unchanged:"identique au précédent",rejected:"refusé",failed:"échec"},Dn={local_drop:"dépôt local",webdav:"WebDAV",manual:"déposé sur l’écran"};function ha(n){return $n[n]??"résultat inconnu"}function sr(n){return Dn[n]??"source inconnue"}function In(n,t){const s=[n.file_name===""?"Le dernier fichier":n.file_name,ha(n.result)],c=Mt(n.occurred_at);c!==""&&s.push("le "+c),s.push("via "+sr(n.source));const _=n.reason===""?"":" "+n.reason;return`${s.join(" ")} — ${nr(n,t).oneLine}.${_}`}function Un(n){return n===""?"Aucun nouvel import enregistré à cet instant, et ce poste ne publie pas ce qu’il surveille.":"Aucun nouvel import enregistré à cet instant. Le poste surveille "+n+" : le fichier n’y était pas, ou il n’a pas encore fini d’arriver."}function Fn(n){return"Ce poste n’a pas de journal : il ne pourra rien dire de l’issue de cette relecture."+(n===""?"":" Le poste surveille "+n+".")}function Ua(n){return n===1?"voir la ligne":`voir les ${Rt(n)} lignes`}function Mn(n){return n.map(t=>`${Bn(t)} (${Rt(t.count)})`).join(", ")}function Bn(n){switch(n.code){case"PREPACKAGED_PRODUCT":return"préemballés";case"INTERNAL_CODE_NOT_WEIGHABLE":return n.value===""?"code interne":"code interne "+n.value;case"NO_BARCODE":return"sans code-barres";default:return n.code}}function Rt(n){return String(n)}var Vn=o(''),Wn=o(''),Gn=o('
Le poste y cherche le fichier , et le supprime
+ une fois lu : c’est ce qui dit au producteur que la livraison est prise.
`,1),ss=o(`
Sur un serveur WebDAV, le dépôt d’un fichier CSV depuis cet écran n’est plus
+ possible : le poste n’a plus de répertoire local où l’écrire. C’est le seul recours
+ du jour de la mise en service.
`,1),ls=o(`
Ce poste ne déclare aucune source : choisissez-en une ci-dessus, sinon il n’ira
+ chercher aucun catalogue.
`),is=o('
',1),os=o('
Aucun import enregistré sur ce poste.
'),us=o('
'),cs=o(`
« Oublier la quarantaine » fait relire un fichier que le poste avait écarté : c’est le
+ seul geste de cette page qui puisse remettre en service un catalogue refusé.
`,1),ds=o(`
Un produit masqué reste vendable : la caisse lit toujours son code-barres, et une
+ étiquette déjà imprimée reste valable. Ce réglage ne fait que retirer sa tuile.
`,1),vs=o('
'),ps=o(`
Déposez ici le fichier clé
Ce dépôt remplace toute la grille par le fichier apporté : il change ce que le poste
+ vend, et le mot de passe est donc demandé au moment du dépôt.
`,1),fs=o('
'),gs=o('
Aucune anomalie sur le dernier import.
'),ms=o('
',1),hs=o('
'),_s=o('
Aucune unité divergente sur le dernier import.
'),bs=o('
',1),ws=o('
'),ys=o('
Aucun produit non pesable sur le dernier import.
'),xs=o('
',1),ks=o('
Aucun import enregistré : rien n’a encore pu être retiré.
'),qs=o('
Aucun produit retiré par le dernier import.
'),Ss=o(`
Ils restent enregistrés avec leur historique : une étiquette déjà collée reste
+ lisible en caisse, et un produit qui revient dans un prochain fichier retrouve sa
+ tuile. Ce poste n’en publie encore que le nombre — leurs noms se lisent dans Odoo,
+ en comparant avec l’export précédent.
`,1),Ls=o('
'),Es=o(`
Un produit retiré de la grille ne se trouve plus ici : il se reprend dans
+ « Décisions en vigueur », plus bas.
`,1),zs=o(`
Sans motif, le poste refuse la décision. Le seul acte qui s’en passe est celui qui
+ EFFACE la décision en vigueur : proposer de nouveau un produit qui ne porte aucune
+ dérogation.
`),js=o(`
Produit choisi :
Ce produit est-il proposé dans la grille ?
La dérogation s’enregistre seule : elle ne remet pas dans la grille un produit
+ qui en a été retiré, et retirer un produit n’efface pas sa dérogation.
`),Ps=o(' ',1),Ts=o('
Aucune décision locale : la grille est celle du fichier.
'),Cs=o('
'),Os=o('
',1),Rs=o('
'),As=o('
Aucun import dans l’historique.
'),Ns=o('
'),$s=o('
Quand
Fichier
Source
Résultat
Motif
Lues
Pesables
Non pesables
Anomalies
Retirés
',1),Ds=o('
');function Is(n,t){Nt(t,!0);const s=(b,G=ia)=>{var oe=as(),qe=a(oe),Je=r(qe,2),Se=a(Je),Pe=a(Se),j=r(Se,2);{var U=Ke=>{var Ye=ts(),ce=a(Ye);v(()=>i(ce,G().path)),l(Ke,Ye)};g(j,Ke=>{Ht.showTechnicalNames&&Ke(U)})}var xe=r(j,2),Xe=a(xe);v((Ke,Ye)=>{Ue(oe,"data-flag",G().path),Ue(oe,"data-on",Ke),la(qe,Ye),i(Pe,G().label),i(Xe,G().hint)},[()=>String(t.draft.flag(G().path)),()=>t.draft.flag(G().path)]),Ze("change",qe,Ke=>t.draft.set(G().path,Ke.currentTarget.checked)),l(b,oe)},c=vt(t,"admin",7),_=20,w=50,T=20,A=["NO_BARCODE","PREPACKAGED_PRODUCT","INTERNAL_CODE_NOT_WEIGHABLE"],M={local_drop:["catalog.options.directory"],webdav:["catalog.options.url","catalog.options.username","catalog.options.password"]},H=u(()=>t.draft.text("catalog.type"));let J=F(Lt([])),fe=F(Lt([])),ge=F(Lt([])),be=F(""),Z=F(""),C=F(""),me=F(""),Q=F(!1),ue=F(""),re=F(""),P=F("loading"),se=F("loading");const V=u(()=>c().busy||e(re)!==""),B=u(()=>e(fe).filter(b=>b.issue==="anomaly")),ne=u(()=>e(fe).filter(b=>A.includes(b.code))),N=u(()=>e(fe).filter(b=>b.code==="UNIT_MISMATCH")),L=u(()=>new Map(e(ge).map(b=>[b.id,b.name]))),q=u(()=>e(ge).filter(b=>b.mode==="by_unit").length),D=u(()=>{if(e(se)==="loading")return"Lecture du catalogue en service…";if(e(se)==="unread")return"Le catalogue en service n’a pas pu être lu : cet écran ne sait pas combien de produits se vendent à l’unité.";if(e(q)===0)return"Aucun produit vendu à l’unité dans le catalogue en service.";const b=e(q)>1,G=b?"produits vendus à l’unité sont":"produit vendu à l’unité est",oe=t.draft.flag("ui.show_by_unit_products")?`${b?"montrés":"montré"} dans la grille de ce poste`:`${b?"masqués":"masqué"} sur ce poste`;return`${_e(e(q))} ${G} ${oe}.`}),X=u(()=>e(be)===""?[]:br(e(ge),wr,e(be))),he=u(()=>e(X).slice(0,_)),ze=u(()=>e(B).slice(0,w)),Ce=u(()=>e(N).slice(0,w)),Le=u(()=>e(ne).slice(0,w)),ee=u(()=>[...t.health.decisions].sort((b,G)=>G.decided_at.localeCompare(b.decided_at))),y=u(()=>e(ee).slice(0,T)),I=u(()=>e(Z)===""?null:e(ee).find(b=>b.product_id===e(Z))??null),W=u(()=>e(I)===null?!0:e(I).offered),te=u(()=>e(I)?.min_weight_g??null),de=u(()=>e(me).trim()===""?null:Number(e(me))),Ve=u(()=>e(de)!==null&&Number.isFinite(e(de))&&e(de)>0),S=u(()=>e(C).trim()),h=u(()=>e(S)!==""||!Ae(!1,e(te))),k=u(()=>e(S)!==""||!Ae(!0,e(te))),O=u(()=>e(Ve)&&(e(S)!==""||!Ae(e(W),e(de)))),le=u(()=>e(S)!==""||!Ae(e(W),null)),Re=u(()=>t.health.catalog?.products_withdrawn_count??0),We=u(()=>e(J).find(b=>t.health.catalog!==null&&b.id`${_e(e(Re))} ${e(Re)>1?"produits retirés":"produit retiré"}`+(e(We)===null?".":` depuis l’import du ${Mt(e(We).occurred_at)}.`)),d=u(()=>e(X).length>e(he).length?`${_e(e(he).length)} produits affichés sur ${_e(e(X).length)} trouvés — précisez votre recherche.`:`${_e(e(X).length)} ${e(X).length>1?"produits trouvés":"produit trouvé"}.`),x=u(()=>`${_e(e(J).length)} ${e(J).length>1?"imports affichés":"import affiché"} : le poste n’en publie jamais plus de vingt.`),$=u(()=>e(P)==="loading"?"Lecture des signalements du dernier import…":"Les signalements du dernier import n’ont pas pu être lus : cet écran ne sait pas ce qu’ils disent."),f=u(()=>e(P)==="loading"?"Lecture de l’historique des imports…":"L’historique des imports n’a pas pu être lu : cet écran ne sait pas ce qu’il contient.");let E;Xt(()=>{const b=t.health.catalog?.id??null;b!==E&&(E=b,ye())});const z=new ir;Xt(()=>{z.observe(t.health)});async function ye(){const b=await c().load(()=>Qr(t.health.catalog?.id));b===null?p(P,"unread"):(p(J,b.imports,!0),p(fe,b.findings,!0),p(P,"read"));const G=await c().load(()=>Ja());if(G===null){p(se,"unread");return}p(ge,G.products,!0),p(se,"read")}async function ie(b,G){p(re,b,!0),c().actionError="",c().notice="",z.forget();try{const oe=await c().protect(G);return oe===null?null:(c().notice=oe.message,await c().refresh(),oe)}finally{p(re,"")}}async function ke(){const b=await ie("reload",Zr);b!==null&&z.begin(b)}function Oe(b){t.draft.set("catalog.type",b);for(const[G,oe]of Object.entries(M))if(G!==b)for(const qe of oe)t.draft.unset(qe)}function ve(b){return t.draft.faults.find(G=>G.field===b)?.message??""}function K(b){p(Z,b,!0),p(C,"");const G=e(ee).find(oe=>oe.product_id===b)?.min_weight_g??null;p(me,G===null?"":String(G),!0)}function Ae(b,G){return!(b&&G===null)}async function Ge(b){if(e(Z)==="")return;const G=e(Z),oe=e(te),qe=e(S);await ie("offered",()=>Aa(G,{offered:b,min_weight_g:oe,reason:qe}))!==null&&p(C,"")}async function Fe(b){if(e(Z)==="")return;const G=e(Z),oe=e(W),qe=e(S);await ie(b===null?"waiver-off":"waiver",()=>Aa(G,{offered:oe,min_weight_g:b,reason:qe}))!==null&&p(C,"")}async function De(b){if(p(Q,!1),b!=null){p(ue,""),c().actionError="",c().notice="",p(re,"import");try{const G=await c().protect(()=>Ya(b));if(G===null)return;p(ue,`${b.name} : ${_e(G.rows_read_count)} lignes lues, ${_e(G.weighable_count)} pesables. `+(G.reason===""?"Le fichier est déposé ; son résultat s’inscrira dans l’historique des imports.":G.reason)),await c().refresh()}finally{p(re,"")}}}function Me(b){b.preventDefault(),p(Q,!1);const G=b.dataTransfer?.files.item(0);if(e(V)){p(ue,`${G?.name??"Ce fichier"} n’a pas été déposé : un acte est déjà en cours sur cette page. Réessayez quand il aura répondu.`);return}De(G)}function ae(b){const G=b.files?.item(0);b.value="",De(G)}function Ne(b){const G=e(L).get(b);return G!==void 0?G:e(se)==="loading"?"Lecture du nom…":e(se)==="unread"?"Nom inconnu : le catalogue n’a pas pu être lu":"Produit absent du catalogue en service"}function pe(b,G,oe,qe){const Je=G>1?qe:oe;return b>=G?`${_e(G)} ${Je}.`:`${_e(b)} lignes affichées sur ${_e(G)} ${Je}.`}function je(b){const G=[];return b.offered||G.push("retiré de la grille"),b.min_weight_g!==null&&G.push(`peut peser à partir de ${_e(b.min_weight_g)} g`),G.length===0?"aucune restriction":G.join(" · ")}function $e(b){const G=ha(b.result);return b.code===""?G:`${G} (${b.code})`}function st(b){return sr(b.source)}var it=Ds(),pt=a(it);Qe(pt,{title:"Où le poste va chercher le catalogue",children:(b,G)=>{var oe=is(),qe=Y(oe),Je=a(qe),Se=a(Je),Pe=r(Je,2),j=a(Pe),U=r(qe,2);{var xe=ce=>{var Ie=rs();l(ce,Ie)},Xe=ce=>{var Ie=ns(),Be=Y(Ie);{let ut=u(()=>Gt("catalog.options.directory")),ht=u(()=>t.draft.text("catalog.options.directory")),kt=u(()=>ve("catalog.options.directory"));_t(Be,{get label(){return e(ut)},path:"catalog.options.directory",get value(){return e(ht)},hint:"Laissez vide pour le répertoire du poste, celui que le service crée lui-même. Un répertoire nommé ici doit exister : le poste ne le crée pas.",get fault(){return e(kt)},onchange:zt=>t.draft.set("catalog.options.directory",zt)})}var ct=r(Be,2),dt=r(a(ct)),ot=a(dt);v(()=>i(ot,`flv_${t.health.station??""}.csv`)),l(ce,Ie)},Ke=ce=>{var Ie=ss(),Be=Y(Ie);{let ot=u(()=>Gt("catalog.options.url")),ut=u(()=>t.draft.text("catalog.options.url")),ht=u(()=>ve("catalog.options.url"));_t(Be,{get label(){return e(ot)},path:"catalog.options.url",get value(){return e(ut)},get fault(){return e(ht)},onchange:kt=>t.draft.set("catalog.options.url",kt)})}var ct=r(Be,2);{let ot=u(()=>Gt("catalog.options.username")),ut=u(()=>t.draft.text("catalog.options.username")),ht=u(()=>ve("catalog.options.username"));_t(ct,{get label(){return e(ot)},path:"catalog.options.username",get value(){return e(ut)},get fault(){return e(ht)},onchange:kt=>t.draft.set("catalog.options.username",kt)})}var dt=r(ct,2);{let ot=u(()=>Gt("catalog.options.password")),ut=u(()=>ve("catalog.options.password"));_t(dt,{get label(){return e(ot)},path:"catalog.options.password",kind:"password",value:"",hint:"Laissez vide : le mot de passe actuel est conservé.",get fault(){return e(ut)},onchange:ht=>t.draft.set("catalog.options.password",ht)})}l(ce,Ie)},Ye=ce=>{var Ie=ls();l(ce,Ie)};g(U,ce=>{t.draft.config===null?ce(xe):e(H)==="local_drop"?ce(Xe,1):e(H)==="webdav"?ce(Ke,2):ce(Ye,-1)})}v(()=>{la(Se,e(H)==="local_drop"),la(j,e(H)==="webdav")}),Ze("change",Se,()=>Oe("local_drop")),Ze("change",j,()=>Oe("webdav")),l(b,oe)},$$slots:{default:!0}});var Ee=r(pt,2);Qe(Ee,{title:"Dernier import",children:(b,G)=>{var oe=cs(),qe=Y(oe);{var Je=ce=>{var Ie=os();l(ce,Ie)},Se=ce=>{lr(ce,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives}})};g(qe,ce=>{t.health.catalog===null?ce(Je):ce(Se,-1)})}var Pe=r(qe,2),j=a(Pe),U=r(Pe,2);{var xe=ce=>{var Ie=us(),Be=a(Ie);v(()=>i(Be,z.sentence)),l(ce,Ie)};g(U,ce=>{z.sentence!==""&&ce(xe)})}var Xe=r(U,2),Ke=a(Xe);{let ce=u(()=>e(re)==="reload");tt(Ke,{act:"reload",kind:"write",label:"Recharger le catalogue",protected:!0,get busy(){return e(ce)},get disabled(){return e(V)},onrun:()=>{ke()}})}var Ye=r(Ke,2);{let ce=u(()=>e(re)==="quarantine");tt(Ye,{act:"quarantine",kind:"destructive",label:"Oublier la quarantaine",protected:!0,get busy(){return e(ce)},get disabled(){return e(V)},onrun:()=>{ie("quarantine",en)}})}v(()=>i(j,t.health.catalog_source===null?"Aucune source de catalogue publiée par ce poste.":"Source : "+t.health.catalog_source.label)),l(b,oe)},$$slots:{default:!0}});var nt=r(Ee,2);Qe(nt,{title:"Ce que la grille montre",note:"Un réglage d’affichage : il ne change ni le fichier reçu, ni ce que le poste sait peser.",children:(b,G)=>{var oe=ds(),qe=Y(oe);s(qe,()=>({path:"ui.show_by_unit_products",label:"Afficher les produits vendus à l’unité",hint:"Décoché, leurs tuiles quittent la grille et la recherche ne les retrouve plus. Ce que le poste perd : une tuile vendue à l’unité imprime une étiquette sans jamais lire la balance, et c’est le seul geste que ce réglage retire."}));var Je=r(qe,2),Se=a(Je);v(()=>i(Se,e(D))),l(b,oe)},$$slots:{default:!0}});var et=r(nt,2);Qe(et,{title:"Déposer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(b,G)=>{var oe=ps(),qe=Y(oe);{var Je=ce=>{var Ie=vs(),Be=a(Ie);v(()=>i(Be,e(ue))),l(ce,Ie)};g(qe,ce=>{e(ue)!==""&&ce(Je)})}var Se=r(qe,2);let Pe;var j=a(Se),U=r(a(j)),xe=a(U),Xe=r(j,2),Ke=a(Xe),Ye=r(Ke);v(()=>{Pe=Zt(Se,1,"drop svelte-3gk7u7",null,Pe,{dropping:e(Q),working:e(re)==="import"}),i(xe,`flv_${t.health.station??""}.csv`),i(Ke,`${e(re)==="import"?"Import en cours…":"Choisir un fichier"} `),Ye.disabled=e(V)}),na("dragover",Se,ce=>{ce.preventDefault(),!e(V)&&p(Q,!0)}),na("dragleave",Se,()=>p(Q,!1)),na("drop",Se,Me),Ze("change",Ye,ce=>ae(ce.currentTarget)),l(b,oe)},$$slots:{default:!0}});var mt=r(et,2);Qe(mt,{title:"Anomalies à corriger dans Odoo",note:"Chaque ligne porte le nom du produit, son numéro dans le CSV, son motif et la valeur fautive.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=fs(),xe=a(U);v(()=>i(xe,e($))),l(j,U)},Se=j=>{var U=gs();l(j,U)},Pe=j=>{var U=ms(),xe=Y(U),Xe=a(xe),Ke=r(Xe);{var Ye=Ie=>{var Be=xt("Corrigez celles-ci dans Odoo : l’import suivant ne signalera que ce qui reste.");l(Ie,Be)};g(Ke,Ie=>{e(B).length>e(ze).length&&Ie(Ye)})}var ce=r(xe,2);ya(ce,()=>e(ze),()=>"anomalies"),v(Ie=>i(Xe,`${Ie??""} `),[()=>pe(e(ze).length,e(B).length,"anomalie","anomalies")]),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(B).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Dt=r(mt,2);Qe(Dt,{title:"Unités divergentes",note:"Le produit reste proposé : le code-barres fait foi, seul le libellé du prix est faux.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=hs(),xe=a(U);v(()=>i(xe,e($))),l(j,U)},Se=j=>{var U=_s();l(j,U)},Pe=j=>{var U=bs(),xe=Y(U),Xe=a(xe),Ke=r(xe,2);ya(Ke,()=>e(Ce),()=>"mismatches"),v(Ye=>i(Xe,Ye),[()=>pe(e(Ce).length,e(N).length,"unité divergente","unités divergentes")]),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(N).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Bt=r(Dt,2);Qe(Bt,{title:"Produits non pesables",note:"Un inventaire, pas une liste d’erreurs : ces produits portent déjà leur code-barres et n’ont aucune raison d’être pesés.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=ws(),xe=a(U);v(()=>i(xe,e($))),l(j,U)},Se=j=>{var U=ys();l(j,U)},Pe=j=>{var U=xs(),xe=Y(U),Xe=a(xe),Ke=r(xe,2);ya(Ke,()=>e(Le),()=>"not-weighable"),v(Ye=>i(Xe,Ye),[()=>pe(e(Le).length,e(ne).length,"produit non pesable","produits non pesables")]),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(ne).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Vt=r(Bt,2);Qe(Vt,{title:"Produits retirés depuis l’import précédent",note:"Un produit absent du nouveau fichier est marqué retiré à sa date, jamais supprimé.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=ks();l(j,U)},Se=j=>{var U=qs();l(j,U)},Pe=j=>{var U=Ss(),xe=Y(U),Xe=a(xe);v(()=>i(Xe,e(we))),l(j,U)};g(qe,j=>{t.health.catalog===null?j(Je):e(Re)===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}});var Et=r(Vt,2);Qe(Et,{title:"Décider d’un produit",note:"Retirer un produit et l’autoriser à peser moins sont deux décisions séparées : l’une n’efface pas l’autre.",children:(b,G)=>{var oe=Ps(),qe=r(Y(oe),2),Je=r(qe,2);{var Se=U=>{var xe=Es(),Xe=Y(xe),Ke=a(Xe),Ye=r(Xe,2),ce=a(Ye);at(ce,21,()=>e(he),Ie=>Ie.id,(Ie,Be)=>{var ct=Ls(),dt=a(ct),ot=a(dt),ut=r(dt,2),ht=a(ut),kt=r(ut,2),zt=a(kt);v(()=>{i(ot,e(Be).name),i(ht,e(Be).id),i(zt,`${e(Be).unit_price_text??""}${e(Be).price_suffix??""}`)}),Ze("click",dt,()=>K(e(Be).id)),l(Ie,ct)}),v(()=>i(Ke,e(d))),l(U,xe)};g(Je,U=>{e(be)!==""&&U(Se)})}var Pe=r(Je,2);{var j=U=>{var xe=js(),Xe=a(xe),Ke=r(a(Xe)),Ye=a(Ke),ce=r(Ke),Ie=r(Xe,2),Be=a(Ie),ct=r(Ie,4),dt=r(ct,2);{var ot=rt=>{var yt=zs();l(rt,yt)};g(dt,rt=>{e(S)===""&&rt(ot)})}var ut=r(dt,2),ht=r(a(ut),2),kt=a(ht);{var zt=rt=>{{let yt=u(()=>e(re)==="offered"),Yt=u(()=>e(V)||!e(h));tt(rt,{act:"offered",kind:"destructive",label:"Ne plus proposer ce produit",protected:!0,get busy(){return e(yt)},get disabled(){return e(Yt)},onrun:()=>{Ge(!1)}})}},qt=rt=>{{let yt=u(()=>e(re)==="offered"),Yt=u(()=>e(V)||!e(k));tt(rt,{act:"offered",kind:"write",label:"Le proposer de nouveau",protected:!0,get busy(){return e(yt)},get disabled(){return e(Yt)},onrun:()=>{Ge(!0)}})}};g(kt,rt=>{e(W)?rt(zt):rt(qt,-1)})}var jt=r(ut,2),Ct=r(a(jt),2),It=r(Ct,2),Wt=a(It);{let rt=u(()=>e(re)==="waiver"),yt=u(()=>e(V)||!e(O));tt(Wt,{act:"waiver",kind:"write",label:"Enregistrer la dérogation",protected:!0,get busy(){return e(rt)},get disabled(){return e(yt)},onrun:()=>{Fe(e(de))}})}var Jt=r(Wt,2);{var ta=rt=>{{let yt=u(()=>e(re)==="waiver-off"),Yt=u(()=>e(V)||!e(le));tt(rt,{act:"waiver-off",kind:"write",label:"Retirer la dérogation",protected:!0,get busy(){return e(yt)},get disabled(){return e(Yt)},onrun:()=>{Fe(null)}})}};g(Jt,rt=>{e(te)!==null&&rt(ta)})}v((rt,yt)=>{Ue(xe,"data-decision",e(Z)),i(Ye,rt),i(ce,` (${e(Z)??""})`),i(Be,`En vigueur : ${yt??""}`),oa(Ct,e(me))},[()=>Ne(e(Z)),()=>e(I)===null?"aucune décision — ce produit suit les règles générales":je(e(I))]),ca(ct,()=>e(C),rt=>p(C,rt)),Ze("input",Ct,rt=>p(me,rt.currentTarget.value,!0)),l(U,xe)};g(Pe,U=>{e(Z)!==""&&U(j)})}ca(qe,()=>e(be),U=>p(be,U)),l(b,oe)},$$slots:{default:!0}});var bt=r(Et,2);Qe(bt,{title:"Décisions en vigueur",note:"Ce qu’un humain a décidé, produit par produit. C’est ici que se reprend un produit retiré de la grille.",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=Pe=>{var j=Ts();l(Pe,j)},Se=Pe=>{var j=Os(),U=Y(j),xe=a(U),Xe=r(U,2),Ke=a(Xe);at(Ke,21,()=>e(y),Ye=>Ye.product_id,(Ye,ce)=>{var Ie=Cs(),Be=a(Ie),ct=r(Be,2),dt=a(ct),ot=r(ct,2),ut=a(ot),ht=r(ot,2),kt=a(ht),zt=r(ht,2),qt=a(zt),jt=r(zt,2),Ct=a(jt);v((It,Wt,Jt)=>{i(dt,It),i(ut,e(ce).product_id),i(kt,Wt),i(qt,e(ce).reason),i(Ct,Jt)},[()=>Ne(e(ce).product_id),()=>je(e(ce)),()=>pa(e(ce).decided_at)]),Ze("click",Be,()=>K(e(ce).product_id)),l(Ye,Ie)}),v(Ye=>i(xe,Ye),[()=>pe(e(y).length,e(ee).length,"décision en vigueur","décisions en vigueur")]),l(Pe,j)};g(qe,Pe=>{e(ee).length===0?Pe(Je):Pe(Se,-1)})}l(b,oe)},$$slots:{default:!0}});var Tt=r(bt,2);Qe(Tt,{title:"Vingt derniers imports",children:(b,G)=>{var oe=Ut(),qe=Y(oe);{var Je=j=>{var U=Rs(),xe=a(U);v(()=>i(xe,e(f))),l(j,U)},Se=j=>{var U=As();l(j,U)},Pe=j=>{var U=$s(),xe=Y(U),Xe=a(xe),Ke=r(xe,2),Ye=a(Ke),ce=r(a(Ye));at(ce,21,()=>e(J),Ie=>Ie.id,(Ie,Be)=>{var ct=Ns(),dt=a(ct),ot=a(dt),ut=r(dt),ht=a(ut),kt=r(ut),zt=a(kt),qt=r(kt),jt=a(qt),Ct=r(qt),It=a(Ct),Wt=r(Ct),Jt=a(Wt),ta=r(Wt),rt=a(ta),yt=r(ta),Yt=a(yt),m=r(yt),R=a(m),Te=r(m),He=a(Te);v((lt,ft,wt,Pt,Ot,St,Kt,Qt)=>{i(ot,lt),i(ht,e(Be).file_name),i(zt,ft),i(jt,wt),i(It,e(Be).reason),i(Jt,Pt),i(rt,Ot),i(Yt,St),i(R,Kt),i(He,Qt)},[()=>Mt(e(Be).occurred_at),()=>st(e(Be)),()=>$e(e(Be)),()=>_e(e(Be).rows_read_count),()=>_e(e(Be).weighable_count),()=>_e(e(Be).not_weighable_count),()=>_e(e(Be).anomalies_count),()=>_e(e(Be).products_withdrawn_count)]),l(Ie,ct)}),v(()=>i(Xe,e(x))),l(j,U)};g(qe,j=>{e(P)!=="read"?j(Je):e(J).length===0?j(Se,1):j(Pe,-1)})}l(b,oe)},$$slots:{default:!0}}),l(n,it),$t()}At(["change","click","input"]);function Us(n){switch(n){case"ok":return"OK";case"warn":return"À SURVEILLER";case"fault":return"EN PANNE";case"off":return"SANS OBJET";default:return"INCONNU"}}var Fs=o('
'),Ms=o('
');function Bs(n,t){Nt(t,!0);var s=Ms(),c=a(s),_=a(c),w=r(_,2),T=a(w),A=r(w,2),M=a(A),H=r(c,2),J=a(H),fe=r(H,2);{var ge=be=>{var Z=Fs(),C=a(Z);v(()=>i(C,t.light.remedy)),l(be,Z)};g(fe,be=>{t.light.remedy!==""&&be(ge)})}v(be=>{Ue(s,"data-light",t.light.id),Ue(s,"data-level",t.light.level),Ue(_,"data-level",t.light.level),i(T,t.light.label),i(M,be),i(J,t.light.value)},[()=>Us(t.light.level)]),l(n,s),$t()}function Vs(n){return[Ws(n),Gs(n),Js(n),Ks(n),Ys(n),Qs(n)]}function Ws(n){const t=n.state.scale,s=va(t.median_ms);return n.scale_present?t.connected?t.too_slow?{id:"scale",label:"Balance",level:"warn",value:`une mesure toutes les ${s}, plus lent que la péremption`,remedy:"À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante. Vérifiez le câble et l’adaptateur USB, puis la cadence sur la page Matériel."}:{id:"scale",label:"Balance",level:"ok",value:Zs(t.provisional,`une mesure toutes les ${s}`),remedy:""}:{id:"scale",label:"Balance",level:"fault",value:"elle ne répond plus",remedy:"Vérifiez le câble et l’alimentation de la balance, puis touchez « Tester la balance ». En attendant, « Basculer en saisie manuelle » permet de continuer à servir."}:{id:"scale",label:"Balance",level:"off",value:"ce poste est déclaré sans balance",remedy:""}}function Gs(n){const t=n.state.printer;switch(t.health){case"faulted":return{id:"printer",label:"Imprimante",level:"fault",value:t.detail===""?"elle ne peut pas imprimer":t.detail,remedy:Hs(n)};case"consumable":return{id:"printer",label:"Imprimante",level:"warn",value:"elle imprime, mais le rouleau arrive en fin de vie",remedy:"Changez le rouleau quand vous passez derrière le comptoir, puis touchez « J’ai changé le rouleau ». Le poste continue de servir en attendant."};case"ready":return{id:"printer",label:"Imprimante",level:"ok",value:"elle répond et n’a rien à signaler",remedy:""};default:return{id:"printer",label:"Imprimante",level:"unknown",value:"elle prend les étiquettes et ne dit rien en retour",remedy:"C’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne. Pour savoir si elle imprime, touchez « Imprimer une étiquette de test »."}}}function Hs(n){const t="Regardez le capot, le rouleau et le câble, puis touchez « Tester l’imprimante ».";return n.printing?.fallback_available===!0?t+" Le poste peut servir en attendant : « Imprimer sur l’imprimante du poste voisin ».":t}function Js(n){const t=n.roll;return t===null?{id:"roll",label:"Rouleau",level:"unknown",value:"aucun compteur d’étiquettes sur ce poste",remedy:`Sans imprimante construite, il n’y a pas de rouleau à compter : vérifiez le « ${Gt("printer.type")} » et ses réglages sur la page Matériel.`}:t.known?t.level==="warn"?{id:"roll",label:"Rouleau",level:"warn",value:t.message,remedy:"Changez le rouleau, puis touchez « J’ai changé le rouleau »."}:{id:"roll",label:"Rouleau",level:"ok",value:t.message,remedy:""}:{id:"roll",label:"Rouleau",level:"unknown",value:t.message,remedy:"Touchez « J’ai changé le rouleau » en mettant un rouleau neuf : c’est le seul geste qui dise quelque chose de vrai du papier."}}function Ks(n){const t=n.catalog_source?.label??"";if(n.state.catalog_count===0)return{id:"catalog",label:"Catalogue",level:"fault",value:"aucun produit dans la grille",remedy:Xs(t)};const s=n.catalog;return s!==null&&s.result!=="applied"&&s.result!=="unchanged"?{id:"catalog",label:"Catalogue",level:"warn",value:`dernier fichier refusé : ${s.reason===""?s.result:s.reason}`,remedy:"La grille tourne toujours sur le catalogue précédent. Corrigez le fichier dans Odoo, ou touchez « Oublier la quarantaine » puis redéposez-le."}:{id:"catalog",label:"Catalogue",level:"ok",value:`${_e(n.state.catalog_count)} produits dans la grille`,remedy:""}}function Xs(n){return"Déposez le fichier du catalogue là où le poste le guette, ou utilisez « Importer un catalogue » ci-contre pour le glisser depuis une clé USB."+(n===""?"":` Ce poste surveille : ${n}.`)}function Ys(n){const t=n.disk;if(t===null)return{id:"disk",label:"Disque",level:"unknown",value:"la place libre n’a pas pu être mesurée",remedy:"Téléchargez le fichier de diagnostic : il porte ce que le poste a pu lire du volume, et c’est la pièce qu’un support demandera."};const s=ka(t.free_bytes),c=`seuil ${_e(t.alert_mb)} Mo`;return t.free_bytes===0?{id:"disk",label:"Disque",level:"fault",value:`plus un octet libre sur ${t.path}`,remedy:"Le journal ne peut plus rien écrire, alors que les étiquettes continuent de sortir. Faites de la place sur le disque, puis rechargez cette page."}:t.alert_mb>0&&t.free_bytes0?{id:"journal",label:"Journal",level:"fault",value:`${_e(t)} pesées imprimées mais non enregistrées`,remedy:"Les étiquettes sortent, les ventes sont bonnes, c’est la trace qui manque. Téléchargez le fichier de diagnostic et prévenez le support : ne redémarrez rien."}:n.counters.journal_rows_count<0?{id:"journal",label:"Journal",level:"unknown",value:"ce poste n’a pas de journal ouvert",remedy:"Le poste pèse et imprime quand même. Téléchargez le fichier de diagnostic : il dit pourquoi la base n’a pas pu être ouverte."}:{id:"journal",label:"Journal",level:"ok",value:`${_e(n.counters.journal_rows_count)} pesées enregistrées`,remedy:""}}function Zs(n,t){return n?t+" (cadence encore provisoire)":t}function el(n){return n===null||!n.known?"unknown":n.configured?"ok":"missing"}var tl=o("Une mesure toutes les ",1),al=o(''),rl=o("Source : ",1),nl=o(`
Aucun import enregistré sur ce poste : le catalogue n’est jamais arrivé, ou le
+ journal ne le porte pas.
`),sl=o('
',1),ll=o(' ',1),il=o('
Aucune décision locale : le catalogue est proposé tel qu’il arrive.
'),ol=o('
'),ul=o(''),cl=o('
'),dl=o('
',1),vl=o(`INCONNU — la question n’a pas pu être posée à ce système. Ce
+ n’est pas « non configuré » : personne ne sait encore.`,1),pl=o('OK ',1),fl=o('NON CONFIGURÉ ',1),gl=o('
'),ml=o(' ',1),hl=o('
Rien à signaler depuis le démarrage du poste.
'),_l=o(''),bl=o('
'),wl=o('
'),yl=o(''),xl=o('
'),kl=o('
Numéro de poste
Nom
Coopérative
Version
Empreinte de configuration
',1),ql=o('
');function Sl(n,t){Nt(t,!0);const s=u(()=>t.health.new_version),c=u(()=>Vs(t.health)),_=u(()=>t.health.state.scale),w=u(()=>t.health.unattended_restart),T=u(()=>t.health.decisions),A=5,M=u(()=>e(T).slice(0,A));var H=ql(),J=a(H);at(J,21,()=>e(c),Q=>Q.id,(Q,ue)=>{Bs(Q,{get light(){return e(ue)}})});var fe=r(J,2);Qe(fe,{title:"Cadence de la balance",children:(Q,ue)=>{var re=al(),P=a(re);{var se=N=>{var L=xt("Ce poste est déclaré sans balance : le poids est saisi à la main.");l(N,L)},V=N=>{var L=xt(`La balance ne répond pas : aucune cadence n’est mesurable tant qu’aucune trame
+ n’arrive.`);l(N,L)},B=N=>{var L=xt(`Aucun intervalle n’a encore été mesuré : la cadence apparaîtra dès les premières
+ trames.`);l(N,L)},ne=N=>{var L=tl(),q=r(Y(L)),D=a(q),X=r(q),he=r(X);{var ze=ee=>{var y=xt("C’est encore une valeur d’attente : moins de huit intervalles ont été observés.");l(ee,y)};g(he,ee=>{e(_).provisional&&ee(ze)})}var Ce=r(he,2);{var Le=ee=>{var y=xt("À cette cadence, un poids serait périmé avant l’arrivée de la mesure suivante.");l(ee,y)};g(Ce,ee=>{e(_).too_slow&&ee(Le)})}v((ee,y)=>{i(D,ee),i(X,`, médiane
+ observée sur ${y??""} intervalles. `)},[()=>va(e(_).median_ms),()=>_e(e(_).observations_count)]),l(N,L)};g(P,N=>{t.health.scale_present?e(_).connected?e(_).observations_count===0?N(B,2):N(ne,-1):N(V,1):N(se)})}l(Q,re)},$$slots:{default:!0}});var ge=r(fe,2);Qe(ge,{title:"Catalogue",children:(Q,ue)=>{var re=ll(),P=Y(re),se=a(P);{var V=q=>{var D=xt("Aucune source de catalogue n’est publiée par ce poste.");l(q,D)},B=q=>{var D=rl(),X=r(Y(D)),he=a(X);v(()=>i(he,t.health.catalog_source.label)),l(q,D)};g(se,q=>{t.health.catalog_source===null?q(V):q(B,-1)})}var ne=r(P,2);{var N=q=>{var D=nl();l(q,D)},L=q=>{var D=sl(),X=Y(D),he=a(X),ze=r(X,2);lr(ze,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives},get onshowrows(){return t.onshowrows}}),v((Ce,Le)=>i(he,`Dernier essai : ${Ce??""} —
+ ${Le??""}${t.health.catalog.file_name===""?"":" ("+t.health.catalog.file_name+")"}${t.health.catalog.reason===""?"":" : "+t.health.catalog.reason}`),[()=>Mt(t.health.catalog.occurred_at),()=>ha(t.health.catalog.result)]),l(q,D)};g(ne,q=>{t.health.catalog===null?q(N):q(L,-1)})}l(Q,re)},$$slots:{default:!0}});var be=r(ge,2);Qe(be,{title:"Décisions locales en vigueur",note:"Ce qu’un humain a décidé de ce catalogue, avec son motif et sa date.",children:(Q,ue)=>{var re=Ut(),P=Y(re);{var se=B=>{var ne=il();l(B,ne)},V=B=>{var ne=dl(),N=Y(ne);{var L=D=>{var X=ol(),he=a(X);v(ze=>i(he,`${ze??""} décisions en vigueur — les
+ 5 plus récentes ci-dessous. La liste entière est sur la page
+ Catalogue.`),[()=>_e(e(T).length)]),l(D,X)};g(N,D=>{e(T).length>A&&D(L)})}var q=r(N,2);at(q,21,()=>e(M),D=>D.product_id,(D,X)=>{var he=cl(),ze=a(he),Ce=a(ze),Le=r(ze,2);{var ee=de=>{var Ve=ul(),S=a(Ve);v(h=>i(S,`peut peser à partir de ${h??""} g`),[()=>_e(e(X).min_weight_g)]),l(de,Ve)};g(Le,de=>{e(X).min_weight_g!==null&&de(ee)})}var y=r(Le,2),I=a(y),W=r(y,2),te=a(W);v(de=>{i(Ce,`${e(X).offered?"Dérogation de poids":"Produit retiré"} — ${e(X).product_id??""}`),i(I,e(X).reason),i(te,`${de??""}, ${e(X).decided_by??""}`)},[()=>pa(e(X).decided_at)]),l(D,he)}),l(B,ne)};g(P,B=>{e(T).length===0?B(se):B(V,-1)})}l(Q,re)},$$slots:{default:!0}});var Z=r(be,2);Qe(Z,{title:"Redémarrage sans intervention",children:(Q,ue)=>{var re=ml(),P=Y(re),se=a(P);{var V=q=>{var D=vl();l(q,D)},B=q=>{var D=pl(),X=r(Y(D));v(()=>i(X,` — après une coupure de courant, ce poste revient seul sur
+ l’écran client. ${e(w).detail??""}`)),l(q,D)},ne=q=>{var D=fl(),X=r(Y(D));v(()=>i(X,` — ${e(w).detail??""}`)),l(q,D)};g(se,q=>{e(w)===null||!e(w).known?q(V):e(w).configured?q(B,1):q(ne,-1)})}var N=r(P,2);{var L=q=>{var D=gl(),X=a(D);v(()=>i(X,e(w).remedy)),l(q,D)};g(N,q=>{e(w)!==null&&e(w).known&&!e(w).configured&&e(w).remedy!==""&&q(L)})}v(q=>Ue(P,"data-verdict",q),[()=>el(e(w))]),l(Q,re)},$$slots:{default:!0}});var C=r(Z,2);Qe(C,{title:"Dix derniers événements",children:(Q,ue)=>{var re=Ut(),P=Y(re);{var se=B=>{var ne=hl();l(B,ne)},V=B=>{var ne=wl();at(ne,21,()=>t.health.events,N=>N.id,(N,L)=>{var q=bl(),D=a(q),X=a(D),he=r(D,2),ze=a(he),Ce=r(he,2);{var Le=I=>{var W=_l(),te=a(W);v(()=>i(te,e(L).code)),l(I,W)};g(Ce,I=>{e(L).code!==""&&Ht.showTechnicalNames&&I(Le)})}var ee=r(Ce,2),y=a(ee);v((I,W)=>{Ue(q,"data-level",e(L).level),i(X,I),i(ze,W),i(y,e(L).message)},[()=>An(e(L).occurred_at),()=>tr(e(L).source)]),l(N,q)}),l(B,ne)};g(P,B=>{t.health.events.length===0?B(se):B(V,-1)})}l(Q,re)},$$slots:{default:!0}});var me=r(C,2);Qe(me,{title:"Ce poste",children:(Q,ue)=>{var re=kl(),P=Y(re),se=r(a(P),2),V=a(se),B=r(se,4),ne=a(B),N=r(B,4),L=a(N),q=r(N,4),D=a(q),X=r(q,4),he=a(X),ze=r(P,2);{var Ce=Le=>{var ee=xl(),y=a(ee),I=r(y);{var W=te=>{var de=yl();Ze("click",de,function(...Ve){t.onshowupdate?.apply(this,Ve)}),l(te,de)};g(I,te=>{t.onshowupdate!==void 0&&te(W)})}v(()=>{Ue(ee,"data-update-available",e(s)),i(y,`Version ${e(s)??""} disponible. `)}),l(Le,ee)};g(ze,Le=>{e(s)!==""&&Le(Ce)})}v(()=>{i(V,t.health.station),i(ne,t.health.station_name===""?"non renseigné":t.health.station_name),i(L,t.health.coop),i(D,t.health.version),i(he,t.health.config_fingerprint)}),l(Q,re)},$$slots:{default:!0}}),l(n,H),$t()}At(["click"]);var Ll=o(''),El=o(`
Lecture de la configuration en cours… tant qu’elle n’est pas arrivée, cette page ne
+ déclare rien de ce poste.
`),Fa=o('
'),zl=o('
'),jl=o('
',1),Pl=o('
'),Tl=o('
',1),Cl=o('
'),Ol=o('
'),Rl=o(''),Al=o('
',1),Nl=o('
Balance
L’état et la cadence viennent de ce que le poste observe vraiment, jamais d’un réglage.
Réglages série de la balance
Imprimante
Réglages de l’imprimante
');function $l(n,t){Nt(t,!0);const s=vt(t,"admin",7),c=20,_=3,w=250,T=50,A=_*1e3+w+1e3,M=12;let H=F(Lt([])),J=F(Lt([])),fe=F(Lt([])),ge=F(Lt([])),be=F(""),Z=F(!1),C=F(""),me=F(0),Q=F(0),ue=F(!1),re=F(null),P=F(!1),se=F(!1),V=!1;const B=u(()=>t.health.state.scale),ne=u(()=>t.health.state.printer),N=u(()=>t.draft.config!==null),L=u(()=>e(N)&&!t.draft.flag("scale.present")),q=u(()=>t.draft.text("scale.options.port")),D=u(()=>e(re)!==null&&e(re).port===e(q)?e(re).reason:""),X=u(()=>e(be)===e(q)?e(ge):[]),he=u($),ze=u(E),Ce=u(ie),Le=u(ve),ee=u(()=>Me("scale.type")!==""||Me("scale.options.port")!==""),y=u(()=>Me("printer.type")!==""||Me("printer.options.transport")!==""||Me("printer.options.queue")!=="");Xt(()=>{e(ee)&&p(P,!0)}),Xt(()=>{e(y)&&p(se,!0)}),Lr(()=>{V=!0}),Ea(()=>{k("ports",O)}),Xt(()=>{e(ue)||!I(e(q))||te(e(q))});function I(m){return!V&&e(N)&&!e(L)&&m!==""&&m===e(q)&&W(m)&&e(D)===""&&e(C)===""}function W(m){return e(H).some(R=>R.name===m)}async function te(m){p(ue,!0);try{for(;I(m);){try{Ve(m,await Ra(m,_))}catch(R){p(re,{port:m,reason:Ne(R)},!0);return}await S()}}finally{p(ue,!1)}}async function de(){const m=e(q),R=await s().protect(()=>Ra(m,_));R!==null&&(Ve(m,R),p(re,null))}function Ve(m,R){if(V||R.length===0)return;const Te=m===e(be)?e(ge):[];p(be,m,!0),p(ge,[...Te,...R].slice(-c),!0)}function S(m=w){return new Promise(R=>setTimeout(R,m))}async function h(){const m=Date.now()+A;for(;e(ue)&&Date.now()Qa(m));R!==null&&(s().notice=R.message,await s().refresh())}async function we(){p(me,0),p(Q,0),e(Z)||await O();const m=e(H);if(m.length===0){p(fe,[],!0);return}await s().protect(()=>d(m))}async function d(m){p(fe,[],!0),p(Q,m.length,!0);for(const[R,Te]of m.entries()){p(me,R+1);try{const He=await Hr(Te.name);p(fe,[...e(fe),{port:Te.name,message:He.message,refused:!1}],!0)}catch(He){if(pe(He))throw He;p(fe,[...e(fe),{port:Te.name,message:Ne(He),refused:!0}],!0)}}}function x(){return e(C)!=="detect"?"Détecter automatiquement":e(ue)?"Détection : le port se libère…":e(Q)===0?"Détection : énumération des ports…":`Détection : port ${_e(e(me))} sur ${_e(e(Q))}…`}function $(){return t.health.scale_present?e(B).connected?e(B).too_slow?{level:"warn",word:"Trop lente",detail:f()+" À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante."}:{level:"ok",word:"Connectée",detail:"Elle répond. "+f()}:{level:"fault",word:"Sans réponse",detail:"Elle ne répond plus. Vérifiez le câble et l’alimentation, puis « Tester la balance » sur la page Dépannage."}:{level:"off",word:"Sans balance",detail:"Ce poste est déclaré sans balance : le feu est éteint et le poids se saisit à la main."}}function f(){return e(B).observations_count===0?"Aucun intervalle n’a encore été mesuré : la cadence sera connue dès les premières trames.":`Une mesure toutes les ${va(e(B).median_ms)} sur ${_e(e(B).observations_count)} intervalles`+(e(B).provisional?", cadence encore provisoire.":".")}function E(){const m=z[e(ne).health]??{level:"unknown",word:"État inconnu",detail:"Le poste a répondu un état que cet écran ne sait pas nommer."};return{...m,detail:e(ne).detail===""?m.detail:e(ne).detail}}const z={ready:{level:"ok",word:"Prête",detail:"Elle répond et n’a rien à signaler."},consumable:{level:"warn",word:"Rouleau en fin de vie",detail:"Elle imprime, mais le rouleau arrive en fin de vie."},faulted:{level:"fault",word:"En panne",detail:"Elle ne peut pas imprimer."},unknown:{level:"unknown",word:"Silencieuse",detail:"Elle prend les étiquettes et ne dit rien en retour : c’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne."}};function ye(){const m=e(ne).observed_at===""?"Jamais observée depuis le démarrage":`Observée le ${Mt(e(ne).observed_at)}`,R=e(ne).pending_jobs_count;return`${m}, ${_e(R)} ${R>1?"travaux":"travail"} en attente.`}function ie(){const m=Oe();return m===""?ke():`${ke()} ${m}`}function ke(){return e(N)?e(L)?"Ce poste est déclaré sans balance : aucun port n’est écouté.":e(q)===""?"Aucun port n’est indiqué : choisissez-en un dans la liste ci-dessus pour écouter les trames.":e(Z)?W(e(q))?e(D)!==""?`L’écoute de ${e(q)} est arrêtée.`:e(C)!==""?`L’écoute de ${e(q)} est suspendue le temps de l’acte en cours.`:`Écoute de ${e(q)}.`:`${e(q)} n’est pas visible depuis ce poste : rien n’est écouté en continu.`:e(C)==="ports"?`Énumération des ports en cours : l’écoute de ${e(q)} démarre dès qu’il est vu.`:`Les ports de ce poste n’ont pas été énumérés : « Lister les ports » dira si ${e(q)} existe.`:"Lecture de la configuration en cours : le port à écouter n’est pas encore connu."}function Oe(){return e(X).length===0?"Aucune trame reçue pour l’instant.":e(X).length===1?`Une seule trame reçue — ${_e(c)} au plus sont gardées.`:`Les ${_e(e(X).length)} dernières trames — ${_e(c)} au plus, la plus récente en bas.`}function ve(){return!e(N)||e(L)||e(q)===""?"":e(D)!==""?"Reprendre l’écoute":e(Z)&&!W(e(q))?"Écouter ce port une fois":""}function K(m,R,Te){const He=`${_e(Te)} ${Te>1?R:m}`;return Te<=M?He+".":`${He} — seules les ${_e(M)} premières lignes sont affichées.`}function Ae(m,R){const Te=ae(m);return Te.length>0?Te:R.slice(0,M)}function Ge(m){return[...new TextEncoder().encode(m)].map(R=>R.toString(16).toUpperCase().padStart(2,"0")).join(" ")}const Fe={0:"NUL",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",9:"TAB",10:"LF",13:"CR",21:"NAK",27:"ESC",127:"DEL"};function De(m){let R="";for(const Te of m){const He=Te.codePointAt(0)??0;if(He>=32&&He!==127){R+=Te;continue}R+=`⟨${Fe[He]??He.toString(16).toUpperCase().padStart(2,"0")}⟩`}return R}function Me(m){return t.draft.faults.find(R=>R.field===m)?.message??""}function ae(m){return t.draft.faults.find(R=>R.field===m)?.allowed??[]}function Ne(m){return m instanceof Ft?m.message:m instanceof Error?"Le poste n’a pas répondu : "+m.message:"Le poste n’a pas répondu."}function pe(m){return m instanceof Ft&&(m.status===401||m.status===409)}const je=[{what:"label",name:"étiquette"},{what:"alignment",name:"alignement"},{what:"ruler",name:"réglette"}];var $e=Nl(),st=a($e),it=a(st),pt=r(a(it),2),Ee=r(a(pt),1,!0),nt=r(it,2),et=a(nt),mt=r(nt,4);{var Dt=m=>{var R=Ll(),Te=a(R);v(()=>la(Te,e(L))),Ze("change",Te,He=>t.draft.set("scale.present",!He.currentTarget.checked)),l(m,R)},Bt=m=>{var R=El();l(m,R)};g(mt,m=>{e(N)?m(Dt):m(Bt,-1)})}var Vt=r(mt,2),Et=a(Vt);{let m=u(()=>e(C)==="ports"),R=u(()=>e(C)!=="");tt(Et,{label:"Lister les ports",get busy(){return e(m)},get disabled(){return e(R)},onrun:()=>{k("ports",O)}})}var bt=r(Et,2);{let m=u(x),R=u(()=>e(C)!=="");tt(bt,{act:"detect",get label(){return e(m)},protected:!0,get disabled(){return e(R)},onrun:()=>{k("detect",we)}})}var Tt=r(Vt,2);{var b=m=>{var R=jl(),Te=Y(R),He=a(Te),lt=r(Te,2);{var ft=wt=>{var Pt=zl();at(Pt,21,()=>e(H).slice(0,M),Ot=>Ot.name,(Ot,St)=>{var Kt=Fa(),Qt=a(Kt),_a=a(Qt),or=r(Qt,2),ur=a(or);v(()=>{i(_a,e(St).name),i(ur,`${(e(St).description===""?"aucune description USB":e(St).description)??""}
+ ${e(St).vid===""?"":` — VID ${e(St).vid} PID ${e(St).pid}`}`)}),Ze("click",Qt,()=>t.draft.set("scale.options.port",e(St).name)),l(Ot,Kt)}),l(wt,Pt)};g(lt,wt=>{e(H).length>0&&wt(ft)})}v(wt=>i(He,wt),[()=>e(H).length===0?"Aucun port série n’est visible depuis ce poste.":K("port détecté","ports détectés",e(H).length)]),l(m,R)};g(Tt,m=>{e(Z)&&m(b)})}var G=r(Tt,2);{var oe=m=>{var R=Tl(),Te=Y(R),He=a(Te),lt=r(Te,2);at(lt,21,()=>e(fe).slice(0,M),ft=>ft.port,(ft,wt)=>{var Pt=Pl();let Ot;var St=a(Pt),Kt=a(St),Qt=r(St,2),_a=a(Qt);v(()=>{Ot=Zt(Pt,1,"svelte-1p4i0xm",null,Ot,{refused:e(wt).refused}),i(Kt,e(wt).port),i(_a,e(wt).message)}),l(ft,Pt)}),v(ft=>i(He,ft),[()=>K("port interrogé","ports interrogés",e(fe).length)]),l(m,R)};g(G,m=>{e(fe).length>0&&m(oe)})}var qe=r(G,2),Je=r(a(qe),2),Se=a(Je);{let m=u(()=>t.draft.text("scale.type")),R=u(()=>Me("scale.type")),Te=u(()=>ae("scale.type")),He=u(()=>!e(N));_t(Se,{label:"Protocole",path:"scale.type",get value(){return e(m)},hint:"Les valeurs acceptées apparaissent ici si l’enregistrement est refusé.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("scale.type",lt)})}var Pe=r(Se,2);{let m=u(()=>t.draft.text("scale.options.port")),R=u(()=>Me("scale.options.port")),Te=u(()=>Ae("scale.options.port",e(H).map(lt=>lt.name))),He=u(()=>!e(N));_t(Pe,{label:"Port série",path:"scale.options.port",get value(){return e(m)},hint:"Choisissez-le dans la liste détectée ci-dessus plutôt que de le taper : l’écoute permanente ne suit que des ports détectés.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("scale.options.port",lt)})}var j=r(qe,2),U=a(j),xe=a(U),Xe=r(U,2);{var Ke=m=>{var R=Cl(),Te=a(R),He=r(Te);{var lt=ft=>{{let wt=u(()=>e(C)==="listen"),Pt=u(()=>e(C)!=="");tt(ft,{act:"listen",get label(){return e(Le)},protected:!0,get busy(){return e(wt)},get disabled(){return e(Pt)},onrun:()=>{k("listen",de)}})}};g(He,ft=>{e(Le)!==""&&ft(lt)})}v(()=>i(Te,`${e(D)??""} `)),l(m,R)};g(Xe,m=>{(e(D)!==""||e(Le)!=="")&&m(Ke)})}var Ye=r(Xe,2);{var ce=m=>{var R=Rl();at(R,21,()=>e(X),ma,(Te,He)=>{var lt=Ol(),ft=a(lt),wt=a(ft),Pt=r(ft,2),Ot=a(Pt);v((St,Kt)=>{i(wt,St),i(Ot,Kt)},[()=>Ge(e(He)),()=>De(e(He))]),l(Te,lt)}),l(m,R)};g(Ye,m=>{e(X).length>0&&m(ce)})}var Ie=r(st,2),Be=a(Ie),ct=r(a(Be),2),dt=r(a(ct),1,!0),ot=r(Be,2),ut=a(ot),ht=r(ot,2),kt=a(ht),zt=r(ht,2),qt=a(zt);{let m=u(()=>e(C)==="printers"),R=u(()=>e(C)!=="");tt(qt,{label:"Lister les files",get busy(){return e(m)},get disabled(){return e(R)},onrun:()=>{k("printers",le)}})}var jt=r(qt,2);{let m=u(()=>e(C)==="discover"),R=u(()=>e(C)!=="");tt(jt,{label:"Rechercher l’imprimante",protected:!0,get busy(){return e(m)},get disabled(){return e(R)},onrun:()=>{k("discover",Re)}})}var Ct=r(jt,2);at(Ct,17,()=>je,m=>m.what,(m,R)=>{{let Te=u(()=>`Auto-test : ${e(R).name}${e(C)===e(R).what?" — en cours…":""}`),He=u(()=>e(C)!=="");tt(m,{get act(){return e(R).what},get label(){return e(Te)},protected:!0,get disabled(){return e(He)},onrun:()=>{k(e(R).what,()=>We(e(R).what))}})}});var It=r(zt,2);{var Wt=m=>{var R=Al(),Te=Y(R),He=a(Te),lt=r(Te,2);at(lt,21,()=>e(J).slice(0,M),ft=>ft.name,(ft,wt)=>{var Pt=Fa(),Ot=a(Pt),St=a(Ot),Kt=r(Ot,2),Qt=a(Kt);v(()=>{i(St,e(wt).name),i(Qt,`${e(wt).detail??""}${e(wt).default?" — file par défaut du système":""}`)}),Ze("click",Ot,()=>t.draft.set("printer.options.queue",e(wt).name)),l(ft,Pt)}),v(ft=>i(He,ft),[()=>K("file d’impression","files d’impression",e(J).length)]),l(m,R)};g(It,m=>{e(J).length>0&&m(Wt)})}var Jt=r(It,2),ta=r(a(Jt),2),rt=a(ta);{let m=u(()=>t.draft.text("printer.type")),R=u(()=>Me("printer.type")),Te=u(()=>ae("printer.type")),He=u(()=>!e(N));_t(rt,{label:"Driver",path:"printer.type",get value(){return e(m)},hint:"Gardez le driver raster : c’est celui que les postes en service utilisent.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("printer.type",lt)})}var yt=r(rt,2);{let m=u(()=>t.draft.text("printer.options.transport")),R=u(()=>Me("printer.options.transport")),Te=u(()=>ae("printer.options.transport")),He=u(()=>!e(N));_t(yt,{label:"Transport",path:"printer.options.transport",get value(){return e(m)},hint:"Local par défaut : une file Windows ou un fichier de périphérique de ce poste.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("printer.options.transport",lt)})}var Yt=r(yt,2);{let m=u(()=>t.draft.text("printer.options.queue")),R=u(()=>Me("printer.options.queue")),Te=u(()=>Ae("printer.options.queue",e(J).map(lt=>lt.name))),He=u(()=>!e(N));_t(Yt,{label:"File ou chemin",path:"printer.options.queue",get value(){return e(m)},hint:"Choisissez-la dans la liste ci-dessus : une file mal orthographiée ne s’imprime pas.",get fault(){return e(R)},get allowed(){return e(Te)},get disabled(){return e(He)},onchange:lt=>t.draft.set("printer.options.queue",lt)})}v(m=>{Ue(pt,"data-level",e(he).level),i(Ee,e(he).word),i(et,e(he).detail),i(xe,e(Ce)),Ue(ct,"data-level",e(ze).level),i(dt,e(ze).word),i(ut,e(ze).detail),i(kt,m)},[()=>ye()]),Oa("open","toggle",qe,m=>p(P,m),()=>e(P)),Oa("open","toggle",Jt,m=>p(se,m),()=>e(se)),l(n,$e),$t()}At(["change","click"]);var Dl=o(""),Il=o('L’export sera proposé quand le journal aura répondu.'),Ul=o('L’export n’est pas proposé : ce poste n’a pas répondu à la lecture du journal.'),Fl=o('Exporter en CSV'),Ml=o('
'),Bl=o(''),Vl=o(`
Aucune trame brute n’a été enregistrée pour cette pesée : il n’y a
+ rien à rejouer.
`),Wl=o(`
La trame repart dans le décodeur du poste EN SERVICE : le poids
+ affiché au client change, et rien ne le remet comme il était. C’est
+ ce qui fait d’un refus inexpliqué un test permanent, sans
+ déplacement au magasin et sans balance.
`,1),Gl=o('
Produit
Référence
Vente
Brut / tare / net
Stabilité
Origine du poids
Résultat
Trame brute
'),Hl=o('
',1),Jl=o('
Quand
Produit
Net
Code-barres
Résultat
Durée
Détail
',1),Kl=o('
',1),Xl=o(''),Yl=o(''),Ql=o(''),Zl=o('
'),ei=o('
',1),ti=o('
');function ai(n,t){Nt(t,!0);const s=vt(t,"admin",7),c=200,_=5e3,w=50,T=500,A=7,M={sent:"envoyée à l’imprimante",rejected:"refusée",failed:"en échec",reprint:"réimpression"},H=[{value:"",label:"toutes"},{value:"sent",label:"envoyées à l’imprimante"},{value:"rejected",label:"refusées"},{value:"failed",label:"en échec"},{value:"reprint",label:"réimpressions"}],J={scale:"balance",manual:"saisie manuelle",replay:"trame rejouée"},fe={stable:"stable",unstable:"instable",unknown:"non déclarée par la balance",not_applicable:"sans objet — saisie manuelle"},ge={by_weight:"au poids",by_unit:"à l’unité"},be={debug:"mise au point",info:"information",warn:"avertissement",error:"erreur",critical:"critique"};let Z=F(Lt([])),C=F(Lt([])),me=F(""),Q=F(null),ue=F(!1),re=F(!1),P=F("loading"),se=F("loading"),V=F(""),B=F("");const ne=u(()=>({limit:String(c),...e(me)===""?{}:{result:e(me)}})),N=u(()=>({...e(ne),limit:String(_)})),L=u(()=>e(Z).find(W=>W.id===e(Q))??null),q=u(()=>H.find(W=>W.value===e(me))?.label??""),D=u(()=>Ce(e(Z).length,c,"pesée","pesées",`L’export CSV en emporte jusqu’à ${_e(_)}.`)),X=u(()=>Ce(e(C).length,w,"ligne","lignes",`Le fichier de diagnostic emporte les ${_e(T)} dernières.`));he();async function he(){p(Q,null),p(re,!0),p(P,"loading"),p(se,"loading"),p(Z,[],!0),p(C,[],!0),p(V,""),p(B,"");try{const W=await s().load(()=>Kr(e(ne)));p(P,W===null?"unread":"read",!0),p(V,W===null?s().actionError:"",!0),p(Z,W??[],!0);const te=await s().load(()=>Yr({limit:String(w)}));p(se,te===null?"unread":"read",!0),p(B,te===null?s().actionError:"",!0),p(C,te??[],!0)}finally{p(re,!1),s().actionError=""}}async function ze(W){s().notice="",s().actionError="",p(ue,!0);try{const te=await s().protect(()=>tn(W));te!==null&&(s().notice=te.message)}finally{p(ue,!1)}}function Ce(W,te,de,Ve,S){const h=W>1?Ve:de;return W{var de=Kl(),Ve=Y(de),S=r(a(Ve),2);at(S,21,()=>H,f=>f.value,(f,E)=>{var z=Dl(),ye=a(z),ie={};v(()=>{i(ye,e(E).label),ie!==(ie=e(E).value)&&(z.value=(z.__value=e(E).value)??"")}),l(f,z)});var h=r(S,2);tt(h,{label:"Rafraîchir",get busy(){return e(re)},onrun:()=>{he()}});var k=r(h,2);{var O=f=>{var E=Il();l(f,E)},le=f=>{var E=Ul();l(f,E)},Re=f=>{var E=Fl();v(z=>Ue(E,"href",z),[()=>Xr(e(N))]),l(f,E)};g(k,f=>{e(P)==="loading"?f(O):e(P)==="unread"?f(le,1):f(Re,-1)})}var We=r(Ve,2);{var we=f=>{var E=Ml(),z=a(E);v(ye=>i(z,`L’export emporte le même filtre que le tableau, mais pas son plafond : il descend
+ jusqu’à ${ye??""} pesées, en point-virgule et en UTF-8 — il s’ouvre
+ tel quel dans le tableur d’un Windows français. Il ne demande aucun mot de passe : la
+ lecture du journal n’en demande pas non plus, et le fichier de diagnostic emporte déjà
+ les deux cents dernières pesées.`),[()=>_e(_)]),l(f,E)};g(We,f=>{e(P)==="read"&&f(we)})}var d=r(We,2);{var x=f=>{var E=Bl(),z=a(E);{var ye=ve=>{var K=xt("Lecture du journal…");l(ve,K)},ie=ve=>{var K=xt();v(()=>i(K,`Le journal n’a pas pu être lu : ${e(V)??""} Ce n’est pas « aucune pesée ».`)),l(ve,K)},ke=ve=>{var K=xt("Le journal ne contient aucune pesée.");l(ve,K)},Oe=ve=>{var K=xt();v(()=>i(K,`Aucune pesée ne correspond au filtre « ${e(q)??""} ».`)),l(ve,K)};g(z,ve=>{e(P)==="loading"?ve(ye):e(P)==="unread"?ve(ie,1):e(me)===""?ve(ke,2):ve(Oe,-1)})}l(f,E)},$=f=>{var E=Jl(),z=Y(E),ye=a(z),ie=r(z,2),ke=a(ie),Oe=r(a(ke));at(Oe,21,()=>e(Z),ve=>ve.id,(ve,K)=>{var Ae=Hl(),Ge=Y(Ae);let Fe;var De=a(Ge),Me=a(De),ae=r(De),Ne=a(ae),pe=r(ae),je=a(pe),$e=r(pe),st=a($e),it=r($e),pt=a(it),Ee=r(it),nt=a(Ee),et=r(Ee),mt=a(et),Dt=a(mt),Bt=r(Ge,2);{var Vt=Et=>{var bt=Gl(),Tt=a(bt);Ue(Tt,"colspan",A);var b=a(Tt),G=a(b),oe=r(b,2),qe=r(a(oe),2),Je=a(qe),Se=r(qe,4),Pe=a(Se),j=r(Se,4),U=a(j),xe=r(j,4),Xe=a(xe),Ke=r(xe,4),Ye=a(Ke),ce=r(Ke,4),Ie=a(ce),Be=r(ce,4),ct=a(Be),dt=r(Be,4),ot=a(dt),ut=a(ot),ht=r(oe,2);{var kt=qt=>{var jt=Vl();l(qt,jt)},zt=qt=>{var jt=Wl(),Ct=Y(jt),It=a(Ct);tt(It,{kind:"destructive",label:"Rejouer cette trame",protected:!0,get busy(){return e(ue)},onrun:()=>{ze(e(L).frame)}}),l(qt,jt)};g(ht,qt=>{e(L).frame===""?qt(kt):qt(zt,-1)})}v((qt,jt,Ct,It,Wt,Jt,ta,rt,yt)=>{Ue(bt,"data-detail",e(K).id),i(G,`Pesée ${e(L).id??""}`),i(Je,`${e(L).product_name??""} (${e(L).product_id??""})`),i(Pe,e(L).reference),i(U,`${qt??""} —
+ ${jt??""}
+ ${e(L).quantity>1?"unités":"unité"}`),i(Xe,`${Ct??""} g / ${It??""} g /
+ ${Wt??""} g`),i(Ye,`${Jt??""} — cadence
+ médiane ${ta??""}`),i(Ie,rt),i(ct,`${yt??""}${e(L).detail===""?"":` — ${e(L).detail}`}`),i(ut,e(L).frame===""?"aucune trame enregistrée":e(L).frame)},[()=>Le(ge,e(L).mode,"mode de vente inconnu"),()=>_e(e(L).quantity),()=>_e(e(L).gross_g),()=>_e(e(L).tare_g),()=>_e(e(L).net_g),()=>Le(fe,e(L).stability,"stabilité inconnue"),()=>va(e(L).rate_ms),()=>Le(J,e(L).source,"origine inconnue"),()=>Le(M,e(L).result,"résultat inconnu")]),l(Et,bt)};g(Bt,Et=>{e(Q)===e(K).id&&e(L)!==null&&Et(Vt)})}v((Et,bt,Tt,b)=>{Fe=Zt(Ge,1,"svelte-86o8oz",null,Fe,{open:e(Q)===e(K).id}),i(Me,Et),i(Ne,e(K).product_name),i(je,`${bt??""} g`),i(st,e(K).barcode),i(pt,Tt),i(nt,b),Ue(mt,"aria-expanded",e(Q)===e(K).id),i(Dt,e(Q)===e(K).id?"fermer":"détail")},[()=>Mt(e(K).occurred_at),()=>_e(e(K).net_g),()=>Le(M,e(K).result,"résultat inconnu"),()=>va(e(K).duration_ms)]),Ze("click",mt,()=>p(Q,e(Q)===e(K).id?null:e(K).id,!0)),l(ve,Ae)}),v(()=>i(ye,e(D))),l(f,E)};g(d,f=>{e(Z).length===0?f(x):f($,-1)})}Ze("change",S,()=>{he()}),Sr(S,()=>e(me),f=>p(me,f)),l(W,de)},$$slots:{default:!0}});var I=r(y,2);Qe(I,{title:"Journal technique",children:(W,te)=>{var de=Ut(),Ve=Y(de);{var S=k=>{var O=Xl(),le=a(O);{var Re=d=>{var x=xt("Lecture du journal technique…");l(d,x)},We=d=>{var x=xt();v(()=>i(x,`Le journal technique n’a pas pu être lu : ${e(B)??""} Ce n’est pas « aucune
+ ligne ».`)),l(d,x)},we=d=>{var x=xt("Aucune ligne technique.");l(d,x)};g(le,d=>{e(se)==="loading"?d(Re):e(se)==="unread"?d(We,1):d(we,-1)})}l(k,O)},h=k=>{var O=ei(),le=Y(O),Re=a(le),We=r(le,2),we=a(We);at(we,21,()=>e(C),d=>d.id,(d,x)=>{var $=Zl(),f=a($),E=a(f),z=r(f,2),ye=a(z),ie=r(z,2),ke=a(ie),Oe=r(ie,2);{var ve=De=>{var Me=Yl(),ae=a(Me);v(()=>i(ae,e(x).code)),l(De,Me)};g(Oe,De=>{e(x).code!==""&&Ht.showTechnicalNames&&De(ve)})}var K=r(Oe,2),Ae=a(K),Ge=r(K,2);{var Fe=De=>{var Me=Ql(),ae=a(Me);v(()=>i(ae,e(x).detail)),l(De,Me)};g(Ge,De=>{e(x).detail!==""&&De(Fe)})}v((De,Me,ae)=>{Ue($,"data-level",e(x).level),i(E,De),i(ye,Me),i(ke,ae),i(Ae,e(x).message)},[()=>Mt(e(x).occurred_at),()=>Le(be,e(x).level,"niveau inconnu"),()=>tr(e(x).source)]),l(d,$)}),v(()=>i(Re,e(X))),l(k,O)};g(Ve,k=>{e(C).length===0?k(S):k(h,-1)})}l(W,de)},$$slots:{default:!0}}),l(n,ee),$t()}At(["change","click"]);var ri=o('
'),ni=o('
'),si=o('
'),li=o('
'),ii=o('
Lecture de la configuration en cours… les flèches attendent qu’elle soit arrivée.
'),oi=o(`
Décalage horizontal
Décalage vertical
Le décalage ne descend pas sous zéro : le poste refuse un décalage négatif quel que
+ soit le gabarit. Le maximum, lui, dépend de la géométrie du gabarit ; il est annoncé
+ ici si l’enregistrement le dépasse.
Le symbole code-barres est volontairement tronqué : un symbole conforme n’entre pas
+ sur 40 × 25 mm avec les cinq champs texte. Ce n’est pas un défaut de rendu et il n’y
+ a rien à corriger.
Le détail chiffré du symbole — largeur de module, modules rendus, modules attendus —
+ n’est pas servi par ce poste. Cet écran n’affiche pas un chiffre qu’il aurait deviné.
`,1),ui=o(`
Chaque appui sort une étiquette pour de bon : le mot de passe est demandé au moment
+ d’imprimer, et l’impression repart d’elle-même une fois la session ouverte.
`,1),ci=o('
');function di(n,t){Nt(t,!0);const s=vt(t,"admin",7),c="printer.template",_="printer.options.offset_x",w="printer.options.offset_y",T=0,A="L’aperçu n’a pas pu être rendu par le poste.",M="Le poste a rendu l’aperçu, mais le navigateur ne l’a pas affiché.",H=[{what:"alignment",label:"Imprimer la mire d’alignement"},{what:"ruler",label:"Imprimer la réglette"}];let J=F(1),fe=F(!0),ge=F(!1),be=F(""),Z=F(null),C=F(""),me=null,Q=!1;const ue=u(()=>t.draft.text(c)),re=u(()=>Jr(e(ue),e(fe),e(ge),e(J))),P=u(()=>t.draft.config!==null),se=u(()=>t.draft.number(_)),V=u(()=>t.draft.number(w)),B=u(()=>I(_)),ne=u(()=>I(w)),N=u(Le),L=u(()=>s().busy||e(C)!=="");Xt(()=>{if(t.draft.config===null)return;if(t.draft.dirty){me===null&&q();return}const S={x:t.draft.number(_),y:t.draft.number(w)};if(me!==null&&me.x===S.x&&me.y===S.y)return;const h=me===null;me=S,p(Z,S,!0),h||he()});async function q(){if(!Q){Q=!0;try{const S=await za(),h={x:D(S.config,_),y:D(S.config,w)};me=h,p(Z,h,!0)}catch{}finally{Q=!1}}}function D(S,h){let k=S;for(const O of h.split(".")){if(k===null||typeof k!="object")return 0;k=k[O]}return typeof k=="number"?k:0}function X(S,h){const k=t.draft.number(S)+h;k=2?"s":""}`}function y(S,h){const k=Number(h);h.trim()===""||Number.isNaN(k)||t.draft.set(S,k)}function I(S){return t.draft.faults.find(h=>h.field===S)?.message??""}async function W(S){if(!e(L)){p(C,S,!0),s().actionError="",s().notice="";try{const h=await s().protect(()=>Qa(S));if(h===null)return;s().notice=h.message,await s().refresh()}finally{p(C,"")}}}var te=ci(),de=a(te);Qe(de,{title:"Aperçu de l’étiquette",note:"Le même moteur que l’impression (A2) : le décalage se voit parce qu’il est cuit dans le bitmap. L’image porte le gabarit en cours d’édition, mais le décalage ENREGISTRÉ — jamais celui que les flèches sont en train de régler.",children:(S,h)=>{var k=oi(),O=Y(k);{var le=Ee=>{var nt=ri(),et=a(nt);v(()=>i(et,e(N))),l(Ee,nt)};g(O,Ee=>{e(N)!==""&&Ee(le)})}var Re=r(O,2),We=a(Re),we=a(We),d=r(we,2);{var x=Ee=>{var nt=ni(),et=a(nt);v(()=>i(et,e(be))),l(Ee,nt)};g(d,Ee=>{e(be)!==""&&Ee(x)})}var $=r(We,2),f=a($),E=r(a(f),2),z=a(E),ye=r(f,2),ie=a(ye);{let Ee=u(()=>!e(P)||e(se)<=T);tt(ie,{label:"← 1 dot",get disabled(){return e(Ee)},onrun:()=>X(_,-1)})}var ke=r(ie,2);{let Ee=u(()=>!e(P));tt(ke,{label:"1 dot →",get disabled(){return e(Ee)},onrun:()=>X(_,1)})}var Oe=r(ye,2);{var ve=Ee=>{var nt=si(),et=a(nt);v(()=>i(et,e(B))),l(Ee,nt)};g(Oe,Ee=>{e(B)!==""&&Ee(ve)})}var K=r(Oe,2),Ae=r(a(K),2),Ge=a(Ae),Fe=r(K,2),De=a(Fe);{let Ee=u(()=>!e(P)||e(V)<=T);tt(De,{label:"↑ 1 dot",get disabled(){return e(Ee)},onrun:()=>X(w,-1)})}var Me=r(De,2);{let Ee=u(()=>!e(P));tt(Me,{label:"1 dot ↓",get disabled(){return e(Ee)},onrun:()=>X(w,1)})}var ae=r(Fe,2);{var Ne=Ee=>{var nt=li(),et=a(nt);v(()=>i(et,e(ne))),l(Ee,nt)};g(ae,Ee=>{e(ne)!==""&&Ee(Ne)})}var pe=r(ae,4),je=a(pe),$e=r(pe,2),st=a($e),it=r($e,2);{var pt=Ee=>{var nt=ii();l(Ee,nt)};g(it,Ee=>{e(P)||Ee(pt)})}v((Ee,nt)=>{Ue(we,"src",e(re)),i(z,Ee),i(Ge,nt)},[()=>ee(e(se)),()=>ee(e(V))]),na("load",we,()=>p(be,"")),na("error",we,()=>{ze(e(re))}),Ze("change",je,()=>he()),Ca(je,()=>e(fe),Ee=>p(fe,Ee)),Ze("change",st,()=>he()),Ca(st,()=>e(ge),Ee=>p(ge,Ee)),l(S,k)},$$slots:{default:!0}});var Ve=r(de,2);Qe(Ve,{title:"Gabarit et impression",children:(S,h)=>{var k=ui(),O=Y(k);{let d=u(()=>I(c)),x=u(()=>!e(P));_t(O,{label:"Gabarit",path:c,get value(){return e(ue)},hint:"Le gabarit reproduit à l’identique s’appelle weighing_identical (A1).",get fault(){return e(d)},get disabled(){return e(x)},onchange:$=>{t.draft.set(c,$),he()}})}var le=r(O,2);{let d=u(()=>t.draft.text("printer.options.darkness")),x=u(()=>I("printer.options.darkness")),$=u(()=>!e(P));_t(le,{label:"Noircissement",path:"printer.options.darkness",kind:"number",get value(){return e(d)},hint:"Trop bas, l’étiquette pâlit au soleil ; trop haut, elle bave et le scanner refuse.",get fault(){return e(x)},get disabled(){return e($)},onchange:f=>y("printer.options.darkness",f)})}var Re=r(le,2);{let d=u(()=>t.draft.text("printer.options.speed")),x=u(()=>I("printer.options.speed")),$=u(()=>!e(P));_t(Re,{label:"Vitesse",path:"printer.options.speed",kind:"number",get value(){return e(d)},get fault(){return e(x)},get disabled(){return e($)},onchange:f=>y("printer.options.speed",f)})}var We=r(Re,2);{let d=u(()=>t.draft.text("printer.options.copies")),x=u(()=>I("printer.options.copies")),$=u(()=>!e(P));_t(We,{label:"Exemplaires",path:"printer.options.copies",kind:"number",get value(){return e(d)},hint:"Un client repart avec une étiquette : deux exemplaires se justifient, pas se devinent.",get fault(){return e(x)},get disabled(){return e($)},onchange:f=>y("printer.options.copies",f)})}var we=r(We,2);at(we,21,()=>H,d=>d.what,(d,x)=>{{let $=u(()=>e(C)===e(x).what);tt(d,{get act(){return e(x).what},get label(){return e(x).label},protected:!0,get busy(){return e($)},get disabled(){return e(L)},onrun:()=>{W(e(x).what)}})}}),l(S,k)},$$slots:{default:!0}}),l(n,te),$t()}At(["change"]);var vi=o(""),pi=o(''),fi=o(''),gi=o('
Aucun tarif déclaré dans la configuration lue.
'),mi=o('Prix du catalogue Odoo — pas de remise'),Ma=o(''),hi=o(' % ',1),_i=o('
'),bi=o(`
Code
Libellé
Abrégé
Remise
Ordre
Un champ vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la
+ retrouve dès qu’on quitte le champ. Une remise effacée serait le plein tarif pour
+ tous les adhérents.
`,1),wi=o(" ",1),yi=o(`
Les deux arrondis sont distincts parce qu’ils tombent à des endroits différents du
+ calcul : arrondir le prix au kilo puis le multiplier, ou multiplier puis arrondir, ne
+ donne pas le même centime sur une étiquette. Le poste applique l’arrondi commercial,
+ et l’écart se voit au centime près sur la même pesée.
`,1),xi=o('
Rien ne s’affiche au client : c’est une information.
'),ki=o('
'),qi=o(''),Si=o('
'),Li=o('
'),Ei=o(`
Aucun verdict en ce moment : ces lignes portent sur la pesée EN COURS, et le poste
+ est au repos. Elles apparaissent le temps d’un cycle, quand un client pose son sac
+ et touche une tuile.
`),zi=o(''),ji=o('',1),Pi=o('
'),Ti=o('
',1),Ci=o(`
L’ordre compte : le premier verdict bloquant décide de ce que le client lit. Les
+ garde-fous 1 à 7 portent sur l’état de la balance, c’est-à-dire le poids brut ; les
+ garde-fous 8 à 14 portent sur la vente, c’est-à-dire le net. Le code en gris est
+ celui du journal, celui qu’on lit au téléphone.
Un seuil vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la retrouve
+ dès qu’on quitte le champ. Pour changer un seuil, on tape l’autre valeur.
Ce que les garde-fous disent de la pesée en cours
`,1),Oi=o(`
Le plan de numérotation n’est PAS ici, et c’est tout l’intérêt : préfixes, largeur de
+ la référence, largeur de la charge utile, décimales et mode de vente sont une
+ constante du binaire, indexée par préfixe et vérifiée au démarrage.
+ Un champ qui change le SENS du code lu par la caisse n’est pas un réglage, c’est un
+ contrat externe : il change avec une version du binaire, relue et testée, jamais
+ depuis l’écran d’un poste.
`,1),Ri=o('
Aucune dérogation : la limite générale s’applique à tous les produits.
'),Ai=o(`
Les noms de produits n’ont pas pu être lus : le catalogue en service n’a pas
+ répondu. Les identifiants Odoo restent affichés.
`),Ni=o(`Produit retiré : le garde-fou 14 refuse le produit avant que cette
+ dérogation ait un sens.`),$i=o('
'),Di=o('
',1),Ii=o('
');function Ui(n,t){Nt(t,!0);const s=(y,I=ia,W=ia,te=ia)=>{var de=fi(),Ve=a(de),S=r(Ve,2),h=a(S),k=a(h),O=r(h,2);{var le=we=>{var d=vi(),x=a(d);v(()=>i(x,I())),l(we,d)};g(O,we=>{Ht.showTechnicalNames&&we(le)})}var Re=r(O,2);{var We=we=>{var d=pi(),x=a(d);v(()=>i(x,te())),l(we,d)};g(Re,we=>{te()!==""&&we(We)})}v((we,d)=>{Ue(de,"data-flag",I()),Ue(de,"data-on",we),la(Ve,d),i(k,W())},[()=>String(t.draft.flag(I())),()=>t.draft.flag(I())]),Ze("change",Ve,we=>t.draft.set(I(),we.currentTarget.checked)),l(y,de)},c=20,_=u(()=>fe(t.draft)),w=u(()=>t.health.state.diagnostics),T=u(()=>t.health.decisions.filter(y=>y.min_weight_g!==null)),A=u(()=>e(T).filter(y=>!y.offered)),M=u(()=>e(T).slice(0,c));let H=F(Lt({})),J=F("loading");re();function fe(y){const I=y.value("pricing.tiers");return Array.isArray(I)?I.map(W=>{const te=W??{},de=te.discount_percent;return{code:String(te.code??""),label:String(te.label??""),abbrev:String(te.abbrev??""),written:de===void 0?null:String(de),discount:ge(de)?de:null,rank:Number(te.rank??0)}}):[]}function ge(y){return typeof y!="number"||!Number.isFinite(y)||y<0||y>100?!1:Math.abs(y*10-Math.round(y*10))<1e-9}const be=u(()=>String(t.draft.value("pricing.reference_code")??""));function Z(y){return y===null?"":String(y).replace(".",",")}function C(y){const I=1e3-Math.round(y*10);return`${String(Math.trunc(I/100))},${String(I%100).padStart(2,"0")}`}function me(y,I){const W=Number(I);I.trim()===""||Number.isNaN(W)||t.draft.set(y,W)}function Q(y,I){const W=I.trim().replace(",",".");if(!/^\d{1,3}(\.\d)?$/u.test(W))return;const te=Number(W);te>100||t.draft.set(y,te)}function ue(y,I){!(y instanceof HTMLInputElement)||y.value===I||(y.value=I)}async function re(){try{const y=await Ja();p(H,Object.fromEntries(y.products.map(I=>[I.id,I.name])),!0),p(J,"read")}catch{p(J,"unread")}}function P(y){const I=e(H)[y];return I!==void 0?I:e(J)==="loading"?"Lecture du nom…":e(J)==="read"?"Produit absent du catalogue en service":"Nom non lu"}function se(y){return y<=1?`${String(y)} tarif déclaré`:`${String(y)} tarifs déclarés`}const V=u(()=>`${_e(e(w).length)} ${e(w).length>1?"verdicts":"verdict"} sur les quatorze garde-fous.`),B=u(()=>`${_e(e(M).length)} ${e(M).length>1?"lignes affichées":"ligne affichée"} sur ${_e(e(T).length)} `+(e(A).length===0?`${e(T).length>1?"dérogations en vigueur":"dérogation en vigueur"}.`:`${e(T).length>1?"dérogations enregistrées":"dérogation enregistrée"}, dont ${_e(e(A).length)} sur un produit retiré, sans effet : le garde-fou 14 refuse le produit avant que le 8 ait un sens.`)+(e(T).length>e(M).length?" Les autres se lisent produit par produit depuis l’onglet Catalogue.":""));function ne(y){return`plancher ${_e(y)} g : refusé à ${_e(y)} g et en dessous`}const N=[{rank:1,code:"OVERLOAD",label:"Surcharge",when:"La balance annonce elle-même OL, ou le poids brut dépasse la capacité.",severity:"Bloquant",blocking:!0,message:"La balance est en surcharge. Retirez votre article.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la capacité, réglée au garde-fou 9 sous « ${Gt("limits.max_weight_g")} ».`},{rank:2,code:"MEASUREMENT_EXPIRED",label:"Poids périmé",when:"La mesure est plus vieille que la péremption, dans les deux modes de stabilité.",severity:"Bloquant",blocking:!0,message:"Poids indisponible. Patientez ou appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil à régler : le poste calcule lui-même à partir du rythme de la balance."},{rank:3,code:"BASKET_MISSING",label:"Panier absent",when:"Le poids brut tombe dans la fenêtre négative du panier : il a été soulevé.",severity:"Bloquant",blocking:!0,message:"Le panier n'est pas sur la balance. Reposez-le.",thresholds:[{path:"limits.basket_min_g",label:"Bas de la fenêtre du panier",hint:"En grammes, NÉGATIF : c’est le poids du panier que la balance a perdu."},{path:"limits.basket_max_g",label:"Haut de la fenêtre du panier",hint:"Négatif lui aussi, et plus proche de zéro que le bas."}],switchPath:"limits.basket_check_enabled",switchLabel:"Ce poste travaille avec un panier taré",note:"La règle s’active ou non, en bloc : il n’y a pas de demi-mesure à régler."},{rank:4,code:"SCALE_EMPTY",label:"Plateau vide",when:"Le poids brut ne sort pas de la bande « il n’y a rien sur le plateau ».",severity:"Bloquant — un filet, hors parcours nominal",blocking:!0,message:"Posez votre produit.",thresholds:[{path:"limits.empty_max_g",label:"Plateau considéré vide",hint:"En dessous, le poste considère qu’il n’y a rien sur le plateau."}],switchPath:"",switchLabel:"",note:"Toucher une tuile sur un plateau vide ARME la sélection au lieu d’être refusé : la règle reste évaluée pour la saisie manuelle et les chemins dérivés."},{rank:5,code:"TARE_REQUIRED",label:"Remise à zéro nécessaire",when:"Le brut est sous la bande du plateau vide, et hors de la fenêtre du panier.",severity:"Bloquant",blocking:!0,message:"La balance doit être remise à zéro.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la valeur négative de celui du garde-fou 4, « ${Gt("limits.empty_max_g")} ».`},{rank:6,code:"WEIGHT_UNSTABLE",label:"Pesée instable",when:"La trame déclare la mesure instable.",severity:"Information par défaut (A3)",blocking:!1,message:"Pesée en cours…",thresholds:[],switchPath:"",switchLabel:"",note:"La sévérité suit l’exigence de stabilité : elle passe à Bloquant quand celle-ci est réglée sur « blocking ». L’impression n’est jamais bloquée par défaut."},{rank:7,code:"TARE_INVALID",label:"Emballage incohérent",when:"Une tare a été saisie, et elle atteint la pesée ou dépasse le maximum.",severity:"Bloquant",blocking:!0,message:"Le poids de l'emballage est supérieur ou égal à la pesée.",thresholds:[{path:"limits.max_tare_g",label:"Tare maximum",hint:"Une tare plus lourde que le maximum est une faute de frappe."}],switchPath:"",switchLabel:"",note:""},{rank:8,code:"WEIGHT_TOO_LOW",label:"Poids trop faible",when:"Vente au poids : le NET est strictement positif et ne dépasse pas le plancher.",severity:"Bloquant",blocking:!0,message:"La balance doit être retarée, ou l'emballage est trop lourd.",thresholds:[{path:"limits.min_weight_g",label:"Poids minimum",hint:"Une dérogation par produit existe, dans l’onglet Catalogue."}],switchPath:"",switchLabel:"",note:""},{rank:9,code:"WEIGHT_TOO_HIGH",label:"Poids trop élevé",when:"Le NET dépasse la capacité — strictement, pour que la capacité reste atteignable.",severity:"Bloquant",blocking:!0,message:"{{.Weight}} kg, ça paraît un peu lourd !",thresholds:[{path:"limits.max_weight_g",label:"Poids maximum",hint:"C’est la capacité du champ NNDDD du code-barres, pas un seuil de vraisemblance."}],switchPath:"",switchLabel:"",note:""},{rank:10,code:"UNITS_OUT_OF_RANGE",label:"Nombre d’unités hors plage",when:"Vente à l’unité : la quantité sort de la plage.",severity:"Bloquant",blocking:!0,message:"{{.Quantity}} unités, ça paraît un peu beaucoup !",thresholds:[{path:"limits.min_units",label:"Unités minimum",hint:""},{path:"limits.max_units",label:"Unités maximum",hint:""}],switchPath:"",switchLabel:"",note:""},{rank:11,code:"AMOUNT_OUT_OF_CAPACITY",label:"Montant hors capacité du code-barres",when:"La charge utile encode un PRIX, et il dépasse ce que le champ peut porter.",severity:"Bloquant",blocking:!0,message:"Prix trop élevé pour le code-barres.",thresholds:[{path:"limits.max_amount_cents",label:"Montant maximum",hint:"En centimes. Aucun préfixe du plan livré n’encode un prix : la règle est éprouvée sans qu’aucun produit puisse l’atteindre."}],switchPath:"",switchLabel:"",note:""},{rank:12,code:"ZERO_PRICE",label:"Prix nul",when:"Le montant du tarif imprimé en grand vaut zéro.",severity:"Bloquant",blocking:!0,message:"Prix nul. Appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : un produit à 0 € est une anomalie sans nuance."},{rank:13,code:"LIGHT_PRODUCT_ALLOWED",label:"Produit léger autorisé",when:"Le garde-fou 8 n’a pas déclenché grâce à la dérogation du produit.",severity:"Information",blocking:!1,message:"",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil général : c’est la dérogation par produit, listée plus bas et posée depuis l’onglet Catalogue. Rien ne s’affiche au client ; l’id du produit est journalisé."},{rank:14,code:"PRODUCT_WITHDRAWN",label:"Produit retiré",when:"Quelqu’un a décidé de ne plus proposer ce produit.",severity:"Bloquant",blocking:!0,message:"Ce produit n'est pas disponible.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : c’est une décision humaine, prise depuis l’onglet Catalogue. Aucune règle d’import ne peut la déduire."}];function L(y){return N.find(I=>I.code===y)?.label??"Garde-fou inconnu de cet écran"}const q=["{{.Weight}}","{{.Quantity}}"];function D(y){return y==="blocking"?"Bloquant":y==="info"?"Information":"Sévérité inconnue de cet écran"}var X=Ii(),he=a(X);Qe(he,{title:"Grille de tarifs",note:"Un second tarif n’est pas une case à cocher : c’est une ligne de plus dans cette grille.",children:(y,I)=>{var W=wi(),te=Y(W);{var de=k=>{var O=gi();l(k,O)},Ve=k=>{var O=bi(),le=Y(O),Re=a(le),We=r(le,2),we=a(We),d=r(a(we));at(d,21,()=>e(_),ma,(x,$,f)=>{var E=_i(),z=a(E),ye=a(z),ie=r(z),ke=a(ie);Ue(ke,"aria-label",`Libellé du tarif ${f+1}`);var Oe=r(ie),ve=a(Oe);Ue(ve,"aria-label",`Abrégé du tarif ${f+1}`);var K=r(Oe),Ae=a(K);{var Ge=pe=>{var je=mi();l(pe,je)},Fe=pe=>{var je=Ma(),$e=a(je);v(()=>i($e,`${e($).written??""} — le tarif de référence est le prix du catalogue : il
+ ne peut pas porter de remise, et l’enregistrement la refusera.`)),l(pe,je)},De=pe=>{var je=Ma(),$e=a(je);v(()=>i($e,`${e($).written??""} — une remise s’écrit au dixième de point ; celle-ci se
+ change dans le fichier de configuration.`)),l(pe,je)},Me=pe=>{var je=hi(),$e=Y(je);Ue($e,"aria-label",`Remise du tarif ${f+1}`);var st=r($e,2),it=a(st);v((pt,Ee)=>{oa($e,pt),i(it,`un produit à 10,00 €/kg s’affiche ${Ee??""} €/kg`)},[()=>Z(e($).discount??0),()=>C(e($).discount??0)]),Ze("input",$e,pt=>Q(`pricing.tiers.${String(f)}.discount_percent`,pt.currentTarget.value)),Ze("focusout",$e,pt=>ue(pt.currentTarget,Z(e($).discount??0))),l(pe,je)};g(Ae,pe=>{e($).code===e(be)&&e($).written===null?pe(Ge):e($).code===e(be)?pe(Fe,1):e($).written!==null&&e($).discount===null?pe(De,2):pe(Me,-1)})}var ae=r(K),Ne=a(ae);v(()=>{i(ye,e($).code),oa(ke,e($).label),oa(ve,e($).abbrev),i(Ne,e($).rank)}),Ze("input",ke,pe=>t.draft.set(`pricing.tiers.${String(f)}.label`,pe.currentTarget.value)),Ze("input",ve,pe=>t.draft.set(`pricing.tiers.${String(f)}.abbrev`,pe.currentTarget.value)),l(x,E)}),v(x=>i(Re,`${x??""}.`),[()=>se(e(_).length)]),l(k,O)};g(te,k=>{e(_).length===0?k(de):k(Ve,-1)})}var S=r(te,2);{let k=u(()=>t.draft.text("pricing.primary_code"));_t(S,{label:"Tarif imprimé en grand",path:"pricing.primary_code",get value(){return e(k)},hint:"Le prix que le client lit sur l’étiquette (A7).",onchange:O=>t.draft.set("pricing.primary_code",O)})}var h=r(S,2);{let k=u(()=>t.draft.text("pricing.reference_code"));_t(h,{label:"Tarif qui serait encodé si le code-barres portait un prix",path:"pricing.reference_code",get value(){return e(k)},hint:"Le plan livré ne porte pas de prix, mais un poids ou un nombre d’unités : la caisse retrouve le prix par la référence, dans Odoo, et ne doit jamais sous-facturer.",onchange:O=>t.draft.set("pricing.reference_code",O)})}l(y,W)},$$slots:{default:!0}});var ze=r(he,2);Qe(ze,{title:"Les deux arrondis",children:(y,I)=>{var W=yi(),te=Y(W);{let Ve=u(()=>t.draft.text("pricing.unit_price_rounding"));_t(te,{label:"Arrondi du prix unitaire dérivé",path:"pricing.unit_price_rounding",get value(){return e(Ve)},hint:"Il s’applique au prix au kilo calculé par la remise.",onchange:S=>t.draft.set("pricing.unit_price_rounding",S)})}var de=r(te,2);{let Ve=u(()=>t.draft.text("pricing.amount_rounding"));_t(de,{label:"Arrondi du montant",path:"pricing.amount_rounding",get value(){return e(Ve)},hint:"Il s’applique au montant de l’étiquette.",onchange:S=>t.draft.set("pricing.amount_rounding",S)})}l(y,W)},$$slots:{default:!0}});var Ce=r(ze,2);Qe(Ce,{title:"Les quatorze garde-fous, dans l’ordre d’évaluation",note:"Le seuil se modifie ici. La sévérité ne se règle pas : elle dit si le poste refuse ou avertit, et cela ne dépend pas du magasin. Le message affiché au client n’est pas encore modifiable depuis cet écran.",children:(y,I)=>{var W=Ci(),te=r(Y(W),2);at(te,21,()=>N,O=>O.code,(O,le)=>{var Re=Li(),We=a(Re),we=a(We),d=a(we),x=r(we,2),$=a(x),f=r(x,2),E=a(f),z=r(f,2),ye=a(z),ie=r(We,2),ke=a(ie),Oe=r(ie,2);{var ve=ae=>{var Ne=xi();l(ae,Ne)},K=ae=>{var Ne=ki(),pe=a(Ne);v(()=>i(pe,`« ${e(le).message??""} »`)),l(ae,Ne)};g(Oe,ae=>{e(le).message===""?ae(ve):ae(K,-1)})}var Ae=r(Oe,2);{var Ge=ae=>{s(ae,()=>e(le).switchPath,()=>e(le).switchLabel,()=>"")};g(Ae,ae=>{e(le).switchPath!==""&&ae(Ge)})}var Fe=r(Ae,2);at(Fe,17,()=>e(le).thresholds,ae=>ae.path,(ae,Ne)=>{var pe=qi(),je=a(pe);{let $e=u(()=>t.draft.text(e(Ne).path));_t(je,{get label(){return e(Ne).label},get path(){return e(Ne).path},kind:"number",get value(){return e($e)},get hint(){return e(Ne).hint},onchange:st=>me(e(Ne).path,st)})}Ze("focusout",pe,$e=>ue($e.target,t.draft.text(e(Ne).path))),l(ae,pe)});var De=r(Fe,2);{var Me=ae=>{var Ne=Si(),pe=a(Ne);v(()=>i(pe,e(le).note)),l(ae,Ne)};g(De,ae=>{e(le).note!==""&&ae(Me)})}v(ae=>{Ue(Re,"data-code",e(le).code),i(d,e(le).rank),i($,e(le).label),i(E,e(le).code),Ue(z,"data-blocking",ae),i(ye,e(le).severity),i(ke,e(le).when)},[()=>String(e(le).blocking)]),l(O,Re)});var de=r(te,4),Ve=a(de),S=r(de,4);{var h=O=>{var le=Ei();l(O,le)},k=O=>{var le=Ti(),Re=Y(le),We=a(Re),we=r(Re,2);at(we,21,()=>e(w),d=>d.code,(d,x)=>{var $=Pi(),f=a($),E=a(f),z=r(f,2),ye=a(z),ie=r(z,2),ke=a(ie),Oe=r(ie,2);{var ve=Ge=>{var Fe=zi(),De=a(Fe);v(()=>i(De,`« ${e(x).message??""} »`)),l(Ge,Fe)};g(Oe,Ge=>{e(x).message!==""&&Ge(ve)})}var K=r(Oe,2);{var Ae=Ge=>{var Fe=ji(),De=Y(Fe),Me=a(De),ae=r(De,2),Ne=a(ae);v(pe=>{i(Me,pe),i(Ne,e(x).product_id)},[()=>P(e(x).product_id)]),l(Ge,Fe)};g(K,Ge=>{e(x).product_id!==""&&Ge(Ae)})}v((Ge,Fe,De)=>{Ue($,"data-blocking",Ge),i(E,Fe),i(ye,e(x).code),i(ke,De)},[()=>String(e(x).blocking),()=>L(e(x).code),()=>D(e(x).severity)]),l(d,$)}),v(d=>{Ue(Re,"data-verdicts",d),i(We,e(V))},[()=>String(e(w).length)]),l(O,le)};g(S,O=>{e(w).length===0?O(h):O(k,-1)})}v(O=>i(Ve,`Les marqueurs ${O??""} sont remplacés par les valeurs de la pesée au
+ moment où le message s’affiche.`),[()=>q.join(" et ")]),l(y,W)},$$slots:{default:!0}});var Le=r(Ce,2);Qe(Le,{title:"Code-barres",note:"Un seul réglage, et c’est voulu : le reste du plan de numérotation n’est pas de la configuration.",children:(y,I)=>{var W=Oi(),te=Y(W);s(te,()=>"barcode.verify_reference_check_digit",()=>"Refuser une référence dont la clé de contrôle est fausse",()=>"Décoché, le poste recalcule une clé juste sur une référence fausse, en silence — et la caisse encaisse un autre article."),l(y,W)},$$slots:{default:!0}});var ee=r(Le,2);Qe(ee,{title:"Dérogations de poids minimum",note:"En lecture ici ; elles se modifient depuis l’onglet Catalogue, là où se trouve le produit.",children:(y,I)=>{var W=Ut(),te=Y(W);{var de=S=>{var h=Ri();l(S,h)},Ve=S=>{var h=Di(),k=Y(h),O=a(k),le=r(k,2);{var Re=we=>{var d=Ai();l(we,d)};g(le,we=>{e(J)==="unread"&&we(Re)})}var We=r(le,2);at(We,21,()=>e(M),we=>we.product_id,(we,d)=>{var x=$i(),$=a(x),f=a($),E=r($,2),z=a(E),ye=r(E,2),ie=a(ye),ke=r(ye,2),Oe=a(ke),ve=r(ke,2),K=a(ve),Ae=r(ve,2);{var Ge=Fe=>{var De=Ni();l(Fe,De)};g(Ae,Fe=>{e(d).offered||Fe(Ge)})}v((Fe,De,Me,ae)=>{Ue(x,"data-withdrawn",Fe),i(f,De),i(z,e(d).product_id),i(ie,Me),i(Oe,e(d).reason),i(K,`${ae??""}, ${e(d).decided_by??""}`)},[()=>String(!e(d).offered),()=>P(e(d).product_id),()=>ne(e(d).min_weight_g??0),()=>pa(e(d).decided_at)]),l(we,x)}),v(()=>i(O,e(B))),l(S,h)};g(te,S=>{e(T).length===0?S(de):S(Ve,-1)})}l(y,W)},$$slots:{default:!0}}),l(n,X),$t()}At(["change","input","focusout"]);function Fi(n,t){const s=[];for(const c of[...qa(n),...qa(t)]){if(s.some(T=>T.path===c))continue;const _=Ba(fa(n,c)),w=Ba(fa(t,c));_!==w&&s.push({path:c,before:_,after:w})}return s}function fa(n,t){let s=n;for(const c of t.split(".")){if(s===null||typeof s!="object")return;s=s[c]}return s}function qa(n,t=""){if(n===null||typeof n!="object"||Array.isArray(n))return t===""?[]:[t];const s=[];for(const[c,_]of Object.entries(n))s.push(...qa(_,t===""?c:`${t}.${c}`));return s}function Ba(n){return n===void 0?"—":n===null?"vide":typeof n=="boolean"?n?"oui":"non":typeof n=="object"?JSON.stringify(n):String(n)}var Mi=o('
Empreinte de la configuration en service
Version du binaire
Répertoire de données
Espace disque
',1),Bi=o('
'),Va=o(""),Vi=o(''),Wi=o("
"),Gi=o(`
Recopier reste possible : les valeurs entrent dans le brouillon, où elles se
+ corrigent champ par champ avant l’enregistrement.
`),Hi=o(`
Ce fichier n’a été comparé à rien : la configuration en service n’a pas pu être
+ lue. Ni « identique », ni « n champs changent » — ce que ce fichier changerait
+ sur ce poste reste inconnu.
`),Ji=o(`
Ce fichier décrit la même configuration que celle en service : il n’y a rien à
+ recopier. C’est ce qu’on veut lire à la fin d’un clonage. Deux champs ne sont pas
+ comparés : la date du dernier enregistrement, que chaque poste écrit lui-même, et
+ le mot de passe du catalogue, qu’aucun des deux ne porte en clair.
`),Ki=o('
'),Xi=o('
'),Yi=o(`
Champ
En service
Dans le fichier
Recopier n’applique rien : les valeurs entrent dans le brouillon, et c’est
+ « Enregistrer » qui les met en service.
`,1),Qi=o('
',1),Zi=o(`
L’export emporte encore l’empreinte du mot de passe : c’est la seule lecture que le
+ poste garde derrière la clé. L’import, lui, est lu PAR LE POSTE, qui écarte les deux
+ secrets et le numéro de poste avant de dire ce qui changerait.
`,1),eo=o('
Lecture des versions enregistrées…
'),to=o(`
Les versions enregistrées n’ont pas pu être lues : cette liste ne dit donc rien de
+ ce que ce poste garde. Ce n’est pas « aucune version ».
`),ao=o('
Aucune version enregistrée : ce poste n’a jamais été reconfiguré.
'),ro=o('
'),no=o(`
Remettre une version en service remplace la configuration du poste sur-le-champ, et
+ ce qui n’a pas été enregistré est perdu : c’est le seul geste de cette page qui
+ change le poste tout de suite, et le seul qui garde ses 72 px.
`,1),so=o('
');function lo(n,t){Nt(t,!0);const s=vt(t,"admin",7),c=40,_=20,w=5,T=new Set(["modified_at","catalog.options.password"]),A=[{path:"station.name",name:"le nom du poste"},{path:"network.listen",name:"l’adresse d’écoute"},{path:"scale.options.port",name:"le port de la balance"},{path:"printer.options.queue",name:"la file d’impression"},{path:"catalog.options.url",name:"l’adresse du partage"},{path:"catalog.options.username",name:"le compte du partage"},{path:"catalog.images.path",name:"le chemin des images"}];let M=F(Lt([])),H=F("reading"),J=F(null),fe=F(""),ge=F(null),be=F(""),Z=F(Lt([])),C=F(""),me="";const Q=u(()=>s().busy||e(C)!==""),ue=u(()=>D(e(J),e(ge))),re=u(()=>e(J)!==null&&e(ge)!==null),P=u(()=>X(e(J),e(ge))),se=u(()=>e(ue).slice(0,c)),V=u(()=>e(Z).slice(0,_)),B=u(()=>e(M).slice(0,w)),ne=u(()=>We(e(se).length,e(ue).length,"champ qui change","champs qui changent")),N=u(()=>We(e(V).length,e(Z).length,"contrôle refuse une clé","contrôles refusent une clé")),L=u(()=>We(e(B).length,e(M).length,"version enregistrée","versions enregistrées")),q=u(()=>e(ue).length>1?`Recopier ces ${_e(e(ue).length)} champs dans le brouillon`:"Recopier ce champ dans le brouillon");Ce();function D(f,E){return f===null||E===null?[]:Fi(f,E).filter(z=>!T.has(z.path))}function X(f,E){if(f===null||E===null)return[];const z=f,ye=E;return A.filter(ie=>he(ye,ie.path)&&!he(z,ie.path))}function he(f,E){const z=fa(f,E);return z==null||z===""?!0:typeof z!="object"||Array.isArray(z)?!1:Object.keys(z).length===0}function ze(f){return f.length<2?f.join(""):`${f.slice(0,-1).join(", ")} et ${f[f.length-1]??""}`}Xt(()=>{const f=t.health.config_fingerprint;f!==me&&(me=f,Le())});async function Ce(){const f=await s().load(Mr);if(f===null){p(H,"unreadable");return}p(M,f,!0),p(H,"read")}async function Le(){try{p(J,(await za()).config,!0),p(fe,"")}catch(f){p(J,null),p(fe,f instanceof Error?f.message:"Le poste n’a pas répondu.",!0)}}async function ee(f){p(C,f?"export-all":"export-clone",!0),s().notice="",s().actionError="";try{const E=await s().protect(()=>y(f));if(E===null)return;te(E.name,E.blob),s().notice=`${E.name} est remis au navigateur : c’est lui qui l’enregistre, voyez ses téléchargements.`}finally{p(C,"")}}async function y(f){const z=await fetch(`/admin/api/config/export?hardware=${f?"1":"0"}`,{headers:{accept:"application/json"}});if(!z.ok)throw new Ft(z.status,O(await z.text(),"L’export"));return{name:I(f),blob:await z.blob()}}function I(f){const E=f?"":"-sans-materiel";return`config-poste${String(t.health.station)}${E}-${W()}.json`}function W(){const f=new Date,E=String(f.getMonth()+1).padStart(2,"0"),z=String(f.getDate()).padStart(2,"0");return`${String(f.getFullYear())}-${E}-${z}`}function te(f,E){const z=URL.createObjectURL(E),ye=document.createElement("a");ye.href=z,ye.download=f,document.body.appendChild(ye),ye.click(),ye.remove(),setTimeout(()=>{URL.revokeObjectURL(z)},0)}async function de(f){const E=await fetch("/admin/api/config/import",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(f)}),z=await E.text();if(!E.ok)throw new Ft(E.status,O(z,"L’import"));return JSON.parse(z)}async function Ve(f){if(f==null)return;s().notice="",s().actionError="";let E;try{E=JSON.parse(await f.text())}catch{s().actionError=`${f.name} n’est pas un fichier JSON lisible.`;return}p(C,"import");try{const z=await s().protect(()=>de(E));if(z===null)return;p(ge,z.config,!0),p(be,f.name,!0),p(Z,z.faults,!0),await Le(),s().notice=S(f.name)}finally{p(C,"")}}function S(f){return e(re)?e(ue).length===0?`${f} décrit la même configuration que celle en service.`:`${f} est lu. Rien n’est appliqué : relisez le tableau.`:`${f} est lu, mais la configuration en service ne l’est pas : rien ne peut être comparé.`}async function h(){if(e(ge)===null)return;const f=e(ge),E=[...e(ue)];for(const z of E)t.draft.set(z.path,fa(f,z.path));s().actionError="",s().notice="Le fichier est recopié dans le brouillon. Rien n’est appliqué avant « Enregistrer ».",await Le()}async function k(f){p(C,`restore-${String(f)}`),s().notice="",s().actionError="";try{if(await s().protect(()=>Br(f))===null)return;await t.draft.load(),await Ce(),await Le(),await s().refresh(),s().notice=`La version ${String(f)} est remise en service.`}finally{p(C,"")}}function O(f,E){try{const z=JSON.parse(f);if(typeof z?.message=="string"&&z.message!=="")return z.message}catch{}return`${E} a été refusé par le poste.`}function le(f){return t.draft.faults.find(E=>E.field===f)?.message??""}function Re(f){return t.draft.faults.find(E=>E.field===f)?.allowed??[]}function We(f,E,z,ye){const ie=E>1?ye:z;return f>=E?`${_e(E)} ${ie}.`:`${_e(f)} lignes affichées sur ${_e(E)} ${ie}.`}var we=so(),d=a(we);Qe(d,{title:"Identité du poste",children:(f,E)=>{var z=Mi(),ye=Y(z);{let je=u(()=>t.draft.text("station.number")),$e=u(()=>le("station.number")),st=u(()=>Re("station.number"));_t(ye,{label:"Numéro du poste",path:"station.number",kind:"number",get value(){return e(je)},get fault(){return e($e)},get allowed(){return e(st)},hint:"C’est de lui que dérive le nom du fichier de catalogue attendu, flv_.csv.",onchange:it=>t.draft.set("station.number",Number(it))})}var ie=r(ye,2);{let je=u(()=>t.draft.text("station.name")),$e=u(()=>le("station.name")),st=u(()=>Re("station.name"));_t(ie,{label:"Nom du poste",path:"station.name",get value(){return e(je)},get fault(){return e($e)},get allowed(){return e(st)},hint:"Ce que lit un bénévole : « Poste 2 — fruits ».",onchange:it=>t.draft.set("station.name",it)})}var ke=r(ie,2);{let je=u(()=>t.draft.text("station.coop")),$e=u(()=>le("station.coop")),st=u(()=>Re("station.coop"));_t(ke,{label:"Coopérative",path:"station.coop",get value(){return e(je)},get fault(){return e($e)},get allowed(){return e(st)},onchange:it=>t.draft.set("station.coop",it)})}var Oe=r(ke,2),ve=r(a(Oe),2),K=a(ve),Ae=r(ve,4),Ge=a(Ae),Fe=r(Ae,4),De=a(Fe),Me=r(Fe,4),ae=a(Me);{var Ne=je=>{var $e=xt("non mesuré");l(je,$e)},pe=je=>{var $e=xt();v((st,it,pt)=>i($e,`${st??""} libres sur
+ ${it??""} — seuil d’alerte
+ ${pt??""} Mo`),[()=>ka(t.health.disk.free_bytes),()=>ka(t.health.disk.total_bytes),()=>_e(t.health.disk.alert_mb)]),l(je,$e)};g(ae,je=>{t.health.disk===null?je(Ne):je(pe,-1)})}v(()=>{i(K,t.health.config_fingerprint),i(Ge,t.health.version),i(De,t.health.disk===null?"non publié par ce poste":t.health.disk.path)}),l(f,z)},$$slots:{default:!0}});var x=r(d,2);Qe(x,{title:"Exporter, importer",note:"Pour installer un autre poste : ce fichier emporte les tarifs, les garde-fous, l’étiquette, les catégories, et les réglages du matériel que les quatre postes partagent — le décalage d’étiquette, le noircissement, la vitesse, le débit de la balance. Reste ici ce qui désigne ce poste-ci ou ce magasin — le mot de passe, le code de secours, le numéro et le nom du poste, le port de la balance, la file d’impression, l’adresse du partage et son compte, le chemin des images et le réseau.",children:(f,E)=>{var z=Zi(),ye=Y(z),ie=a(ye);{let ae=u(()=>e(C)==="export-all");tt(ie,{label:"Exporter tout",protected:!0,get busy(){return e(ae)},get disabled(){return e(Q)},onrun:()=>{ee(!0)}})}var ke=r(ie,2);{let ae=u(()=>e(C)==="export-clone");tt(ke,{label:"Exporter sans le matériel",protected:!0,get busy(){return e(ae)},get disabled(){return e(Q)},onrun:()=>{ee(!1)}})}var Oe=r(ke,2);let ve;var K=a(Oe),Ae=r(K,3),Ge=r(ye,4);{var Fe=ae=>{var Ne=Bi(),pe=a(Ne);v(()=>i(pe,`La configuration en service n’a pas pu être lue : ${e(fe)??""} La colonne
+ « En service » ne peut donc rien affirmer.`)),l(ae,Ne)};g(Ge,ae=>{e(fe)!==""&&ae(Fe)})}var De=r(Ge,2);{var Me=ae=>{var Ne=Qi(),pe=Y(Ne),je=a(pe),$e=r(pe,2);{var st=et=>{var mt=Gi(),Dt=a(mt),Bt=a(Dt),Vt=r(Dt,2);at(Vt,21,()=>e(V),ma,(Et,bt)=>{var Tt=Wi(),b=a(Tt),G=a(b),oe=r(b,2);{var qe=j=>{var U=Va(),xe=a(U);v(()=>i(xe,e(bt).field)),l(j,U)};g(oe,j=>{Ht.showTechnicalNames&&j(qe)})}var Je=r(oe),Se=r(Je);{var Pe=j=>{var U=Vi(),xe=a(U);v(Xe=>i(xe,`Valeurs acceptées : ${Xe??""}.`),[()=>e(bt).allowed.join(", ")]),l(j,U)};g(Se,j=>{e(bt).allowed!==void 0&&e(bt).allowed.length>0&&j(Pe)})}v(j=>{i(G,j),i(Je,` ${e(bt).message??""} `)},[()=>Gt(e(bt).field)]),l(Et,Tt)}),v(()=>i(Bt,`Ce fichier serait refusé en l’état : ${e(N)??""}`)),l(et,mt)};g($e,et=>{e(Z).length>0&&et(st)})}var it=r($e,2);{var pt=et=>{var mt=Hi();l(et,mt)},Ee=et=>{var mt=Ji();l(et,mt)},nt=et=>{var mt=Yi(),Dt=Y(mt),Bt=a(Dt),Vt=r(Bt);{var Et=Se=>{var Pe=xt("Les autres sont dans le fichier, et « Recopier » les prend tous.");l(Se,Pe)};g(Vt,Se=>{e(ue).length>e(se).length&&Se(Et)})}var bt=r(Dt,2),Tt=a(bt),b=r(a(Tt));at(b,21,()=>e(se),Se=>Se.path,(Se,Pe)=>{const j=u(()=>Gt(e(Pe).path));var U=Ki(),xe=a(U),Xe=a(xe),Ke=r(Xe);{var Ye=dt=>{var ot=Va(),ut=a(ot);v(()=>i(ut,e(Pe).path)),l(dt,ot)};g(Ke,dt=>{Ht.showTechnicalNames&&e(j)!==e(Pe).path&&dt(Ye)})}var ce=r(xe),Ie=a(ce),Be=r(ce),ct=a(Be);v(()=>{Ue(U,"data-path",e(Pe).path),i(Xe,`${e(j)??""} `),i(Ie,e(Pe).before),i(ct,e(Pe).after)}),l(Se,U)});var G=r(bt,2);{var oe=Se=>{var Pe=Xi(),j=a(Pe);v(U=>i(j,`Ce fichier ne porte pas ${U??""} :
+ l’export sans le matériel les retire, et l’import ne remet que le numéro du
+ poste. Les lignes correspondantes sont VIDES ci-dessus, et
+ « Recopier » recopie ce vide dans le brouillon.`),[()=>ze(e(P).map(U=>U.name))]),l(Se,Pe)};g(G,Se=>{e(P).length>0&&Se(oe)})}var qe=r(G,2),Je=a(qe);tt(Je,{kind:"write",get label(){return e(q)},get disabled(){return e(Q)},onrun:()=>{h()}}),v(()=>i(Bt,`${e(ne)??""} `)),l(et,mt)};g(it,et=>{e(re)?e(ue).length===0?et(Ee,1):et(nt,-1):et(pt)})}v(()=>i(je,`Fichier lu : ${e(be)??""}`)),l(ae,Ne)};g(De,ae=>{e(ge)!==null&&ae(Me)})}v(()=>{ve=Zt(Oe,1,"choose svelte-18wbxwu",null,ve,{working:e(C)==="import",off:e(Q)}),i(K,`${e(C)==="import"?"Lecture du fichier…":"Importer un fichier"} `),Ae.disabled=e(Q)}),Ze("change",Ae,ae=>{Ve(ae.currentTarget.files?.item(0))}),l(f,z)},$$slots:{default:!0}});var $=r(x,2);Qe($,{title:"Cinq versions restaurables",note:"Chaque enregistrement fait tourner les versions : la plus récente est la 1.",children:(f,E)=>{var z=Ut(),ye=Y(z);{var ie=K=>{var Ae=eo();l(K,Ae)},ke=K=>{var Ae=to();l(K,Ae)},Oe=K=>{var Ae=ao();l(K,Ae)},ve=K=>{var Ae=no(),Ge=Y(Ae),Fe=a(Ge),De=r(Ge,2),Me=a(De);at(Me,21,()=>e(B),ae=>ae.version,(ae,Ne)=>{var pe=ro(),je=a(pe),$e=a(je),st=r(je,2),it=a(st),pt=r(st,2),Ee=a(pt),nt=r(pt,2);{let et=u(()=>e(C)===`restore-${String(e(Ne).version)}`);tt(nt,{kind:"destructive",label:"Remettre cette version en service",protected:!0,get busy(){return e(et)},get disabled(){return e(Q)},onrun:()=>{k(e(Ne).version)}})}v((et,mt)=>{i($e,`version ${et??""}`),i(it,mt),i(Ee,e(Ne).config_fingerprint)},[()=>_e(e(Ne).version),()=>Mt(e(Ne).modified_at)]),l(ae,pe)}),v(()=>i(Fe,`${e(L)??""} Le poste n’en garde jamais plus de cinq.`)),l(K,Ae)};g(ye,K=>{e(H)==="reading"?K(ie):e(H)==="unreadable"?K(ke,1):e(M).length===0?K(Oe,2):K(ve,-1)})}l(f,z)},$$slots:{default:!0}}),l(n,we),$t()}At(["change"]);var io=o('clé'),oo=o(''),uo=o('');function aa(n,t){const s=vt(t,"kind",3,"read"),c=vt(t,"hint",3,""),_=vt(t,"disabled",3,!1),w=vt(t,"engaged",3,!1),T=vt(t,"busy",3,!1),A=vt(t,"protected",3,!1);var M=uo();let H;var J=a(M),fe=a(J),ge=r(fe);{var be=me=>{var Q=io();l(me,Q)};g(ge,me=>{A()&&me(be)})}var Z=r(J,2);{var C=me=>{var Q=oo(),ue=a(Q);v(()=>i(ue,T()?"En cours…":c())),l(me,Q)};g(Z,me=>{c()!==""&&me(C)})}v(()=>{H=Zt(M,1,`big touch-target ${s()??""}`,"svelte-15nlt6i",H,{engaged:w(),busy:T()}),Ue(M,"data-kind",s()),M.disabled=_()||T(),i(fe,`${t.label??""} `)}),Ze("click",M,function(...me){t.onrun?.apply(this,me)}),l(n,M)}At(["click"]);const co="ERR-SCL-09";var vo=o('
');function _o(n,t){Nt(t,!0);const s=vt(t,"admin",7);let c=F("");const _=u(()=>t.health.state.degraded?.code===co),w=u(()=>t.health.printing),T=u(()=>e(w)?.on_fallback===!0);let A=F(!1),M=F("");const H=u(()=>s().busy||e(M)!==""),J=new ir;Xt(()=>{J.observe(t.health)});const fe=u(()=>t.health.catalog_source===null?"Ce poste ne publie pas la source de son catalogue.":"Catalogue surveillé : "+t.health.catalog_source.label),ge=u(()=>t.health.catalog!==null&&(t.health.catalog.result==="rejected"||t.health.catalog.result==="failed")?t.health.catalog:null);function be(h){p(c,""),J.forget(),p(M,h,!0)}async function Z(h,k){be(h);try{await s().run(k)}finally{p(M,"")}}async function C(){be("reload");try{const h=await s().run(jr);h!==null&&J.begin(h)}finally{p(M,"")}}async function me(h,k){be(h);try{const O=await s().load(k);O!==null&&p(c,O.message,!0)}finally{p(M,"")}}async function Q(h,k){be(h);try{const O=await s().protect(k);O!==null&&(s().notice=O.message,await s().refresh())}finally{p(M,"")}}async function ue(h){if(p(A,!1),h!=null){be("import");try{const k=await s().protect(()=>Ya(h));if(k===null)return;p(c,k.result==="rejected"||k.result==="failed"?`${h.name} : REFUSÉ${k.reason===""?"":" — "+k.reason}. Le catalogue en service n’a pas changé.`:`${h.name} : ${String(k.rows_read_count)} lignes lues, ${String(k.weighable_count)} pesables. La veille l’appliquera dans la seconde.`,!0),await s().refresh()}finally{p(M,"")}}}var re=ho(),P=a(re);{var se=h=>{var k=vo(),O=a(k);v(()=>i(O,e(c))),l(h,k)};g(P,h=>{e(c)!==""&&h(se)})}var V=r(P,2);{var B=h=>{var k=po(),O=a(k);v(()=>i(O,J.sentence)),l(h,k)};g(V,h=>{J.sentence!==""&&h(B)})}var ne=r(V,2);{var N=h=>{var k=fo(),O=a(k);v(()=>i(O,e(w).banner)),l(h,k)};g(ne,h=>{e(w)!==null&&e(w).banner!==""&&h(N)})}var L=r(ne,2),q=a(L),D=r(L,2),X=a(D);{let h=u(()=>e(M)==="scale");aa(X,{label:"Tester la balance",hint:"Ce que le poste a déjà observé — le port n’est pas rouvert.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{me("scale",Or)}})}var he=r(X,2);{let h=u(()=>e(M)==="printer");aa(he,{label:"Tester l’imprimante",hint:"Ce que le superviseur a vu il y a moins d’une seconde.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{me("printer",Rr)}})}var ze=r(he,2);{let h=u(()=>e(M)==="label");aa(ze,{label:"Imprimer une étiquette de test",hint:"Une étiquette de démonstration sort de l’imprimante du poste.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{Z("label",Ar)}})}var Ce=r(ze,2);{let h=u(()=>e(M)==="reprint");aa(Ce,{label:"Réimprimer la dernière",hint:"La dernière étiquette imprimée sort une seconde fois.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{Z("reprint",zr)}})}var Le=r(Ce,2);{let h=u(()=>e(M)==="reload");aa(Le,{label:"Recharger le catalogue",kind:"write",hint:"La veille refait tout de suite le contrôle qu’elle fait toutes les cinq secondes.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{C()}})}var ee=r(Le,2);{let h=u(()=>e(_)?"Revenir à la balance":"Basculer en saisie manuelle"),k=u(()=>e(_)?"Le poids sera de nouveau lu sur la balance.":"Le poids se tape à la main : le poste continue de servir sans balance."),O=u(()=>e(M)==="manual");aa(ee,{get label(){return e(h)},kind:"destructive",get hint(){return e(k)},get engaged(){return e(_)},protected:!0,get busy(){return e(O)},get disabled(){return e(H)},onrun:()=>{Q("manual",()=>Pr(!e(_)))}})}var y=r(ee,2);{let h=u(()=>e(M)==="roll");aa(y,{label:"J’ai changé le rouleau",kind:"write",hint:"Le compteur d’étiquettes repart à zéro. C’est le seul geste qui dise quelque chose de vrai du papier.",get busy(){return e(h)},get disabled(){return e(H)},onrun:()=>{Z("roll",Tr)}})}var I=r(y,2);{var W=h=>{{let k=u(()=>e(T)?"Revenir à l’imprimante du poste":"Imprimer sur l’imprimante du poste voisin"),O=u(()=>e(T)?"Les étiquettes repartiront sur l’imprimante de ce poste.":"Les étiquettes sortiront sur l’imprimante voisine, pour cette session seulement."),le=u(()=>e(M)==="fallback");aa(h,{get label(){return e(k)},kind:"write",get hint(){return e(O)},get engaged(){return e(T)},get busy(){return e(le)},get disabled(){return e(H)},onrun:()=>{Z("fallback",()=>Cr(!e(T)))}})}};g(I,h=>{e(w)!==null&&e(w).fallback_available&&h(W)})}var te=r(I,2),de=r(D,2);Qe(de,{title:"Importer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(h,k)=>{var O=go();let le;var Re=a(O),We=r(a(Re)),we=a(We),d=r(Re,2),x=r(a(d));v(()=>{le=Zt(O,1,"drop svelte-1yd0nzg",null,le,{dropping:e(A)}),i(we,`flv_${t.health.station??""}.csv`)}),na("dragover",O,$=>{$.preventDefault(),p(A,!0)}),na("dragleave",O,()=>p(A,!1)),na("drop",O,$=>{$.preventDefault(),ue($.dataTransfer?.files.item(0))}),Ze("change",x,$=>{ue($.currentTarget.files?.item(0))}),l(h,O)},$$slots:{default:!0}});var Ve=r(de,2);{var S=h=>{Qe(h,{title:"Le dernier fichier n’a pas pris service",children:(k,O)=>{var le=mo(),Re=Y(le),We=a(Re),we=r(Re,2),d=a(we);v((x,$)=>{i(We,`${x??""} —
+ ${(e(ge).reason===""?"aucun motif enregistré.":e(ge).reason)??""}
+ Le catalogue en service n’a pas changé.`),i(d,`Dernier essai : ${$??""}.`)},[()=>ha(e(ge).result),()=>Mt(e(ge).occurred_at)]),l(k,le)},$$slots:{default:!0}})};g(Ve,h=>{e(ge)!==null&&h(S)})}v(()=>{i(q,e(fe)),Ue(te,"href",Nr)}),l(n,re),$t()}At(["change"]);const bo={succeeded:"La dernière mise à jour a réussi.","rolled-back":"La dernière mise à jour a échoué. La version précédente a été remise et le poste fonctionne.","rolled-back-unhealthy":"La dernière mise à jour a échoué et le poste n’a pas redémarré. Appelez le support.","not-started":"La dernière mise à jour n’a pas démarré : rien n’a été remplacé. Vous pouvez réessayer."},wo=2e3,yo=300*1e3;var xo=o('
'),Wa=o('
'),ko=o("
Dernière vérification
",1),qo=o(`
La mise à jour depuis cet écran n’existe que sur les postes Windows. Sur les
+ autres, elle se fait à la main — voir la notice d’installation.
Le poste va s’arrêter environ une minute. L’écran client s’éteindra puis reviendra
+ tout seul. Si la nouvelle version ne démarre pas, la précédente sera remise
+ automatiquement — mais les données enregistrées, elles, ne reviendront pas en
+ arrière.
`),Po=o('
Mise à jour en cours. Le poste redémarre, ne le débranchez pas.
'),To=o('
',1),Co=o("
Terminée le
",1),Oo=o("
Raison
",1),Ro=o('
Depuis
Vers
',1),Ao=o(" ",1);function No(n,t){Nt(t,!0);let s=F(null),c=F(""),_=F(""),w=F(!1),T=F("");const A=u(()=>e(s)?.outcome??null),M=u(()=>e(s)?.supported===!0&&e(s).available),H=u(()=>e(s)?.latest??"");Xt(()=>{J()});async function J(){try{p(s,await nn(),!0),p(c,"")}catch(P){p(c,P instanceof Error?P.message:"Lecture impossible.",!0)}}async function fe(){p(_,"check");try{const P=await t.admin.protect(()=>sn());P!==null&&(p(s,P,!0),p(c,""))}finally{p(_,"")}}async function ge(P){p(_,"apply"),p(T,"");try{if(await t.admin.protect(()=>ln(P))===null)return;p(w,!0),p(T,await be()?"":"Le poste n’a pas répondu dans les cinq minutes. Allez le voir.",!0),await J()}finally{p(_,""),p(w,!1)}}async function be(){const P=Date.now()+yo;for(;Date.now()
');function Zo(n,t){Nt(t,!0);const s=new wn,c=new on(s),_=[{title:"Au quotidien",pages:[{id:"dashboard",label:"Tableau de bord"},{id:"troubleshooting",label:"Dépannage"}]},{title:"Réglages",pages:[{id:"hardware",label:"Matériel"},{id:"label",label:"Étiquette"},{id:"rules",label:"Règles"},{id:"catalog",label:"Catalogue"},{id:"journal",label:"Journal"},{id:"station",label:"Poste"},{id:"update",label:"Mise à jour"}]}],w=u(()=>_.flatMap(d=>d.pages).find(d=>d.id===s.page)?.label??""),T=u(()=>s.health===null||s.health.station_name===""?`Poste ${String(s.health?.station??"")}`:s.health.station_name),A=u(()=>(c.pending?.changed_blocks??[]).map(d=>vn(d)).join(", ")),M=u(()=>(c.pending?.changed_blocks??[]).join(", "));Ea(()=>(s.start(),()=>s.stop()));async function H(d){s.open(d),$a(d)&&c.config===null&&await c.load()}async function J(){await s.protect(()=>c.save())===!0&&(s.notice="La configuration est enregistrée et appliquée.")}var fe=Qo(),ge=a(fe),be=r(a(ge),2);at(be,17,()=>_,d=>d.title,(d,x)=>{var $=Do(),f=Y($),E=a(f),z=r(f,2);at(z,17,()=>e(x).pages,ye=>ye.id,(ye,ie)=>{var ke=$o();let Oe;var ve=a(ke);v(()=>{Oe=Zt(ke,1,"entry svelte-9b2mjq",null,Oe,{current:s.page===e(ie).id}),Ue(ke,"aria-current",s.page===e(ie).id?"page":void 0),i(ve,e(ie).label)}),Ze("click",ke,()=>{H(e(ie).id)}),l(ye,ke)}),v(()=>i(E,e(x).title)),l(d,$)});var Z=r(be,2),C=a(Z);{var me=d=>{var x=Io(),$=Y(x),f=a($),E=r($,2),z=a(E),ye=r(E,2),ie=a(ye);v(()=>{i(f,e(T)),i(z,`${s.health.coop??""} · version ${s.health.version??""}`),i(ie,`configuration ${s.health.config_fingerprint??""}`)}),l(d,x)};g(C,d=>{s.health!==null&&d(me)})}var Q=r(C,2),ue=a(Q),re=r(Q,2);{var P=d=>{var x=Uo();Ze("click",x,function(...$){t.onclose?.apply(this,$)}),l(d,x)};g(re,d=>{t.onclose!==void 0&&d(P)})}var se=r(ge,2),V=a(se);{var B=d=>{var x=Fo(),$=a(x);v(()=>i($,s.notice)),l(d,x)};g(V,d=>{s.notice!==""&&d(B)})}var ne=r(V,2);{var N=d=>{var x=Mo(),$=a(x);v(()=>i($,s.linkError)),l(d,x)};g(ne,d=>{s.linkError!==""&&d(N)})}var L=r(ne,2);{var q=d=>{var x=Bo(),$=a(x);v(()=>i($,s.actionError)),l(d,x)};g(L,d=>{s.actionError!==""&&d(q)})}var D=r(L,2);{var X=d=>{var x=Wo(),$=a(x),f=a($),E=r(f);{var z=ke=>{var Oe=Vo(),ve=a(Oe);v(()=>i(ve,e(M))),l(ke,Oe)};g(E,ke=>{Ht.showTechnicalNames&&ke(z)})}var ye=r(E),ie=r($,2);tt(ie,{kind:"write",label:"Tout fonctionne : confirmer",onrun:()=>{s.protect(()=>c.confirm())}}),v(()=>{i(f,`Configuration appliquée mais NON CONFIRMÉE. Ce qui a changé : ${e(A)??""}. `),i(ye,` Le poste reviendra tout seul à la version précédente dans
+ ${c.pending.seconds_left??""} secondes si personne ne confirme.`)}),l(d,x)};g(D,d=>{c.pending!==null&&d(X)})}var he=r(D,2),ze=a(he),Ce=a(ze),Le=r(ze,2);{var ee=d=>{var x=Go();l(d,x)},y=d=>{Sl(d,{get health(){return s.health},onshowrows:()=>{H("catalog")},onshowupdate:()=>{H("update")}})},I=d=>{_o(d,{get admin(){return s},get health(){return s.health}})},W=d=>{$l(d,{get admin(){return s},get draft(){return c},get health(){return s.health}})},te=d=>{di(d,{get admin(){return s},get draft(){return c}})},de=d=>{Ui(d,{get draft(){return c},get health(){return s.health}})},Ve=d=>{Is(d,{get admin(){return s},get draft(){return c},get health(){return s.health}})},S=d=>{ai(d,{get admin(){return s}})},h=d=>{lo(d,{get admin(){return s},get draft(){return c},get health(){return s.health}})},k=d=>{No(d,{get admin(){return s}})};g(Le,d=>{s.health===null?d(ee):s.page==="dashboard"?d(y,1):s.page==="troubleshooting"?d(I,2):s.page==="hardware"?d(W,3):s.page==="label"?d(te,4):s.page==="rules"?d(de,5):s.page==="catalog"?d(Ve,6):s.page==="journal"?d(S,7):s.page==="station"?d(h,8):s.page==="update"&&d(k,9)})}var O=r(he,2);{var le=d=>{var x=Yo(),$=a(x);{var f=ie=>{var ke=Ho(),Oe=a(ke),ve=r(Oe);at(ve,16,()=>c.retired,K=>K,(K,Ae)=>{{let Ge=u(()=>`retirer ${Ae}`);tt(K,{kind:"write",get label(){return e(Ge)},onrun:()=>c.dropRetired(Ae)})}}),v(K=>i(Oe,`Ce fichier porte des réglages que cette version du poste ne connaît plus :
+ ${K??""}. `),[()=>c.retired.map(K=>Gt(K)).join(", ")]),l(ie,ke)};g($,ie=>{c.retired.length>0&&ie(f)})}var E=r($,2);{var z=ie=>{var ke=Xo();at(ke,21,()=>c.faults,Oe=>Oe.field,(Oe,ve)=>{var K=Ko(),Ae=a(K),Ge=a(Ae),Fe=r(Ae,2);{var De=pe=>{var je=Jo(),$e=a(je);v(()=>i($e,e(ve).field)),l(pe,je)};g(Fe,pe=>{Ht.showTechnicalNames&&pe(De)})}var Me=r(Fe),ae=r(Me);{var Ne=pe=>{var je=xt();v($e=>i(je,`— valeurs acceptées : ${$e??""}`),[()=>e(ve).allowed.join(", ")]),l(pe,je)};g(ae,pe=>{e(ve).allowed!==void 0&&e(ve).allowed.length>0&&pe(Ne)})}v(pe=>{i(Ge,pe),i(Me,` ${e(ve).message??""} `)},[()=>Gt(e(ve).field)]),l(Oe,K)}),l(ie,ke)};g(E,ie=>{c.faults.length>0&&ie(z)})}var ye=r(E,2);{let ie=u(()=>c.dirty?"Enregistrer la configuration":"Aucune modification à enregistrer"),ke=u(()=>!c.dirty||s.busy);tt(ye,{kind:"write",get label(){return e(ie)},get disabled(){return e(ke)},onrun:()=>{J()}})}l(d,x)},Re=u(()=>$a(s.page)&&c.config!==null);g(O,d=>{e(Re)&&d(le)})}var We=r(se,2);{var we=d=>{En(d,{get admin(){return s}})};g(We,d=>{s.pending!==null&&d(we)})}v(()=>{la(ue,Ht.showTechnicalNames),i(Ce,e(w))}),Ze("change",ue,()=>Ht.toggleTechnicalNames()),l(n,fe),$t()}At(["click","change"]);let da=null;function au(n){if(da!==null||n.querySelector("[data-admin]")!==null)return;const t=document.createElement("div");n.appendChild(t),da={component:yr(Zo,{target:t,props:{onclose:eu}}),host:t}}function eu(){if(da===null)return;const{component:n,host:t}=da;da=null,xr(n),t.remove()}export{eu as closeAdmin,au as mountAdmin};
diff --git a/internal/web/dist/index.html b/internal/web/dist/index.html
index 059d488..0a4be6a 100644
--- a/internal/web/dist/index.html
+++ b/internal/web/dist/index.html
@@ -6,7 +6,7 @@
Pesée
-
+
diff --git a/web/src/admin/lib/dto.ts b/web/src/admin/lib/dto.ts
index d0445ac..14a3f63 100644
--- a/web/src/admin/lib/dto.ts
+++ b/web/src/admin/lib/dto.ts
@@ -224,6 +224,13 @@ export interface WeighingDTO {
export interface FindingDTO {
csv_line: number
product_id: string
+ /**
+ * Le nom commercial TEL QUE CET IMPORT L'A LU, instantané d'affichage.
+ *
+ * Vide dans les deux cas où le fichier n'en donne pas : un signalement qui ne porte sur
+ * aucun produit, et une ligne trop abîmée pour porter un nom.
+ */
+ product_name: string
code: string
issue: string
message: string
diff --git a/web/src/admin/pages/Catalog.svelte b/web/src/admin/pages/Catalog.svelte
index 1d76d3b..48bd0f9 100644
--- a/web/src/admin/pages/Catalog.svelte
+++ b/web/src/admin/pages/Catalog.svelte
@@ -20,9 +20,13 @@
*
* The list of not-weighable products is a NEUTRAL INVENTORY and never a list of
* errors: a prepackaged bulgur is not at fault, it is simply no business of a scale.
- * Anomalies, on the other hand, each carry their CSV line number, their reason in
- * French and the offending value — that is the difference between « 16 anomalies »
- * and a work plan somebody can follow in Odoo (§10.3 bis).
+ * Anomalies, on the other hand, each carry the NAME of the product, their CSV line
+ * number, their reason in French and the offending value — that is the difference
+ * between « 16 anomalies » and a work plan somebody can follow in Odoo (§10.3 bis).
+ * The name comes from the import itself and never from the catalog in service: it is
+ * the one the file carried, so the findings of an import from March keep saying what
+ * that import said, and a batch the station refused — whose products never entered the
+ * base at all — is named too.
*
* Six things this page owes ADR-033 and §14.4, and had not:
*
@@ -657,6 +661,19 @@
{#each list as finding}
diff --git a/web/test/admin-catalog.test.ts b/web/test/admin-catalog.test.ts
index fc391eb..8175fd8 100644
--- a/web/test/admin-catalog.test.ts
+++ b/web/test/admin-catalog.test.ts
@@ -18,7 +18,7 @@ import { FLV_1_IMPORT, FLV_IMPORT, nominalHealth } from './fixtures/health'
/**
* La page Catalogue dit-elle la vérité, et le mot de passe est-il demandé au bon moment ?
*
- * Onze choses sont tenues ici, et chacune est un écart constaté à §14.4 ou un défaut :
+ * Douze choses sont tenues ici, et chacune est un écart constaté à §14.4 ou un défaut :
*
* 1. aucune liste sans plafond, et aucun plafond muet — la troncature à vingt de la
* recherche ne disait rien, et un fichier de 116 anomalies affichait « 20 » sans un
@@ -45,7 +45,10 @@ import { FLV_1_IMPORT, FLV_IMPORT, nominalHealth } from './fixtures/health'
* 9. les signalements sont RELUS quand le poste change d'import ;
* 10. aucun bouton n'est armé pour un refus certain : le motif est exigé par le service ;
* 11. la zone de dépôt obéit au même verrou que le reste de la page, et le sélecteur de
- * fichier se réarme, sans quoi le MÊME fichier ne peut pas être redéposé.
+ * fichier se réarme, sans quoi le MÊME fichier ne peut pas être redéposé ;
+ * 12. les trois listes de signalements NOMMENT les produits : « 4412 » est un numéro qu'il
+ * faut chercher dans Odoo avant de commencer, et le nom vient de l'import lui-même, pas
+ * du catalogue en service.
*/
const HERE = dirname(fileURLToPath(import.meta.url))
@@ -346,6 +349,7 @@ function manyAnomalies(count: number): FindingDTO[] {
return Array.from({ length: count }, (_unused, index) => ({
csv_line: index + 2,
product_id: `id-${String(index)}`,
+ product_name: `TOMATE ${String(index)}`,
code: 'INVALID_BARCODE',
issue: 'anomaly',
message: 'Corrigez ce code dans Odoo.',
@@ -353,6 +357,27 @@ function manyAnomalies(count: number): FindingDTO[] {
}))
}
+/** Un signalement, dans la forme exacte que la route d'imports sert. */
+function finding(overrides: Partial = {}): FindingDTO {
+ return {
+ csv_line: 42,
+ product_id: '4412',
+ product_name: 'AIL VIOLET SAF',
+ code: 'RESERVED_ZONE_NOT_EMPTY',
+ issue: 'anomaly',
+ message: 'Corrigez le code-barres dans Odoo.',
+ value: '0493021012365',
+ ...overrides,
+ }
+}
+
+/** Les noms que dessine une des listes de signalements. */
+function namesIn(list: string): string[] {
+ return [...host.querySelectorAll(`[data-rows="${list}"] [data-name]`)].map(
+ (span) => span.textContent ?? '',
+ )
+}
+
/** Une décision humaine en vigueur. */
function decision(overrides: Partial = {}): DecisionDTO {
return {
@@ -404,8 +429,8 @@ describe('aucune liste sans plafond, et aucun plafond muet', () => {
// ET un code faux, c'est deux signalements sur la ligne 42. Une boucle clée par
// `csv_line` lèverait `each_key_duplicate` et emporterait tout l'écran.
findings = [
- { csv_line: 42, product_id: 'x', code: 'ZERO_PRICE', issue: 'anomaly', message: 'a', value: '0' },
- { csv_line: 42, product_id: 'x', code: 'INVALID_BARCODE', issue: 'anomaly', message: 'b', value: '1' },
+ finding({ code: 'ZERO_PRICE', message: 'a', value: '0' }),
+ finding({ code: 'INVALID_BARCODE', message: 'b', value: '1' }),
]
open()
await settle()
@@ -414,6 +439,59 @@ describe('aucune liste sans plafond, et aucun plafond muet', () => {
})
})
+describe('les listes de signalements nomment les produits', () => {
+ it('nomme le produit dans les trois listes, et pas seulement son identifiant Odoo', async () => {
+ // « 4412 » est un numéro qu'il faut d'abord chercher dans Odoo ; « AIL VIOLET SAF » est
+ // le produit que celui qui corrige le fichier reconnaît. Les trois listes sont dessinées
+ // par le MÊME extrait, et c'est pour ça qu'elles sont vérifiées ensemble : une seule
+ // d'entre elles qui nommerait le produit serait un extrait dupliqué quelque part.
+ findings = [
+ finding({ product_name: 'AIL VIOLET SAF' }),
+ finding({
+ code: 'UNIT_MISMATCH',
+ issue: 'info',
+ product_id: '5209',
+ product_name: 'OEUFS PLEIN AIR',
+ message: 'Corrigez l’unité dans Odoo.',
+ }),
+ finding({
+ code: 'PREPACKAGED_PRODUCT',
+ issue: 'info',
+ product_id: '77',
+ product_name: 'BOULGOUR GROS 5 KG',
+ message: 'Rien à corriger : c’est un code fournisseur.',
+ }),
+ ]
+ open()
+ await settle()
+
+ expect(namesIn('anomalies')).toEqual(['AIL VIOLET SAF'])
+ expect(namesIn('mismatches')).toEqual(['OEUFS PLEIN AIR'])
+ expect(namesIn('not-weighable')).toEqual(['BOULGOUR GROS 5 KG'])
+ // Le nom N'A PAS remplacé l'identifiant : c'est lui qui ouvre la fiche dans Odoo.
+ expect(host.querySelector('[data-rows="anomalies"] .id')?.textContent).toBe('4412')
+ })
+
+ it('n’invente aucun libellé pour un signalement sans nom', async () => {
+ // Deux signalements ne portent pas de nom : celui qui ne porte sur aucun produit, et une
+ // ligne trop abîmée pour en avoir un — ce qu'UNREADABLE_ROW dit déjà dans son message.
+ // Un « nom inconnu » écrit à leur place serait un fait que cette page n'a pas lu.
+ findings = [
+ finding({
+ code: 'UNREADABLE_ROW',
+ product_id: '90',
+ product_name: '',
+ message: 'Corrigez la ligne : elle doit porter un identifiant et un nom.',
+ }),
+ ]
+ open()
+ await settle()
+
+ expect(host.querySelectorAll('[data-rows="anomalies"] li')).toHaveLength(1)
+ expect(namesIn('anomalies')).toEqual([])
+ })
+})
+
describe('« Le proposer de nouveau », et le chemin qui y mène', () => {
it('reprend un produit retiré depuis les décisions en vigueur', async () => {
// La route CLIENT ne sert JAMAIS un produit retiré : la recherche ne peut donc pas le