Le champ/colonne "1,1%" affiche le delta seul, pas HT + 1,1%

field_cotisation_urssaf stockait round(HT * 1.011, 2) (le montant
augmenté) -- "1,1%" comme libellé de champ/colonne désigne la
cotisation elle-même, pas HT + cotisation. Change pour
round(HT * 0.011, 2). La TVA continue de s'appliquer sur la base
augmentée (HT + ce champ) : Montant TTC est inchangé par cette
correction, seule la valeur affichée/stockée dans "1,1%" change.

Migration (update hook 8008) : recalcule field_cotisation_urssaf pour
les 154 lignes que figli_compta_ledger_update_8007() avait marquées
cotisation active, en soustrayant simplement le HT déjà correct --
contrairement à cette dernière, ce n'est pas un arbitrage sur des
données historiques ambiguës, juste un bug dans du code écrit plus tôt
le même jour, donc recalculé sans condition ni prudence particulière.

Ajoute aussi le total "1,1%" au pied du tableau (solde de l'année),
absent jusqu'ici : LedgerStatsController::totauxAnnee() somme
maintenant field_cotisation_urssaf comme il le fait déjà pour HT/TTC.
This commit is contained in:
2026-09-07 14:38:10 +02:00
parent 6c33bc0098
commit 0baa03855b
5 changed files with 64 additions and 6 deletions
@@ -685,3 +685,41 @@ function figli_compta_ledger_update_8007() {
return "Cotisation/TVA : $corrected lignes corrigées (cotisation active), $alreadyOfficial déjà correctes (cotisation inactive), $stillAmbiguous laissées telles quelles (taux non standard des deux façons), $noData sans HT/TTC, $skipped2021 lignes 2021 ignorées.";
}
/**
* Corrects what field_cotisation_urssaf stores for the lines
* figli_compta_ledger_update_8007() marked cotisation-active: that
* migration (and figli_compta_ledger_node_presave() as first written)
* stored Montant HT + 1,1% there, but "1,1%" as a field/column label
* means just the 1,1% itself -- the delta, not the augmented total.
* TVA still applies to the augmented base (Montant HT + this field), so
* Montant TTC is untouched by this correction; only
* field_cotisation_urssaf's own value changes, from round(HT * 1.011, 2)
* to round(HT * 0.011, 2). Safe to run unconditionally on every
* cotisation-active line -- this is a bug in code from earlier the same
* day, not a judgment call about ambiguous historical data like
* figli_compta_ledger_update_8007()'s.
*/
function figli_compta_ledger_update_8008() {
$storage = \Drupal::entityTypeManager()->getStorage('node');
$nids = $storage->getQuery()
->accessCheck(FALSE)
->condition('type', 'ligne_comptable')
->condition('field_cotisation_active', 1)
->execute();
$fixed = 0;
\Drupal::state()->set('figli_compta_ledger.skip_validation', TRUE);
foreach ($storage->loadMultiple($nids) as $node) {
if (!$node->hasField('field_montant_ht') || $node->get('field_montant_ht')->isEmpty()) {
continue;
}
$ht = (float) $node->get('field_montant_ht')->value;
$node->set('field_cotisation_urssaf', round($ht * 0.011, 2));
$node->save();
$fixed++;
}
\Drupal::state()->delete('figli_compta_ledger.skip_validation');
return "field_cotisation_urssaf corrigé (delta seul, pas HT + delta) pour $fixed lignes.";
}
@@ -137,7 +137,7 @@ function figli_compta_ledger_form_alter(&$form, FormStateInterface $form_state,
'visible' => [':input[name="field_type_ligne"]' => ['value' => 'entree']],
];
$form['field_cotisation_urssaf']['widget'][0]['value']['#disabled'] = TRUE;
$form['field_cotisation_urssaf']['widget'][0]['value']['#description'] = t('Calculé automatiquement (Montant HT + 1,1%) si la cotisation ci-dessus est cochée.');
$form['field_cotisation_urssaf']['widget'][0]['value']['#description'] = t('Calculé automatiquement (1,1% du montant HT) si la cotisation ci-dessus est cochée. La TVA est ensuite appliquée sur Montant HT + ce montant.');
}
// TVA: a select of the official French rates covers the overwhelming
@@ -398,9 +398,15 @@ function figli_compta_ledger_node_presave(NodeInterface $node) {
}
if ($inputs_changed) {
if ($cotisation_applies) {
$base = round($montant_ht * 1.011, 2);
// field_cotisation_urssaf stores just the 1,1% itself (the
// delta), not Montant HT + 1,1% -- that's what "1,1%" as a
// column/field label means. TVA still applies to the augmented
// base though, so $base (used for Montant TTC just below) stays
// HT + delta; only what gets *written* to the field changes.
$delta = round($montant_ht * 0.011, 2);
$base = $montant_ht + $delta;
if ($node->hasField('field_cotisation_urssaf')) {
$node->set('field_cotisation_urssaf', $base);
$node->set('field_cotisation_urssaf', $delta);
}
}
else {
@@ -63,9 +63,14 @@
}
var base = htValue;
if (cotisationApplies()) {
base = Math.round(htValue * 1.011 * 100) / 100;
// Displayed/stored value is just the 1,1% itself (the
// delta), not HT + 1,1% -- but TVA still applies to the
// augmented base, so $base for the TTC calc below stays
// htValue + delta regardless.
var delta = Math.round(htValue * 0.011 * 100) / 100;
base = htValue + delta;
if (cotisationAmount) {
cotisationAmount.value = base.toFixed(2);
cotisationAmount.value = delta.toFixed(2);
}
}
else if (cotisationAmount) {
@@ -43,6 +43,7 @@ class LedgerStatsController extends ControllerBase {
$par_compte = [];
$montant_ht = 0.0;
$cotisation = 0.0;
$montant_ttc = 0.0;
$ecart = 0.0;
foreach ($storage->loadMultiple($nids) as $node) {
@@ -52,6 +53,13 @@ class LedgerStatsController extends ControllerBase {
? (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) {
@@ -71,6 +79,7 @@ class LedgerStatsController extends ControllerBase {
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),
@@ -263,7 +263,7 @@
<span v-if="currentYearLoading" class="figli-note">chargement…</span>
</td>
<td class="amount figli-ht-col">{{ currentYearTotals ? formatEur(currentYearTotals.montant_ht) : '' }}</td>
<td class="amount figli-cotisation-col"></td>
<td class="amount figli-cotisation-col">{{ currentYearTotals ? formatEur(currentYearTotals.cotisation) : '' }}</td>
<td class="amount figli-tva-col"></td>
<td class="amount figli-ttc-col">{{ currentYearTotals ? formatEur(currentYearTotals.montant_ttc) : '' }}</td>
<td v-for="c in allComptes" :key="c" class="amount compte-col" :class="currentYearTotals ? soldeClass(currentYearTotals.par_compte[c]) : ''">{{ currentYearTotals && currentYearTotals.par_compte[c] !== undefined ? formatEur(currentYearTotals.par_compte[c]) : '' }}</td>