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