Ajoute field_ecart (Montant HT - somme répartition), stocké à la sauvegarde
Phase 1 du chantier filtrage serveur de /lignes : l'écart devient une vraie valeur stockée plutôt que recalculée à la volée en résolvant les paragraphes de répartition, pour que le futur filtre "Écarts uniquement" côté serveur puisse filtrer directement dessus. Contrairement au Montant TTC (protégé par un garde-fou pour ne jamais altérer un TTC historique), l'écart n'a pas de valeur passée à protéger -- il reflète l'état *actuel* de la répartition, donc recalculé sans condition à chaque sauvegarde. figli_compta_ledger_ node_presave() calculait déjà cette somme pour la validation répartition == HT ; le stockage était quasi gratuit à ajouter au même endroit. Migration (update hooks 8009/8010) : rétro-calcul mécanique pour les 1556 lignes existantes (tous types, contrairement aux migrations TVA/ cotisation qui ne concernaient que les entrées client) -- aucune ambiguïté à arbitrer, juste HT moins répartition. Vérifié indépendamment ligne par ligne après coup : 0 écart entre la valeur recalculée à la main et celle stockée par la migration. Nouveau flag d'état 'figli_compta_ledger.skip_revision', utilisé uniquement par cette migration : backfiller un champ purement calculé sur 1556 lignes déjà migrées n'est pas un changement éditorial qui justifie 1556 nouvelles révisions -- vérifié qu'aucune révision supplémentaire n'a été créée. Reste indépendant de skip_validation (déjà utilisé par tous les autres scripts de migration de ce module, et qui doit continuer à créer une révision). Restructuration de figli_compta_ledger_node_presave() : le calcul de l'écart et son stockage se font maintenant même sous skip_validation (seul le lancement de l'exception reste conditionnel) -- vérifié que l'exception se déclenche toujours normalement sur une vraie répartition incohérente, et que Montant TTC/Cotisation restent inchangés sur une sauvegarde qui ne touche ni HT ni TVA.
This commit is contained in:
@@ -7,6 +7,7 @@ dependencies:
|
|||||||
- field.field.node.ligne_comptable.field_cotisation_active
|
- field.field.node.ligne_comptable.field_cotisation_active
|
||||||
- field.field.node.ligne_comptable.field_cotisation_urssaf
|
- field.field.node.ligne_comptable.field_cotisation_urssaf
|
||||||
- field.field.node.ligne_comptable.field_date_ligne
|
- field.field.node.ligne_comptable.field_date_ligne
|
||||||
|
- field.field.node.ligne_comptable.field_ecart
|
||||||
- field.field.node.ligne_comptable.field_entree_liee
|
- field.field.node.ligne_comptable.field_entree_liee
|
||||||
- field.field.node.ligne_comptable.field_flag
|
- field.field.node.ligne_comptable.field_flag
|
||||||
- field.field.node.ligne_comptable.field_montant_ht
|
- field.field.node.ligne_comptable.field_montant_ht
|
||||||
@@ -175,5 +176,6 @@ content:
|
|||||||
placeholder: ''
|
placeholder: ''
|
||||||
third_party_settings: { }
|
third_party_settings: { }
|
||||||
hidden:
|
hidden:
|
||||||
|
field_ecart: true
|
||||||
promote: true
|
promote: true
|
||||||
sticky: true
|
sticky: true
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ dependencies:
|
|||||||
- field.field.node.ligne_comptable.field_cotisation_active
|
- field.field.node.ligne_comptable.field_cotisation_active
|
||||||
- field.field.node.ligne_comptable.field_cotisation_urssaf
|
- field.field.node.ligne_comptable.field_cotisation_urssaf
|
||||||
- field.field.node.ligne_comptable.field_date_ligne
|
- field.field.node.ligne_comptable.field_date_ligne
|
||||||
|
- field.field.node.ligne_comptable.field_ecart
|
||||||
- field.field.node.ligne_comptable.field_entree_liee
|
- field.field.node.ligne_comptable.field_entree_liee
|
||||||
- field.field.node.ligne_comptable.field_flag
|
- field.field.node.ligne_comptable.field_flag
|
||||||
- field.field.node.ligne_comptable.field_montant_ht
|
- field.field.node.ligne_comptable.field_montant_ht
|
||||||
@@ -65,6 +66,17 @@ content:
|
|||||||
third_party_settings: { }
|
third_party_settings: { }
|
||||||
weight: 0
|
weight: 0
|
||||||
region: content
|
region: content
|
||||||
|
field_ecart:
|
||||||
|
type: number_decimal
|
||||||
|
label: above
|
||||||
|
settings:
|
||||||
|
thousand_separator: ''
|
||||||
|
decimal_separator: .
|
||||||
|
scale: 2
|
||||||
|
prefix_suffix: true
|
||||||
|
third_party_settings: { }
|
||||||
|
weight: 13
|
||||||
|
region: content
|
||||||
field_entree_liee:
|
field_entree_liee:
|
||||||
type: entity_reference_label
|
type: entity_reference_label
|
||||||
label: above
|
label: above
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
uuid: 79bd7ee6-1e84-4edd-8065-775929c45fcb
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
config:
|
||||||
|
- field.storage.node.field_ecart
|
||||||
|
- node.type.ligne_comptable
|
||||||
|
id: node.ligne_comptable.field_ecart
|
||||||
|
field_name: field_ecart
|
||||||
|
entity_type: node
|
||||||
|
bundle: ligne_comptable
|
||||||
|
label: Écart
|
||||||
|
description: ''
|
||||||
|
required: false
|
||||||
|
translatable: true
|
||||||
|
default_value: { }
|
||||||
|
default_value_callback: ''
|
||||||
|
settings:
|
||||||
|
min: null
|
||||||
|
max: null
|
||||||
|
prefix: ''
|
||||||
|
suffix: ''
|
||||||
|
field_type: decimal
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
uuid: 95ae1d5b-db18-4ebf-93a4-34d6522a32d6
|
||||||
|
langcode: en
|
||||||
|
status: true
|
||||||
|
dependencies:
|
||||||
|
module:
|
||||||
|
- node
|
||||||
|
id: node.field_ecart
|
||||||
|
field_name: field_ecart
|
||||||
|
entity_type: node
|
||||||
|
type: decimal
|
||||||
|
settings:
|
||||||
|
precision: 12
|
||||||
|
scale: 2
|
||||||
|
module: core
|
||||||
|
locked: false
|
||||||
|
cardinality: 1
|
||||||
|
translatable: true
|
||||||
|
indexes: { }
|
||||||
|
persist_with_no_fields: false
|
||||||
|
custom_storage: false
|
||||||
@@ -226,6 +226,11 @@ function _figli_compta_ledger_create_node_type_ligne_comptable() {
|
|||||||
_figli_field('node', 'ligne_comptable', 'field_cotisation_active', 'Cotisation diffuseur URSSAF (1,1%)', 'boolean');
|
_figli_field('node', 'ligne_comptable', 'field_cotisation_active', 'Cotisation diffuseur URSSAF (1,1%)', 'boolean');
|
||||||
_figli_field('node', 'ligne_comptable', 'field_cotisation_urssaf', '1,1%', 'decimal', ['precision' => 12, 'scale' => 2]);
|
_figli_field('node', 'ligne_comptable', 'field_cotisation_urssaf', '1,1%', 'decimal', ['precision' => 12, 'scale' => 2]);
|
||||||
_figli_field('node', 'ligne_comptable', 'field_montant_ttc', 'Montant TTC (€)', 'decimal', ['precision' => 12, 'scale' => 2]);
|
_figli_field('node', 'ligne_comptable', 'field_montant_ttc', 'Montant TTC (€)', 'decimal', ['precision' => 12, 'scale' => 2]);
|
||||||
|
// Montant HT - somme(répartition), kept in sync at every save (see
|
||||||
|
// figli_compta_ledger_node_presave()) so /lignes' "Écarts uniquement"
|
||||||
|
// filter and its future server-side equivalent can filter on a real
|
||||||
|
// stored value instead of resolving répartition paragraphs per row.
|
||||||
|
_figli_field('node', 'ligne_comptable', 'field_ecart', 'Écart', 'decimal', ['precision' => 12, 'scale' => 2]);
|
||||||
_figli_field('node', 'ligne_comptable', 'field_notes', 'Notes / détail', 'string_long');
|
_figli_field('node', 'ligne_comptable', 'field_notes', 'Notes / détail', 'string_long');
|
||||||
|
|
||||||
// Free-tagging signalement (e.g. "client impayé", "à relancer") -- purely
|
// Free-tagging signalement (e.g. "client impayé", "à relancer") -- purely
|
||||||
@@ -723,3 +728,82 @@ function figli_compta_ledger_update_8008() {
|
|||||||
|
|
||||||
return "field_cotisation_urssaf corrigé (delta seul, pas HT + delta) pour $fixed lignes.";
|
return "field_cotisation_urssaf corrigé (delta seul, pas HT + delta) pour $fixed lignes.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds field_ecart (Montant HT - somme répartition) to ligne_comptable --
|
||||||
|
* kept in sync at every save from now on (see
|
||||||
|
* figli_compta_ledger_node_presave()), so the server-side filtering work
|
||||||
|
* planned for /lignes can filter on a real stored value instead of
|
||||||
|
* resolving répartition paragraphs per row on every request. Existing
|
||||||
|
* content backfilled separately, see figli_compta_ledger_update_8010().
|
||||||
|
*/
|
||||||
|
function figli_compta_ledger_update_8009() {
|
||||||
|
// No form widget added -- purely a diagnostic/filtering value, already
|
||||||
|
// fully represented in the edit form by Montant HT + the Répartition
|
||||||
|
// paragraphs it's derived from, and already shown on /lignes' own
|
||||||
|
// Écart column. Nothing new for an associate to look at here; simply
|
||||||
|
// never calling setComponent() for it on the form display is enough
|
||||||
|
// to keep it off that form.
|
||||||
|
_figli_field('node', 'ligne_comptable', 'field_ecart', 'Écart', 'decimal', ['precision' => 12, 'scale' => 2]);
|
||||||
|
|
||||||
|
$view_display = EntityViewDisplay::load('node.ligne_comptable.default');
|
||||||
|
if ($view_display && !$view_display->getComponent('field_ecart')) {
|
||||||
|
$view_display->setComponent('field_ecart', ['type' => 'number_decimal', 'weight' => 13])->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backfills field_ecart for every existing ligne_comptable (all types,
|
||||||
|
* unlike the TVA/cotisation migrations which only ever touched "Entrée
|
||||||
|
* client" lines -- répartition applies to every line regardless of
|
||||||
|
* type). Mechanical, not a judgment call like
|
||||||
|
* figli_compta_ledger_update_8007()'s TVA correction: Montant HT and the
|
||||||
|
* répartition amounts are both already-correct historical values, this
|
||||||
|
* just computes their difference and stores it, exactly what
|
||||||
|
* figli_compta_ledger_node_presave() will do going forward.
|
||||||
|
*
|
||||||
|
* Runs under two state flags, not just the usual skip_validation:
|
||||||
|
* skip_validation avoids tripping the répartition-sum exception on the
|
||||||
|
* ~18 lines with a known, deliberately-preserved historical mismatch
|
||||||
|
* (same as every other bulk script in this module); skip_revision
|
||||||
|
* additionally suppresses the forced-revision block in
|
||||||
|
* figli_compta_ledger_node_presave() -- backfilling a purely computed
|
||||||
|
* diagnostic field onto ~1500 already-migrated lines isn't an editorial
|
||||||
|
* change worth 1500 new revisions cluttering the audit trail that
|
||||||
|
* mechanism exists to protect. Montant TTC is never touched either way
|
||||||
|
* (skip_validation already keeps it out of reach, see
|
||||||
|
* figli_compta_ledger_node_presave()).
|
||||||
|
*/
|
||||||
|
function figli_compta_ledger_update_8010() {
|
||||||
|
$storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||||
|
$nids = $storage->getQuery()
|
||||||
|
->accessCheck(FALSE)
|
||||||
|
->condition('type', 'ligne_comptable')
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$filled = 0;
|
||||||
|
$skipped = 0;
|
||||||
|
\Drupal::state()->set('figli_compta_ledger.skip_validation', TRUE);
|
||||||
|
\Drupal::state()->set('figli_compta_ledger.skip_revision', TRUE);
|
||||||
|
foreach ($storage->loadMultiple($nids) as $node) {
|
||||||
|
if (!$node->hasField('field_montant_ht') || $node->get('field_montant_ht')->isEmpty()
|
||||||
|
|| !$node->hasField('field_repartition')) {
|
||||||
|
$skipped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ht = (float) $node->get('field_montant_ht')->value;
|
||||||
|
$somme = 0.0;
|
||||||
|
foreach ($node->get('field_repartition')->referencedEntities() as $paragraph) {
|
||||||
|
if ($paragraph->hasField('field_montant') && !$paragraph->get('field_montant')->isEmpty()) {
|
||||||
|
$somme += (float) $paragraph->get('field_montant')->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$node->set('field_ecart', round($ht - $somme, 2));
|
||||||
|
$node->save();
|
||||||
|
$filled++;
|
||||||
|
}
|
||||||
|
\Drupal::state()->delete('figli_compta_ledger.skip_validation');
|
||||||
|
\Drupal::state()->delete('figli_compta_ledger.skip_revision');
|
||||||
|
|
||||||
|
return "Écart calculé pour $filled lignes (aucune nouvelle révision créée), $skipped laissées vides (pas de Montant HT).";
|
||||||
|
}
|
||||||
|
|||||||
@@ -330,7 +330,11 @@ function figli_compta_ledger_node_form_ajax_submit(array $form, FormStateInterfa
|
|||||||
* being silently fixed. Set the 'figli_compta_ledger.skip_validation' state
|
* being silently fixed. Set the 'figli_compta_ledger.skip_validation' state
|
||||||
* flag around such a bulk import to bypass this check *and* the Montant TTC
|
* flag around such a bulk import to bypass this check *and* the Montant TTC
|
||||||
* auto-computation below; new lines entered by associates through the form
|
* auto-computation below; new lines entered by associates through the form
|
||||||
* are never exempted from either.
|
* are never exempted from either. field_ecart is the one thing still kept
|
||||||
|
* in sync even under skip_validation (see below) -- separately, a second
|
||||||
|
* flag ('figli_compta_ledger.skip_revision') additionally suppresses the
|
||||||
|
* forced-revision block, used only by figli_compta_ledger_update_8010()'s
|
||||||
|
* field_ecart backfill.
|
||||||
*/
|
*/
|
||||||
function figli_compta_ledger_node_presave(NodeInterface $node) {
|
function figli_compta_ledger_node_presave(NodeInterface $node) {
|
||||||
if ($node->bundle() !== 'ligne_comptable') {
|
if ($node->bundle() !== 'ligne_comptable') {
|
||||||
@@ -341,81 +345,89 @@ function figli_compta_ledger_node_presave(NodeInterface $node) {
|
|||||||
// acting user as its author -- this is the audit trail for a shared
|
// acting user as its author -- this is the audit trail for a shared
|
||||||
// ledger, not the répartition-sum check below, so it's never exempted
|
// ledger, not the répartition-sum check below, so it's never exempted
|
||||||
// (not even during a skip_validation import: migrated data still needs
|
// (not even during a skip_validation import: migrated data still needs
|
||||||
// an honest revision history).
|
// an honest revision history). The one deliberate exception:
|
||||||
$node->setNewRevision(TRUE);
|
// figli_compta_ledger_update_8010()'s one-off field_ecart backfill sets
|
||||||
$node->setRevisionLogMessage('');
|
// 'figli_compta_ledger.skip_revision' -- a purely computed diagnostic
|
||||||
$node->setRevisionUserId(\Drupal::currentUser()->id());
|
// value being backfilled onto ~1500 already-migrated lines isn't an
|
||||||
// setNewRevision() alone doesn't refresh this -- it stays whatever it
|
// editorial change worth 1500 new revisions cluttering the very audit
|
||||||
// was on the entity as loaded (the previous revision's timestamp),
|
// trail this exists to protect.
|
||||||
// silently mislabeling every edit with its predecessor's save time.
|
if (!\Drupal::state()->get('figli_compta_ledger.skip_revision', FALSE)) {
|
||||||
$node->setRevisionCreationTime(\Drupal::time()->getRequestTime());
|
$node->setNewRevision(TRUE);
|
||||||
|
$node->setRevisionLogMessage('');
|
||||||
|
$node->setRevisionUserId(\Drupal::currentUser()->id());
|
||||||
|
// setNewRevision() alone doesn't refresh this -- it stays whatever it
|
||||||
|
// was on the entity as loaded (the previous revision's timestamp),
|
||||||
|
// silently mislabeling every edit with its predecessor's save time.
|
||||||
|
$node->setRevisionCreationTime(\Drupal::time()->getRequestTime());
|
||||||
|
}
|
||||||
|
|
||||||
if (!$node->hasField('field_montant_ht')) {
|
if (!$node->hasField('field_montant_ht')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (\Drupal::state()->get('figli_compta_ledger.skip_validation', FALSE)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$skip_validation = \Drupal::state()->get('figli_compta_ledger.skip_validation', FALSE);
|
||||||
$montant_ht = (float) $node->get('field_montant_ht')->value;
|
$montant_ht = (float) $node->get('field_montant_ht')->value;
|
||||||
$tva = $node->hasField('field_tva') && !$node->get('field_tva')->isEmpty() ? (float) $node->get('field_tva')->value : 0.0;
|
|
||||||
$type = $node->hasField('field_type_ligne') ? $node->get('field_type_ligne')->value : NULL;
|
|
||||||
$cotisation_active = $node->hasField('field_cotisation_active') && !$node->get('field_cotisation_active')->isEmpty()
|
|
||||||
? (bool) $node->get('field_cotisation_active')->value : FALSE;
|
|
||||||
// Only "Entrée client" lines ever go through a client devis -- the
|
|
||||||
// only place the cotisation diffuseur is invoiced at all (see
|
|
||||||
// figli_compta_ledger_update_8006()'s docblock) -- so the checkbox is
|
|
||||||
// ignored for every other type even if it somehow ended up checked
|
|
||||||
// (e.g. a line's type changed after the fact).
|
|
||||||
$cotisation_applies = $type === 'entree' && $cotisation_active;
|
|
||||||
|
|
||||||
// Montant TTC is no longer entered by hand -- it's a pure function of
|
if (!$skip_validation) {
|
||||||
// Montant HT, TVA, and (entrée client only) the cotisation diffuseur
|
$tva = $node->hasField('field_tva') && !$node->get('field_tva')->isEmpty() ? (float) $node->get('field_tva')->value : 0.0;
|
||||||
// URSSAF cascade (see figli_compta_ledger_update_8004()/_8006()). Only
|
$type = $node->hasField('field_type_ligne') ? $node->get('field_type_ligne')->value : NULL;
|
||||||
// recompute it when HT, TVA, type, or the cotisation toggle actually
|
$cotisation_active = $node->hasField('field_cotisation_active') && !$node->get('field_cotisation_active')->isEmpty()
|
||||||
// changed, though, rather than on every save unconditionally: the TVA
|
? (bool) $node->get('field_cotisation_active')->value : FALSE;
|
||||||
// backfilled onto pre-migration lines (figli_compta_ledger_update_8005()/
|
// Only "Entrée client" lines ever go through a client devis -- the
|
||||||
// _8007()) is a best-effort reconstruction and doesn't reproduce every
|
// only place the cotisation diffuseur is invoiced at all (see
|
||||||
// historical Montant TTC to the exact centime, so blindly recomputing
|
// figli_compta_ledger_update_8006()'s docblock) -- so the checkbox is
|
||||||
// on an unrelated edit (fixing a typo in Libellé, say) would silently
|
// ignored for every other type even if it somehow ended up checked
|
||||||
// nudge an untouched historical value -- exactly what this module's
|
// (e.g. a line's type changed after the fact).
|
||||||
// "never correct historical data" rule exists to prevent. A genuinely
|
$cotisation_applies = $type === 'entree' && $cotisation_active;
|
||||||
// new/changed HT, TVA, type, or cotisation toggle has no such history
|
|
||||||
// to protect.
|
// Montant TTC is no longer entered by hand -- it's a pure function of
|
||||||
if ($node->hasField('field_montant_ttc')) {
|
// Montant HT, TVA, and (entrée client only) the cotisation diffuseur
|
||||||
$inputs_changed = $node->isNew();
|
// URSSAF cascade (see figli_compta_ledger_update_8004()/_8006()). Only
|
||||||
if (!$inputs_changed && isset($node->original)) {
|
// recompute it when HT, TVA, type, or the cotisation toggle actually
|
||||||
$original = $node->original;
|
// changed, though, rather than on every save unconditionally: the TVA
|
||||||
$orig_ht = $original->hasField('field_montant_ht') && !$original->get('field_montant_ht')->isEmpty()
|
// backfilled onto pre-migration lines (figli_compta_ledger_update_8005()/
|
||||||
? (float) $original->get('field_montant_ht')->value : NULL;
|
// _8007()) is a best-effort reconstruction and doesn't reproduce every
|
||||||
$orig_tva = $original->hasField('field_tva') && !$original->get('field_tva')->isEmpty()
|
// historical Montant TTC to the exact centime, so blindly recomputing
|
||||||
? (float) $original->get('field_tva')->value : 0.0;
|
// on an unrelated edit (fixing a typo in Libellé, say) would silently
|
||||||
$orig_type = $original->hasField('field_type_ligne') ? $original->get('field_type_ligne')->value : NULL;
|
// nudge an untouched historical value -- exactly what this module's
|
||||||
$orig_cotisation_active = $original->hasField('field_cotisation_active') && !$original->get('field_cotisation_active')->isEmpty()
|
// "never correct historical data" rule exists to prevent. A genuinely
|
||||||
? (bool) $original->get('field_cotisation_active')->value : FALSE;
|
// new/changed HT, TVA, type, or cotisation toggle has no such history
|
||||||
$inputs_changed = $orig_ht !== $montant_ht || abs($orig_tva - $tva) > 0.00005
|
// to protect.
|
||||||
|| $orig_type !== $type || $orig_cotisation_active !== $cotisation_active;
|
if ($node->hasField('field_montant_ttc')) {
|
||||||
}
|
$inputs_changed = $node->isNew();
|
||||||
if ($inputs_changed) {
|
if (!$inputs_changed && isset($node->original)) {
|
||||||
if ($cotisation_applies) {
|
$original = $node->original;
|
||||||
// field_cotisation_urssaf stores just the 1,1% itself (the
|
$orig_ht = $original->hasField('field_montant_ht') && !$original->get('field_montant_ht')->isEmpty()
|
||||||
// delta), not Montant HT + 1,1% -- that's what "1,1%" as a
|
? (float) $original->get('field_montant_ht')->value : NULL;
|
||||||
// column/field label means. TVA still applies to the augmented
|
$orig_tva = $original->hasField('field_tva') && !$original->get('field_tva')->isEmpty()
|
||||||
// base though, so $base (used for Montant TTC just below) stays
|
? (float) $original->get('field_tva')->value : 0.0;
|
||||||
// HT + delta; only what gets *written* to the field changes.
|
$orig_type = $original->hasField('field_type_ligne') ? $original->get('field_type_ligne')->value : NULL;
|
||||||
$delta = round($montant_ht * 0.011, 2);
|
$orig_cotisation_active = $original->hasField('field_cotisation_active') && !$original->get('field_cotisation_active')->isEmpty()
|
||||||
$base = $montant_ht + $delta;
|
? (bool) $original->get('field_cotisation_active')->value : FALSE;
|
||||||
if ($node->hasField('field_cotisation_urssaf')) {
|
$inputs_changed = $orig_ht !== $montant_ht || abs($orig_tva - $tva) > 0.00005
|
||||||
$node->set('field_cotisation_urssaf', $delta);
|
|| $orig_type !== $type || $orig_cotisation_active !== $cotisation_active;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
if ($inputs_changed) {
|
||||||
$base = $montant_ht;
|
if ($cotisation_applies) {
|
||||||
if ($node->hasField('field_cotisation_urssaf')) {
|
// field_cotisation_urssaf stores just the 1,1% itself (the
|
||||||
$node->set('field_cotisation_urssaf', NULL);
|
// 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', $delta);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
$base = $montant_ht;
|
||||||
|
if ($node->hasField('field_cotisation_urssaf')) {
|
||||||
|
$node->set('field_cotisation_urssaf', NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$node->set('field_montant_ttc', round($base * (1 + $tva / 100), 2));
|
||||||
}
|
}
|
||||||
$node->set('field_montant_ttc', round($base * (1 + $tva / 100), 2));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,6 +443,21 @@ function figli_compta_ledger_node_presave(NodeInterface $node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$ecart = round($montant_ht - $somme, 2);
|
$ecart = round($montant_ht - $somme, 2);
|
||||||
|
|
||||||
|
// field_ecart mirrors this sum -- kept in sync unconditionally,
|
||||||
|
// regardless of skip_validation (see figli_compta_ledger_update_8009()'s
|
||||||
|
// docblock): unlike Montant TTC, there's no historical value to protect
|
||||||
|
// here, the field's whole purpose is to reflect the *current*
|
||||||
|
// répartition state, not a preserved snapshot -- and it's what /lignes'
|
||||||
|
// "Écarts uniquement" filter will eventually filter on server-side.
|
||||||
|
if ($node->hasField('field_ecart')) {
|
||||||
|
$node->set('field_ecart', $ecart);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($skip_validation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (abs($ecart) > 0.01) {
|
if (abs($ecart) > 0.01) {
|
||||||
throw new EntityStorageException(sprintf(
|
throw new EntityStorageException(sprintf(
|
||||||
"Répartition incohérente : la somme des comptes (%.2f €) ne correspond pas au montant HT (%.2f €). Écart : %.2f €. Corrigez la répartition avant d'enregistrer.",
|
"Répartition incohérente : la somme des comptes (%.2f €) ne correspond pas au montant HT (%.2f €). Écart : %.2f €. Corrigez la répartition avant d'enregistrer.",
|
||||||
|
|||||||
Reference in New Issue
Block a user