9 Commits
Author SHA1 Message Date
bachir af36591d70 Ajoute le total en bas de chaque graph Charges structurelles par client
Ligne "Total" sous le graph principal et sous chacune des cartes par
année -- somme calculée à partir des items déjà tracés (sumItems()), pas
d'un champ stats séparé, pour rester cohérente par construction avec les
barres affichées au-dessus.
2026-09-08 15:38:17 +02:00
bachir 9234050a68 Ajoute un filtre par tag de signalement sur /lignes et /dashboard/compte
Même modèle multiselect/OR que les filtres Compte et Type déjà en place,
en complément (pas en remplacement) du simple on/off "Signalées
uniquement" du grand livre. Sur /dashboard/compte, la liste d'options se
limite aux tags réellement présents pour le compte sélectionné, et se
réinitialise au changement de compte.
2026-09-06 21:59:32 +02:00
bachirandClaude Sonnet 5 11250bea77 Add solde footers + per-année small multiples to /dashboard/compte
1. "Entrées sur-versées" and "Versements sans entrée liée" tables get a
   solde footer, same convention as "Reste à verser" above them --
   reuses the totalSurVerse/totalNonLies computeds already backing the
   summary cards, no new computation.

2. "Répartition de l'activité par type" and "Top clients" keep their
   all-time chart, now followed by a small-multiples grid of the same
   chart per année. The type breakdown reuses
   total_par_type_par_compte_par_annee, a new field on
   DashboardStatsController::stats() built from the same répartition-
   level rows already fetched for total_par_type_par_compte (no extra
   query, just one more level of grouping in the PHP aggregation). Top
   clients per année is computed client-side from the same
   entreesDuCompte already used for the all-time version, capped to top
   5 (not 10) to keep the grid readable.

HBarChart gains a `compact` prop (narrower fixed columns, smaller text)
for use inside the small-multiples cards -- the all-time charts above
keep the full-width layout unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 20:41:11 +02:00
bachirandClaude Sonnet 5 bd251d0a5e Fix diverging year-bars chart: bars were pinned to the top, not the zero line
Root cause: .figli-year-bar was position: relative with an explicit
height (set inline by barStyle()), which opts it out of the flex
container's default stretch alignment -- so it rendered flex-start
(top) aligned first, and the top/bottom: 50% from barStyle() only
*offset* that already-top position instead of anchoring an edge to the
middle. Visibly: green (positive) bars piling up near the top instead
of growing upward from the zero line.

Fixed by wrapping each bar in a .figli-year-bar-slot that stays in the
normal flex flow (so horizontal side-by-side layout for two bars/year
still works) and giving the bar itself position: absolute, anchored
against the slot's full-height box -- that's what makes a 50% top/
bottom offset actually mean "the zero line" instead of "50% further
down/up from wherever flex already put it."

Verified geometrically in-browser: bars now straddle the track's
midpoint and extend outward in the correct direction (positive up,
negative down) on both "Entrées vs versements par année" and
"Évolution du solde".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:14:00 +02:00
bachirandClaude Sonnet 5 9ce5d87ace Surface signalement (flag) tags on the per-compte dashboard
Fetches field_flag alongside the existing entrée/versement data (same
JSON:API include list, same buildRows() shape as home.js's flags/
hasFlag). Two changes:

- Rows already shown in the reste-à-verser/sur-versé/non-liés tables
  get the same amber left-edge accent + tag badges as /lignes when
  they're also flagged.
- New "Lignes signalées" section lists every flagged entrée/versement
  for the selected compte, including ones that don't appear in any of
  the other tables -- a fully-settled line can still carry a flag for
  an unrelated reason (e.g. "client injoignable"), which none of the
  reconciliation-based tables would otherwise surface.

Verified live against an isolated test node plus two real flagged lines
already present in production data (client had already started using
the /lignes signalement feature) -- both the section and the inline
badges render correctly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 16:43:44 +02:00
bachirandClaude Sonnet 5 ceaf301bf0 Add per-compte dashboard: reste à verser between entrées and versements
New /dashboard/compte page, one compte associé (freelance) at a time via
a selector. Focused on entree/versement only (not achat/hébergement/
sous-traitant): reuses the same entrée<->versement reconciliation
algorithm as the /lignes badges (a versement can settle several entrées
at once, split equally), aggregated per compte to surface outstanding
"reste à verser", over-paid entrées, and versements with no linked
entrée at all -- plus solde/entrées-vs-versements/top-clients charts for
context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 14:29:27 +02:00
bachir 2f9ad7d52c Rebuild /dashboard with charts, add Grand livre/Dashboard nav
Nav: a small "Grand livre" / "Dashboard" switcher, top-right on both
pages, with the current page highlighted. Needed hook_theme() to
declare current_route as an accepted variable for both theme hooks --
same class of bug as the earlier can_view_history fix: an undeclared
variable passed via the render array is silently dropped rather than
reaching Twig.

Dashboard: replaced the old "solde par compte / par client" tables
(computed client-side from a full unwindowed JSON:API fetch of every
node -- the same performance problem the /lignes sliding window was
built to avoid, just not yet felt at 1500+ lines) with a single
aggregate endpoint (DashboardStatsController, plain SQL GROUP BY) and
five chart panels: CA par année, solde par compte (diverging,
red/green), solde par compte trend (small multiples per compte),
répartition par type, top clients par CA. No charting library --
small dependency-free div/CSS bar charts (HBarChart/ColumnChart/
MiniTrend components in dashboard.js), consistent with this project
vendoring its own JS.

Two data-correctness fixes along the way: (1) the historical stray
mistyped dates (0213-06-15, 2015-08-29 -- preserved as-is per this
module's policy) needed excluding from per-year buckets without
excluding their money from all-time totals, so the filtering happens
per-output-field in PHP rather than as a blanket SQL date range. (2)
PHP silently casts numeric-looking array keys ("2021") to actual
integers, so array_keys() on a year-keyed map produces a mix of ints
and strings -- json_encode emits the int ones as bare JSON numbers in
a list (unlike object keys, which JSON always stringifies), which
broke the frontend's annees[i].slice(2) trend-card labels. Fixed with
an explicit array_map('strval', ...).

Verified: bar widths/colors match the underlying data exactly (e.g.
EXT.'s red bar is proportionally sized against Maud's green one per
their actual solde ratio), all 8 trend cards render correct year
labels, and both nav links correctly highlight on their own page.
2026-09-04 23:30:40 +02:00
bachirandClaude Sonnet 5 a6626e023a Fix dark mode readability + replace error-row fill with thin red outline
Gin dark mode (html.gin--dark-mode) is on by default and the previous CSS
relied on Gin custom properties that didn't resolve here, causing
black-on-black/white-on-white text. Now uses self-contained explicit
colors with a dark-mode override block. Error rows get a thin red border
instead of a background fill, as requested.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 22:55:11 +02:00
bachirandClaude Sonnet 5 ca800be21a Initial Drupal site: content model + validation + Vue dashboard
- Content type "Ligne comptable" with Paragraphs "Répartition" (Compte + Montant)
- Taxonomies: Compte (9 comptes) and Client (unified client list)
- hook_node_presave + form validate: sum(répartition) must equal montant HT
- /dashboard route (progressive decoupling): Vue 3 app fetching JSON:API,
  computing solde par compte / par client client-side
- "Ajouter une ligne" opens the real Drupal node form in an AJAX modal
- Gin as default + admin theme
- 9 opening-balance lines seeded from suivi_compta_SASFigli2026_v2.ods

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 22:32:54 +02:00