Les totaux du footer de /lignes suivent maintenant les filtres actifs
Remplace /lignes/api/totaux (LedgerStatsController::totauxAnnee(), toujours non filtré) par une agrégation côté client des lignes renvoyées par LedgerRowsController::index() (même endpoint que la fenêtre glissante) avec annee=<année visible> + les filtres actifs. Reste une requête serveur dédiée sur l'année entière (pas de LIMIT/range sur la requête Entity), donc le total ne dépend jamais de ce qui est effectivement chargé dans la fenêtre glissante à cet instant -- vérifié : 207 lignes non filtrées vs 13 avec le filtre "OVH", total du footer identique au calcul indépendant dans les deux cas. onFilterChanged() déclenche maintenant systématiquement loadCurrentYearTotals() (pas seulement quand detectCurrentYear() détecte un changement d'année) -- sinon changer un filtre en restant sur la même année laissait le footer afficher l'ancien total non filtré. totauxAnnee() et sa route sont supprimés (plus aucun appelant après ce changement, vérifié par recherche).
This commit is contained in:
@@ -60,9 +60,9 @@ class DashboardStatsController extends ControllerBase {
|
||||
// share, pre-summed per (annee, compte, type) in SQL -- backs solde
|
||||
// par compte, both all-time and per-year (each year's own total
|
||||
// already includes that year's ouverture line, so it *is* that
|
||||
// year's closing balance -- same logic as
|
||||
// LedgerStatsController::totauxAnnee()), and the per-compte type
|
||||
// breakdown used by /dashboard/compte.
|
||||
// year's closing balance -- same logic as home.js's
|
||||
// aggregateTotals()/fetchYearTotals() use for the /lignes footer),
|
||||
// and the per-compte type breakdown used by /dashboard/compte.
|
||||
$compteQuery = $connection->select('node__field_date_ligne', 'd');
|
||||
$compteQuery->innerJoin('node__field_type_ligne', 't2', 't2.entity_id = d.entity_id');
|
||||
$compteQuery->innerJoin('node__field_repartition', 'r', 'r.entity_id = d.entity_id');
|
||||
|
||||
@@ -5,87 +5,20 @@ namespace Drupal\figli_compta_ledger\Controller;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Small aggregate endpoints backing the /lignes sliding window: the
|
||||
* row-level JSON:API fetch only ever covers a date range (see home.js), so
|
||||
* neither the totals footer nor the "Année" dropdown can be computed from
|
||||
* whatever's currently loaded -- they need their own always-accurate
|
||||
* queries, decoupled from the row window.
|
||||
* row-level fetch only ever covers a date range (see home.js), so the
|
||||
* "Année" dropdown can't be computed from whatever's currently loaded --
|
||||
* it needs its own always-accurate query, decoupled from the row window.
|
||||
* The footer totals used to live here too (totauxAnnee(), unfiltered)
|
||||
* until the footer needed to reflect the active toolbar filters -- it's
|
||||
* now computed client-side in home.js from LedgerRowsController::index()
|
||||
* rows instead, the same server-side-filtered endpoint the table itself
|
||||
* uses.
|
||||
*/
|
||||
class LedgerStatsController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* GET /lignes/api/totaux?annee=2023 -- per-compte répartition sums (plus
|
||||
* montant HT/TTC/écart totals) for every ligne_comptable dated that
|
||||
* year, including ouverture lines: the footer is meant to read as the
|
||||
* actual account balance (solde) for the year, i.e. the same "clôture
|
||||
* calculée" (ouverture + every movement dated within the year) that
|
||||
* reconciliationOuverture() compares the *next* year's ouverture
|
||||
* against. Excluding ouverture here would make this a net-movement
|
||||
* figure instead, which never matches what the reconciliation badge's
|
||||
* tooltip cites for the same year.
|
||||
*/
|
||||
public function totauxAnnee(Request $request) {
|
||||
$annee = $request->query->get('annee');
|
||||
if (!$annee || !preg_match('/^\d{4}$/', $annee)) {
|
||||
return new JsonResponse(['error' => 'Paramètre "annee" invalide.'], 400);
|
||||
}
|
||||
|
||||
$storage = $this->entityTypeManager()->getStorage('node');
|
||||
$nids = $storage->getQuery()
|
||||
->accessCheck(TRUE)
|
||||
->condition('type', 'ligne_comptable')
|
||||
->condition('field_date_ligne', $annee . '-01-01', '>=')
|
||||
->condition('field_date_ligne', ((int) $annee + 1) . '-01-01', '<')
|
||||
->execute();
|
||||
|
||||
$par_compte = [];
|
||||
$montant_ht = 0.0;
|
||||
$cotisation = 0.0;
|
||||
$montant_ttc = 0.0;
|
||||
$ecart = 0.0;
|
||||
foreach ($storage->loadMultiple($nids) as $node) {
|
||||
$ht = $node->hasField('field_montant_ht') && !$node->get('field_montant_ht')->isEmpty()
|
||||
? (float) $node->get('field_montant_ht')->value : 0.0;
|
||||
$ttc = $node->hasField('field_montant_ttc') && !$node->get('field_montant_ttc')->isEmpty()
|
||||
? (float) $node->get('field_montant_ttc')->value : 0.0;
|
||||
$montant_ht += $ht;
|
||||
$montant_ttc += $ttc;
|
||||
// Blank (never 0) for most lines -- only "Entrée client" lines
|
||||
// with the cotisation checkbox on ever have this field set (see
|
||||
// figli_compta_ledger_node_presave()) -- but summing a blank
|
||||
// value as 0 here is exactly right for a total.
|
||||
if ($node->hasField('field_cotisation_urssaf') && !$node->get('field_cotisation_urssaf')->isEmpty()) {
|
||||
$cotisation += (float) $node->get('field_cotisation_urssaf')->value;
|
||||
}
|
||||
|
||||
$somme = 0.0;
|
||||
foreach ($node->get('field_repartition')->referencedEntities() as $paragraph) {
|
||||
if (!$paragraph->hasField('field_montant') || $paragraph->get('field_montant')->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
$montant = (float) $paragraph->get('field_montant')->value;
|
||||
$compte = $paragraph->get('field_compte')->entity ? $paragraph->get('field_compte')->entity->label() : NULL;
|
||||
if ($compte) {
|
||||
$par_compte[$compte] = ($par_compte[$compte] ?? 0) + $montant;
|
||||
}
|
||||
$somme += $montant;
|
||||
}
|
||||
$ecart += round($ht - $somme, 2);
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'annee' => $annee,
|
||||
'montant_ht' => round($montant_ht, 2),
|
||||
'cotisation' => round($cotisation, 2),
|
||||
'montant_ttc' => round($montant_ttc, 2),
|
||||
'ecart' => round($ecart, 2),
|
||||
'par_compte' => array_map(fn ($v) => round($v, 2), $par_compte),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /lignes/api/annees -- distinct years, most recent first, with at
|
||||
* least 5 lines. The threshold exists specifically to keep the handful
|
||||
|
||||
Reference in New Issue
Block a user