partiduo-ui-bulma
= partiduo-ui-bulma — interface Bulma de Partiduo :toc: left :toc-title: Table des matières :icons: font
Partiduo est le portage en Crystal de https://noalyss.eu[NOALYSS], logiciel libre de comptabilité et d'ERP créé et maintenu par Dany De Bontridder. Partiduo n'est pas un projet officiel NOALYSS.
Ce dépôt est la première interface de Partiduo (ADR-005 D5) : routes, handlers, gabarits, feuilles de style, JavaScript et écrans Opal. Il compose le cœur partiduo-app (shard partiduo) et ne lui parle que par le contrat Partiduo::Api (ADR-005 D1, D2) : aucune règle métier ici, aucun modèle du cœur lu directement. Une autre interface (partiduo-ui-<nom>) peut remplacer celle-ci sans toucher au métier.
La spécification est dans ../partiduo-docs/ (ADR-001 à ADR-006, maquette de référence maquette/index.html). Les décisions et blocages du portage sont consignés dans ../partiduo-app/DECISIONS.adoc et ../partiduo-app/BLOCAGES.adoc.
== Organisation
[source]
config/ # réglages Marten (composition cœur + interface), routes src/ ├── partiduo_ui.cr # point d'entrée : require "partiduo" + application ui ├── server.cr # serveur HTTP (Marten.start) ├── cli.cr # ligne de commande (migrations du cœur comprises) └── ui/ # application Marten « ui » (PartiduoUi) ├── format.cr # montants et dates selon la langue et le pays ├── table.cr, form.cr, screen.cr # listes triables, formulaires, consultations ├── handlers/ # handlers HTTP (reference/ : référentiel du lot 1) ├── templates/ui/ # gabarits (base.html, _icon.html…) ├── locales/ # libellés d'écran fr, en, nl (ADR-005 D7) └── assets/ui/ ├── css/ # bulma.min.css (1.0.4), theme.css, fonts.css, app.css ├── fonts/ # IBM Plex Sans et Plex Mono (woff2, SIL OFL 1.1) ├── icons/ # sprite.svg : jeu d'icônes unique (Lucide, ISC) └── js/ # htmx.min.js (2.0.11), opal/<écran>.js (générés) opal/ # sources Ruby des écrans Opal icons/lucide/ # sources SVG des icônes retenues scripts/ # opal-build, icon_sprite.cr, api_boundary.cr, CI
== Prérequis
- Crystal 1.19.1 ;
- PostgreSQL 18, joint par socket Unix (
/tmp/.s.PGSQL.5432) ; - le cœur à côté du dépôt (
../partiduo-app) et les shards maison dansprod-crystal/(marten,authn,password-policy,totp,webauthn,jose), en dépendancespath:(voirshard.override.yml) ; - pour recompiler les écrans Opal seulement : Ruby ≥ 3.1 et Bundler. Ni npm ni Node ne sont utilisés.
== Installation et lancement
[source,sh]
SKIP_MARTEN_CLI_PRECOMPILATION=1 shards install createdb --encoding=UTF8 partiduo_dev crystal run manage.cr -- migrate crystal run src/server.cr # http://demo.partiduo.localhost:8000/
PORT change le port ; DATABASE_URL (par défaut postgres:///partiduo_dev?host=/tmp) et PARTIDUO_MODULES sont lus par le cœur. En production : MARTEN_ENV=production, MARTEN_SECRET_KEY, MARTEN_ALLOWED_HOSTS, puis crystal run manage.cr -- collectassets ; les fichiers collectés sont servis par Marten (Marten::Middleware::AssetServing).
== Specs
Chaque développeur ou agent utilise sa propre base de test (nom contenant test) :
[source,sh]
createdb --encoding=UTF8 partiduo_test_ui DATABASE_URL="postgres:///partiduo_test_ui?host=/tmp" crystal spec
Les specs couvrent la coquille (barre supérieure, menu des modules actifs, langues), les écrans d'authentification (connexion par passkey ou mot de passe
- TOTP, enrôlement, sécurité du compte, codes de récupération, temporisation et blocage), le contrôle d'accès des extensions, l'accessibilité automatisable, le service des fichiers statiques, le garde-fou d'interface, les conventions (SPDX, traductions, icônes) et Opal. Les comptes de test sont créés par le contrat (
spec/support/accounts.cr) ; les passkeys sont simulées par un authentificateur ES256 (spec/support/authenticator.cr) ; l'extension facticeUITEST(spec/fixtures/uitest/) sert au contrôle d'accès. Les specs qui exécutent le JavaScript compilé utilisent un moteur autonome (JavaScriptCore, présent sur macOS ;PARTIDUO_JS_ENGINEpour en indiquer un autre) ; elles sont en attente s'il n'y en a pas.
Le schéma de la base de test est reconstruit par les migrations du cœur au début de la suite (spec/support/database.cr, DECISIONS D-UI-028) : les écritures s'appuient sur les contraintes et déclencheurs posés en SQL (équilibre différé, périodes closes, numérotation des factures). Les dossiers comptables de test sont provisionnés par le contrat (spec/support/books.cr).
== Écran
Coquille de la maquette de référence (ADR-005 D5), gabarit ui/base.html :
- barre supérieure : dossier (
Api::Core.settings), exercice et période courants (en attente du contrat du lot 1), recherche globale (/), langue (fr, en, nl), menu du compte (sécurité, à propos, déconnexion) ; - menu latéral :
Api::Modules.menu(acteur)— rubriques du socle dans l'ordre de l'ADR-005, seuls les modules actifs et les entrées permises, puis les extensions (badge de leur code). Un nom de route du manifeste (accounting:chart) que l'interface ne fournit pas encore donne une entrée désactivée ; - contenu : fil d'Ariane, titre, actions (blocs
crumbs,heading,actions,content).
Trois tailles : ordinateur (≥ 1280 px), tablette (menu repliable, choix mémorisé), téléphone (< 768 px : barre non figée sur trois lignes, menu fermé, colonnes secondaires masquées par pd-hide-s). Cibles tactiles de 44 px, focus visible, lien d'évitement, propriétés CSS logiques.
== Comptabilité et Facturation (lots 2 et F)
[cols="2,3",options="header"] |=== |Route |Écran
|/ |Tableau de bord : tuiles des modules actifs, dernières factures et écritures, « À traiter » |/accounting/entries/{purchase,sale,financial,misc} |Saisie au clavier : achats, ventes, extrait financier, opérations diverses ; équilibre et écriture calculée en continu (check_entry, check_document, check_financial) |/accounting/entries, /accounting/entries/<id> |Écritures : recherche, consultation, annulation par extourne |/accounting/accounts?q= |Comptes et tiers (ADR-005 D9) : synthèse, balance âgée, mouvements, lettrer, relancer, export CSV ; version téléphone |/accounting/matching?q= |Lettrage : lignes non lettrées à cocher, sélection contrôlée (check_matching), délettrage |/invoicing/documents |Devis et factures : onglets par nature, filtre de statut, export CSV |/invoicing/documents/new?kind=quote, /invoicing/invoices/new |Nouveau brouillon (client et articles complétés, totaux par check_document) |/invoicing/documents/<id> |Consultation : valider, transformer, avoir, acompte, décision sur un devis, règlement, relance |/invoicing/documents/<id>/preview, …/pdf |Aperçu imprimable ; PDF/A-3 (Factur-X pour factures, acomptes, avoirs) |/invoicing/payments, /invoicing/reminders, /invoicing/export |Règlements (Comptabilité inactive), relances proposées, transmission au comptable |/invoicing/documents/<id>/send |Envoi d'un document émis par courriel (PDF Factur-X joint), tracé |/invoicing/settings, /invoicing/templates |Paramètres de facturation ; modèles de mise en page (couleurs, en-tête, pied de page) |/accounting/invoicing-history |Factures, avoirs et règlements de la Facturation à comptabiliser : tout ou un, écarter, rendre (ADR-006 D2) |===
Dates abrégées partout où l'on saisit une date : 12 = le 12 du mois de la période de travail, 12/3 = le 12 mars, 120326 = le 12 mars 2026.
== Éditions (lot 3)
[cols="2,3",options="header"] |=== |Route |Écran
|/accounting/reports/trial-balance |Balance générale : ouverture, mouvements, soldes, totaux par classe, synthèse |/accounting/reports/auxiliary-balance |Balance des tiers (clients, fournisseurs) |/accounting/reports/aged-balance |Balance âgée au jour choisi ; éléments ouverts d'un tiers |/accounting/reports/general-ledger |Grand livre par compte ou par tiers (grand livre auxiliaire) |/accounting/reports/journals |Journaux : écritures, lignes, totaux du mois |/accounting/reports/balance-sheet, …/income-statement |Bilan et compte de résultat (FR, BE), exercice précédent en regard |/accounting/reports/custom |Rapports personnalisés par formules : liste, calcul, création, modification |/accounting/reports/fec |FEC de l'exercice (séparateur, encodage) |===
Chaque édition exporte en CSV et en PDF (?format=csv|pdf, fichiers du cœur) ; ses critères sont gardés d'une visite à l'autre (?reset=1 les oublie, DECISIONS D-UI-035) ; tout compte, tiers ou montant agrégé est un lien vers sa consultation (D-UI-036). Le plan comptable affiche les soldes (D-UI-037).
== Stock, prévisions et suivi (lot 6)
[cols="2,3",options="header"] |=== |Route |Écran
|/stock/repositories, /stock/settings |Dépôts (création, modification, suppression) ; dépôt par défaut des mouvements automatiques |/stock/items |Articles suivis en stock : fiche article, code stock, quantité du jour |/stock/changes, /stock/changes/new, /stock/changes/<id> |Opérations manuelles (quantité signée, coût unitaire) et inventaires : liste, saisie, consultation, suppression |/stock/inventory |Inventaire en deux temps : quantités théoriques proposées, puis quantités comptées (l'écart devient un mouvement) |/stock/state, /stock/history, /stock/valuation |État des stocks, historique des mouvements (pièce d'origine liée), valorisation au coût moyen pondéré ; CSV du cœur |/accounting/forecasts |Prévisions budgétaires : prévision, catégories, éléments (formule du réel, montant par période, montants propres), copie |/accounting/forecasts/<id>/report |Estimé, réel et écart par élément et par période ; totaux par catégorie ; CSV |/followup/actions |Actions de suivi : recherche (texte, état, type, étiquette, fiche, dates, actions internes), export CSV du cœur |/followup/actions/new, /followup/actions/<id> |Saisie et consultation d'une action : fiches par quick code, étiquettes, commentaires, état, actions liées, opérations rattachées (entry:42) |/followup/reminders |Rappels du jour et rappels dépassés |/followup/types, /followup/tags |Types d'action (préfixe, prochain numéro, types de base) ; étiquettes |===
Modules inactifs : 404. La fiche d'un tiers mène à ses actions de suivi, celle d'un article suivi à l'historique de son stock ; la consultation d'une écriture liste les actions qui la citent (DECISIONS D-UI-045 à D-UI-047).
== Paramètres, fin d'exercice et rapprochement (critique de complétude)
[cols="2,5",options="header"] |=== |Adresse |Écran
|/settings/company, …/edit |Société : identité, coordonnées, langue, domaine, méthodes et niveau d'authentification (régime fiscal figé) |/settings/modules |Modules et extensions : activer, désactiver (données conservées), dépendances (ADR-006 D2) |/settings/currencies, …/<code> |Devises et cours datés |/settings/users, …/new, …/<id> |Utilisateurs : invitation (courriel et lien affiché), rôle comptable, profil, fin d'accès, révocation, déblocage, droits par journal |/settings/profiles |Profils et permissions par pièce ; profils par défaut dont « Comptable invité (lecture) » (ADR-006 D4) |/settings/audit |Journal d'audit nominatif |/cards/categories |Catégories de fiches et leurs attributs propres |/accounting/closing |Fin d'exercice : clôture des comptes 6 et 7, à-nouveaux de l'exercice suivant |/accounting/reconciliation |Rapprochement bancaire : opérations d'un journal financier cochées d'après le relevé, écart contrôlé (HTMX), relevés rapprochés |===
Décisions D-UI-051 à D-UI-056 (DECISIONS du cœur).
== Authentification (ADR-002)
[cols="2,3",options="header"] |=== |Route |Écran
|/login |Connexion : passkey d'abord, puis mot de passe ; temporisation et blocage affichés |/login/second-factor |Code de l'application d'authentification ou code de récupération |/invitation/<jeton> |Confirmation de l'invitation, puis session d'enrôlement |/account/enrollment |Enrôlement : passkey proposée d'abord, mot de passe en repli |/account/security |Sécurité du compte : niveaux, ce qui manque, élévation, passkeys, TOTP, codes, mot de passe |/account/totp |Enrôlement TOTP : QR code et secret base32, confirmation par HTMX |/account/passkey-prompt |Invitation à enrôler une passkey après une connexion par mot de passe |/password/forgotten, /password/reset/<jeton> |Remise à zéro par courriel |/unlock, /unlock/<jeton> |Déblocage par courriel |===
Le jeton de session du cœur est gardé dans le cookie partiduo_session (HttpOnly, SameSite=Lax, Secure en HTTPS) ; chaque requête le convertit en acteur par Api::Auth.actor. Les passkeys passent par src/ui/assets/ui/js/passkey.js (WebAuthn, octets en base64url) : aucune règle n'y est codée.
== Extensions (ADR-003 D3, ADR-005 D4)
Le dossier ui/bulma/ d'une extension déclare ses routes et les monte :
[source,crystal]
module Skel::Ui ROUTES = Marten::Routing::Map.draw do path "/", Skel::Ui::IndexHandler, name: "index" # route du menu : skel:index path "/edit", Skel::Ui::EditHandler, name: "edit" end end
PartiduoUi::Extensions.mount "SKEL", Skel::Ui::ROUTES, permissions: {"edit" => "skel.page.edit"} # ou permission: pour toutes
Les routes sont servies sous /ext/SKEL/ et nommées skel:<nom>, comme dans le manifeste. Toute requête passe d'abord par PartiduoUi::ExtensionHandler : non connecté → connexion ; extension inconnue ou inactive → 404 ; session sous le niveau exigé → sécurité du compte ; permission donnée au montage (déclarée par le manifeste de l'extension), à défaut celle de l'entrée de menu du manifeste qui porte la route ; sinon 403. L'extension n'a aucun contrôle à écrire. Ses gabarits étendent ui/base.html.
== JavaScript
HTMX, les paquets Opal, et deux scripts de liaison au navigateur écrits à la main, sans compilation ni logique métier (DECISIONS D-UI-005) : shell.js (repli du menu, raccourci /, choix de langue) et passkey.js (WebAuthn). Toutes les pages restent utilisables sans JavaScript, sauf la passkey, qui l'exige par nature.
== Opal
Opal fait partie de la stack (ADR-001 D4, ADR-005 D5) : chaque écran démarre en HTMX et passe en Opal quand l'ergonomie l'exige (saisie, lettrage, rapprochement). Tout passage d'un écran en Opal est consigné dans DECISIONS.
- Sources :
opal/<écran>.rb(un paquet par fichier à la racine deopal/) ; bibliothèques partagées sousopal/partiduo_ui/. Les composants s'enregistrent auprès dePartiduoUi::Boot.register("<sélecteur CSS>")et sont montés au chargement de la page et après chaque remplacement HTMX (htmx:load). - Compilation : gem
opal(version verrouillée parGemfile.lock), sans npm ni Node. Le JavaScript produit,src/ui/assets/ui/js/opal/<écran>.js, est versionné et servi par Marten comme HTMX. - Exemple :
opal/demo.rb, compteur de clics de la page « À propos ». - Saisie au clavier :
opal/entry.rb(entry.js), monté sur tout formulairedata-pd-entry(écritures, devis et factures) : Entrée avance d'un champ (et ajoute une ligne après le dernier), Alt+↓ ajoute une ligne (bouton HTMXdata-pd-add-line), Ctrl+Entrée enregistre, le boutondata-pd-del-lineretire la ligne sur place (DECISIONS D-UI-027). La règle des touches (opal/partiduo_ui/entry/keys.rb) et le montage sont exécutés sous JavaScriptCore parspec/opal/build_spec.cr.
Recompiler après toute modification de opal/ :
[source,sh]
bundle install # une fois bundle exec scripts/opal-build # recompile tous les paquets bundle exec scripts/opal-build --check
L'en-tête de chaque paquet porte l'empreinte SHA-256 des sources : la spec spec/opal/build_spec.cr échoue si un paquet n'a pas été recompilé.
== Icônes
Un seul jeu, Lucide (licence ISC, version dans icons/lucide/VERSION) : chaque icône retenue est un fichier de icons/lucide/, assemblé dans src/ui/assets/ui/icons/sprite.svg par crystal run scripts/icon_sprite.cr. Dans un gabarit :
[source,html]
{% include "ui/_icon.html" with name="search" %}
== Garde-fou ADR-005 D3
scripts/api_boundary.cr (exécuté en CI et par spec/architecture/api_boundary_spec.cr) refuse toute référence au cœur hors de Partiduo::Api : constante Partiduo::<Interne>, appel Partiduo.<méthode>, require "partiduo/<fichier interne>", réouverture d'un espace Partiduo. Seule la composition est admise : require "partiduo", require "partiduo/cli", Partiduo.apply_settings, Partiduo::INSTALLED_APPS, Partiduo::LOCALES, Partiduo::VERSION, Partiduo::API_VERSION.
== Licences
- Code de ce dépôt : GNU AGPL v3 ou ultérieure (
LICENSE) ; chaque fichier source commence parSPDX-License-Identifier: AGPL-3.0-or-later. - Bulma (MIT), HTMX (0BSD), runtime Opal (MIT), IBM Plex (SIL OFL 1.1), Lucide (ISC) : fichiers tiers versionnés tels quels.
partiduo-ui-bulma
- 0
- 0
- 0
- 0
- 1
- about 3 hours ago
- September 27, 2026
GNU Affero General Public License v3.0
Sun, 27 Sep 2026 19:02:30 GMT