From b73a98028203413c49a91d973a01f83c78c22ae9 Mon Sep 17 00:00:00 2001 From: bach Date: Sat, 5 Sep 2026 19:03:41 +0200 Subject: [PATCH] =?UTF-8?q?Resolve=20the=20full=20entr=C3=A9e/versement=20?= =?UTF-8?q?group=20regardless=20of=20the=20sliding=20window?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit filterEntreeGroup, reconciliationByEntree and versementStatus only ever searched `rows`, the currently loaded date-range slice -- a versement linked to entrées from other years (confirmed live: one node had 3 linked entrées spanning 2023/2025/2025) silently only showed whatever happened to be in the loaded window, both in the drill-down modal and in the reste-à-verser/sur-versé math. Added LedgerStatsController::groupeEntree() (GET /lignes/api/groupe/ {node}), a real DB query following field_entree_liee in both directions (a node's own targets, and any node referencing it) -- something the client can't discover from a partial window. toggleEntreeFilter() now fetches the complete group up front and merges whatever isn't already loaded into groupExtraRows; every affected computed reads the combined pool via a new allKnownRows. Also stopped versementStatus from defaulting to a false "ok" when a linked entrée's reconciliation can't be resolved yet -- it now reports a distinct "inconnu" (À vérifier) status instead of silently assuming everything's settled. --- .../figli_compta_ledger.routing.yml | 11 ++ .../custom/figli_compta_ledger/js/home.js | 135 +++++++++++++++--- .../src/Controller/LedgerStatsController.php | 48 +++++++ .../templates/figli-compta-home.html.twig | 2 +- 4 files changed, 177 insertions(+), 19 deletions(-) diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml index c6ce5c1..26a8207 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.routing.yml @@ -55,6 +55,17 @@ figli_compta_ledger.api_reconciliation_ouverture: requirements: _permission: 'access content' +figli_compta_ledger.api_groupe_entree: + path: '/lignes/api/groupe/{node}' + defaults: + _controller: '\Drupal\figli_compta_ledger\Controller\LedgerStatsController::groupeEntree' + requirements: + _permission: 'access content' + options: + parameters: + node: + type: entity:node + figli_compta_ledger.api_dashboard_stats: path: '/dashboard/api/stats' defaults: diff --git a/web/modules/custom/figli_compta_ledger/js/home.js b/web/modules/custom/figli_compta_ledger/js/home.js index 6373a86..e3ba48e 100644 --- a/web/modules/custom/figli_compta_ledger/js/home.js +++ b/web/modules/custom/figli_compta_ledger/js/home.js @@ -123,6 +123,31 @@ return { data: dedup, includedMap }; } + // GET /lignes/api/groupe/{nid} -- the full transitive closure of node + // ids connected via field_entree_liee (both directions), resolved + // server-side since the sliding window can't discover it client-side + // (see LedgerStatsController::groupeEntree()). + async function fetchGroupeIds(nid) { + const res = await fetch('/lignes/api/groupe/' + nid, { headers: { Accept: 'application/json' } }); + if (!res.ok) throw new Error('/lignes/api/groupe a répondu ' + res.status); + const json = await res.json(); + return json.nids || []; + } + + // Fetches full ligne data (same includes as the sliding window) for a + // specific set of node ids, regardless of date -- used to fill in + // whichever entrée/sortie nodes fetchGroupeIds() found that aren't in + // the currently loaded window. + async function fetchLignesByNids(nids) { + if (!nids.length) return { data: [], includedMap: new Map() }; + const params = new URLSearchParams(); + params.set('include', 'field_repartition,field_repartition.field_compte,field_client,field_entree_liee'); + params.set('filter[parNid][condition][path]', 'drupal_internal__nid'); + params.set('filter[parNid][condition][operator]', 'IN'); + nids.forEach((nid) => params.append('filter[parNid][condition][value][]', nid)); + return fetchLignes(API_BASE + '?' + params.toString()); + } + async function fetchClientNames() { // page[limit]=200 is silently clamped to core's hard cap of 50 by // JSON:API (Query\OffsetPage::SIZE_MAX) -- with 106 client terms, @@ -290,6 +315,14 @@ onlyErrors: false, hoverCol: null, filterEntreeId: null, + // Rows fetched to fill in entrées/sorties that /lignes/api/groupe + // found but the sliding window doesn't have loaded (a versement + // can link to an entrée from any prior year) -- see + // allKnownRows()/toggleEntreeFilter(). Kept around (not cleared + // on close) as a soft cache: reopening the same or an overlapping + // group doesn't need to re-fetch. + groupExtraRows: [], + groupLoading: false, // Which row's type badge is currently showing its inline