Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 111 additions & 98 deletions INSTALLATION.md

Large diffs are not rendered by default.

34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,34 @@ la caisse, la géométrie de l'étiquette — mais aucune ligne de code.
en service.** Il reste la recette sur un poste pilote. Ce qui est prouvé, ce qui ne
l'est pas et ce qui reste ouvert : [SUIVI.md](SUIVI.md).

## Installer un poste

Sur un Windows nu, sans dépôt, sans Go et sans archive à décompresser. Les droits
administrateur sont demandés en cours de route :

```powershell
irm https://raw.githubusercontent.com/lostmind84/OpenScale/main/deploy/windows/bootstrap.ps1 | iex
```

<details>
<summary>Depuis une invite de commandes (<code>cmd</code>)</summary>

```cmd
curl -fsSL https://raw.githubusercontent.com/lostmind84/OpenScale/main/deploy/windows/bootstrap.cmd -o %TEMP%\openscale.cmd && %TEMP%\openscale.cmd
```

</details>

La commande prend la dernière version publiée, **vérifie son empreinte avant de la
décompresser**, pose trois questions — mot de passe de la session du poste, production ou
pilote, ouverture de session automatique — puis installe tout : compte Windows dédié,
service, tâche du kiosque, réglages d'alimentation, fiche d'installation à ranger dans le
classeur du magasin. Comptez **15 minutes** jusqu'à la première étiquette, matériel branché.

Le parcours complet du bénévole — redémarrage de recette, balance, imprimante, catalogue,
et **l'installation par clé USB d'un poste sans Internet** — est dans
[`INSTALLATION.md`](INSTALLATION.md). Sous Linux, `deploy/linux/install.sh`.

## Essayer, sans balance et sans imprimante

Un poste complet tourne sur votre machine en quatre commandes. **Les deux colonnes font
Expand Down Expand Up @@ -129,10 +157,10 @@ détecteur de course sont détaillés dans
Un poste ne s'installe pas en copiant `openscale.exe` : il lui faut les scripts, la tâche
planifiée ou les unités systemd, la configuration et les documents du bénévole.
`make release` assemble le tout en une archive par plateforme, et pousser un tag de
version fait la même chose sur la page *Releases*.
version fait la même chose sur la page *Releases*. C'est cette archive que la commande
ci-dessus télécharge.

L'installation elle-même est décrite dans [`INSTALLATION.md`](INSTALLATION.md), écrit pour
un bénévole ; la fabrication des archives dans
La fabrication des archives est décrite dans
[`docs/06-developpement.md`](docs/06-developpement.md).

## Licence
Expand Down
7 changes: 4 additions & 3 deletions SUIVI.md
Original file line number Diff line number Diff line change
Expand Up @@ -1172,10 +1172,10 @@ d'étiquette, clone la configuration vers les 3 autres postes et vérifie l'empr

| Membre du critère | Ce qui le porte |
|---|---|
| installer seul, sans développeur | `deploy/windows/install.ps1` — compte local, ACL, service, tâche, alimentation, Windows Update, fiche d'installation. Idempotent, chaque appel natif gardé |
| installer seul, sans développeur | **une commande** — `deploy/windows/bootstrap.ps1`, téléchargé depuis `main`, résout la dernière release, vérifie son empreinte AVANT de décompresser, débloque, pose trois questions et appelle `deploy/windows/install.ps1` — compte local, ACL, service, tâche, alimentation, Windows Update, fiche d'installation. Idempotent, chaque appel natif gardé |
| **revenir seul sur l'écran client** | ouverture de session automatique écrite par l'installeur (bloquant-7) + tâche `OpenScale-Kiosk` en `InteractiveToken` + `openscale kiosk` (`internal/kiosk`) |
| en 15 minutes | `INSTALLATION.md` **compte les étapes : 17 minutes** pour le premier poste, ~7 pour les suivants. L'écart est dit, pas caché |
| régler le décalage avec l'aperçu | écran Étiquette (front admin) — étape 6 de la notice, celle qui fait dépasser le compte |
| en 15 minutes | `INSTALLATION.md` **compte les étapes : 15 minutes** pour le premier poste, ~7 pour les suivants. Les deux minutes d'écart étaient le téléchargement et la décompression, que la commande unique absorbe |
| régler le décalage avec l'aperçu | écran Étiquette (front admin) — étape 5 de la notice, la plus longue des six |
| cloner et **vérifier l'empreinte** | `openscale config export` / `fingerprint`, et le test qui prouve les deux sens : même empreinte pour deux postes réglés à l'identique, empreinte différente dès qu'un réglage métier diverge |
| mettre à jour sans risque | `update.ps1` / `update.sh` : arrêt borné, sauvegarde horodatée, vérification de `/healthz`, **restauration automatique** |
| désinstaller sans casser le retour en arrière | `uninstall.ps1` restaure `restore.json` et **garde les données** (important-15) |
Expand Down Expand Up @@ -1365,6 +1365,7 @@ engageantes :

