Add solde totals footer row + fix JSON:API pagination duplicate bug
- tfoot row: sum per compte (créditeur/débiteur colored) for the currently filtered rows, plus HT/TTC/écart totals - Fixed a real bug: fetchAllLignes() paginated without a unique sort key (field_date_ligne alone, many ties), which let Drupal's JSON:API return the same row on two pages -- silently inflating totals (Bachir showed -3115,38€ instead of -3013,56€). Now sorts by field_date_ligne,drupal_internal__nid (home) / drupal_internal__nid (dashboard), plus a defensive client-side de-dup by node id either way. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,11 @@
|
||||
};
|
||||
|
||||
async function fetchAllLignes() {
|
||||
let url = API_BASE + '?include=field_repartition,field_repartition.field_compte,field_client&page[limit]=50&sort=field_date_ligne';
|
||||
// sort includes drupal_internal__nid as a tie-breaker: field_date_ligne
|
||||
// alone is not unique (many lines share a date), and without a unique
|
||||
// secondary sort key, offset pagination can silently duplicate or skip
|
||||
// rows across pages.
|
||||
let url = API_BASE + '?include=field_repartition,field_repartition.field_compte,field_client&page[limit]=50&sort=field_date_ligne,drupal_internal__nid';
|
||||
const allData = [];
|
||||
const includedMap = new Map();
|
||||
while (url) {
|
||||
@@ -33,7 +37,10 @@
|
||||
(json.included || []).forEach((item) => includedMap.set(item.type + ':' + item.id, item));
|
||||
url = json.links && json.links.next ? json.links.next.href : null;
|
||||
}
|
||||
return { data: allData, includedMap };
|
||||
// Defensive de-dup by node id, in case pagination ever repeats a row.
|
||||
const seen = new Set();
|
||||
const dedup = allData.filter((n) => (seen.has(n.id) ? false : (seen.add(n.id), true)));
|
||||
return { data: dedup, includedMap };
|
||||
}
|
||||
|
||||
function resolve(includedMap, ref) {
|
||||
@@ -107,6 +114,20 @@
|
||||
errorCount() {
|
||||
return this.rows.filter((r) => r.hasError).length;
|
||||
},
|
||||
footerTotals() {
|
||||
const parCompte = {};
|
||||
this.allComptes.forEach((c) => { parCompte[c] = 0; });
|
||||
let montantHt = 0, montantTtc = 0, ecart = 0;
|
||||
for (const r of this.filteredRows) {
|
||||
montantHt += r.montant_ht || 0;
|
||||
montantTtc += r.montant_ttc || 0;
|
||||
ecart += r.ecart || 0;
|
||||
for (const c of this.allComptes) {
|
||||
if (r.parCompte[c] !== undefined) parCompte[c] += r.parCompte[c];
|
||||
}
|
||||
}
|
||||
return { montant_ht: montantHt, montant_ttc: montantTtc, parCompte, ecart };
|
||||
},
|
||||
filteredRows() {
|
||||
return this.rows.filter((r) => {
|
||||
if (this.filterCompte && r.parCompte[this.filterCompte] === undefined) return false;
|
||||
@@ -151,6 +172,11 @@
|
||||
typeLabel(t) {
|
||||
return TYPE_LABELS[t] || t;
|
||||
},
|
||||
soldeClass(v) {
|
||||
if (v > 0.5) return 'figli-solde-crediteur';
|
||||
if (v < -0.5) return 'figli-solde-debiteur';
|
||||
return '';
|
||||
},
|
||||
openAddForm() {
|
||||
Drupal.ajax({
|
||||
url: '/node/add/ligne_comptable',
|
||||
|
||||
Reference in New Issue
Block a user