partiduo-ui-bulma

Partiduo — portage de NOALYSS en Crystal (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 dans prod-crystal/ (marten, authn, password-policy, totp, webauthn, jose), en dépendances path: (voir shard.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 factice UITEST (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_ENGINE pour 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 de opal/) ; bibliothèques partagées sous opal/partiduo_ui/. Les composants s'enregistrent auprès de PartiduoUi::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 par Gemfile.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 formulaire data-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 HTMX data-pd-add-line), Ctrl+Entrée enregistre, le bouton data-pd-del-line retire 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 par spec/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 par SPDX-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.
Repository

partiduo-ui-bulma

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 1
  • about 3 hours ago
  • September 27, 2026
License

GNU Affero General Public License v3.0

Links
Synced at

Sun, 27 Sep 2026 19:02:30 GMT

Languages