| Date | Événement |
|---|---|
| 31/07/2026 | **Un poste s'installe en une commande** (`deploy/windows/bootstrap.ps1`, `bootstrap.cmd`). Sept gestes disparaissent, dont **trois qui échouaient en silence chez un bénévole** : la case « Débloquer » oubliée, que la stratégie d'exécution transforme en refus parlant de « fichier téléchargé depuis Internet » et jamais d'OpenScale ; PowerShell ouvert sans élévation, dont le refus arrivait après le téléchargement ; et le mauvais répertoire, qui faisait accuser l'archive. Le script vit sur `main` et **ne nomme aucune version** — il demande `/releases/latest`, seul point de l'API qui exclut brouillons et pré-versions par contrat. **L'empreinte est vérifiée avant l'extraction**, jamais après : décompresser une archive non vérifiée écrit sur le disque des fichiers dont la ligne suivante exécute un en administrateur. Trois questions au lieu de zéro paramètre — mot de passe du compte Windows, production ou pilote, ouverture de session automatique. **Le plancher de quatre caractères n'est pas redit ici** : il vit dans `common.ps1` et `Resolve-AccountPassword` en est l'autorité, la question le lit. Un bootstrap qui l'aurait réécrit aurait accepté ce que l'installeur refuse trois pas plus loin, devant un bénévole ayant déjà tout répondu. Le refus réseau `SeDenyNetworkLogonRight` a été **proposé et écarté** au passage, le réseau du magasin étant de confiance. **Le mot de passe ne sort jamais du processus** : saisie masquée, `install.ps1` appelé par éclatement de table dans le même processus, et l'auto-élévation REFUSÉE quand `-AccountPassword` est fourni — une relance élevée ferait passer le mot de passe par une ligne de commande, lisible dans la liste des processus. **Deux trous rebouchés en chemin** : `install.ps1` ne copie aucun script, donc un poste installé depuis `%TEMP%` n'aurait eu ni désinstalleur ni script de mise à jour — le dossier extrait est conservé sous `ProgramData\OpenScale\installer\<version>` ; et `-Version`, qui installe une release antérieure, aurait fait échouer l'appel sur un paramètre inconnu **après** les trois questions, donc les paramètres sont confrontés à ce qu'`install.ps1` déclare. `INSTALLATION.md` retombe à **15 minutes comptées**, la voie clé USB reste écrite pour le poste hors ligne. **La branche a croisé `-AccountPassword`, livré le même jour sur `main` pour un autre défaut** : le `-SessionPassword` qu'elle portait faisait double emploi en moins bon — il ne conservait pas le mot de passe d'un poste réinstallé — et a été retiré au profit du paramètre de `main`. **13 tests de `deploy/`** ajoutés |
| 31/07/2026 | **Un catalogue redéposé à l'identique vidait le plan de travail de la page Catalogue.** Signalé depuis le poste : *« les articles à corriger dans Odoo et les autres sections sont vides alors même que l'import a bien détecté des problèmes »*. Les compteurs de l'encadré du haut viennent de la **ligne d'import** ; les trois listes en dessous viennent des **signalements**, lus par identifiant d'import — et un export redéposé à l'identique est enregistré `unchanged`, ce qu'ADR-015 tient pour un événement **nominal**, sans réécrire aucun signalement, puisqu'ils appartiennent à l'import qui a produit la grille, une ligne plus haut (`importer.unchanged`). Les écrans lisaient la **dernière ligne** : à la deuxième livraison du même fichier, « 16 anomalies · 8 non pesables » restait affiché au-dessus de trois listes répondant toutes *« Aucune anomalie sur le dernier import. »*, **et définitivement**. Le poste nomme désormais la ligne qui décrit le catalogue **en service** (`catalog_findings_id`), parce qu'il est le seul côté à voir au-delà des vingt imports servis à la page ; `failed` retombe dessus pour la même raison, `applied` et `rejected` gardent la leur — les remarques d'un lot refusé sont exactement ce qu'il faut corriger pour que le fichier suivant entre (§10.5). Même lecture qu'ADR-053, au même endroit : deux écrans, une ligne d'une table. Le tableau de bord y gagne au passage la reprise de sa ventilation « — préemballés (7), code interne 0490 (1) », perdue en même temps. **2 885 tests Go** (`go test ./... -v`, sous-tests compris : 2 878 verts, 7 écartés) sur 35 paquets et **769 tests front**, tous verts, passe `-race` verte sur les deux paquets touchés |
| 31/07/2026 | **La date en barre basse mentait à chaque redémarrage** (ADR-053). L'écran client affiche en permanence *« Catalogue du … »* pour répondre à « ces prix datent de quand ? », et §14.3 en faisait **l'instant de la bascule** — un instant que **rien ne persistait** : `newHub` le posait à `Clock.Now()` sur le catalogue relu en base, donc chaque reboot, chaque mise à jour et chaque reprise après plantage redatait un catalogue que personne n'avait réimporté. Un poste privé de fichier depuis trois semaines affichait la date du matin, **soit exactement le silence que cette date existait pour révéler**. L'instant devient celui de **l'import appliqué** — `imports.occurred_at` où `result = 'applied'`, écrit dans la même transaction que le catalogue, donc impossible à contredire — relu au démarrage (`Options.CatalogAt`) et transporté par le lot jusqu'à la bascule (`BatchResult.AppliedAt` → `CatalogBatch.ImportedAt`), au lieu d'être relu à l'arrivée : le même catalogue porte le même nombre en service et après le prochain redémarrage. **Deux découvertes en chemin** : `CatalogBatch.ReceivedAt` était **écrit et jamais lu** — le champ prévu pour porter cet instant n'avait jamais été branché — et `storeCatalog` stockait `time.Time{}.UnixNano()`, un très grand nombre **négatif**, ce qui aurait fait dire « 1754 » à l'écran d'un poste sans import plutôt que « Catalogue en attente ». Persister la bascule était l'autre voie : écarté, elle demandait une écriture SQLite **dans la goroutine du Hub**, que §13.2 interdit. **2 883 tests Go** (`go test ./... -v`, sous-tests compris) sur 35 paquets et **768 tests front**, tous verts, passe `-race` verte sur les quatre paquets touchés |
| 31/07/2026 | **L'origine des produits devient enfichable, et le CSV n'en est plus qu'un mode** (ADR-052). Acquisition (`ports.CatalogSource`) et format (`catalog.RowReader`) sont deux axes séparés ; ce qu'un catalogue **décide** vit dans `catalog.Assemble` et ne se réimplémente dans aucune source. Cinq constats, tous reproduits : les deux sources livrées appelaient le parseur CSV en dur, `ports.Batch` avait la forme d'un fichier, la racine tenait une `map` au lieu de son registre, `Config.Validate` codait `local_drop` et `webdav` en dur dans le domaine, et **la coupe 2 ne couvrait pas les sources** — `internal/web` aurait pu importer un paquet source sans un mot. Contrôle 47 supprimé, numéro laissé en trou. `internal/catalog/example` livré : source d'ERP par HTTP, paginée, **enregistrée nulle part**. **2 835 tests Go** verts sur 35 paquets, passe `-race` verte. Trois découvertes en écrivant l'exemple, dont `next_page` cherché avant `products` — qui lit la page 1 et l'appelle le catalogue entier, **en silence** |
Expand Down
Loading