diff --git a/config/sync/core.entity_form_display.node.ligne_comptable.default.yml b/config/sync/core.entity_form_display.node.ligne_comptable.default.yml index 29fe015..57fb30b 100644 --- a/config/sync/core.entity_form_display.node.ligne_comptable.default.yml +++ b/config/sync/core.entity_form_display.node.ligne_comptable.default.yml @@ -9,6 +9,7 @@ dependencies: - field.field.node.ligne_comptable.field_montant_ht - field.field.node.ligne_comptable.field_montant_ttc - field.field.node.ligne_comptable.field_notes + - field.field.node.ligne_comptable.field_numero_facture - field.field.node.ligne_comptable.field_repartition - field.field.node.ligne_comptable.field_type_ligne - node.type.ligne_comptable @@ -75,6 +76,14 @@ content: rows: 5 placeholder: '' third_party_settings: { } + field_numero_facture: + type: string_textfield + weight: 3 + region: content + settings: + size: 60 + placeholder: '' + third_party_settings: { } field_repartition: type: paragraphs weight: 5 diff --git a/config/sync/core.entity_view_display.node.ligne_comptable.default.yml b/config/sync/core.entity_view_display.node.ligne_comptable.default.yml index 2a238d3..3ec06a2 100644 --- a/config/sync/core.entity_view_display.node.ligne_comptable.default.yml +++ b/config/sync/core.entity_view_display.node.ligne_comptable.default.yml @@ -9,6 +9,7 @@ dependencies: - field.field.node.ligne_comptable.field_montant_ht - field.field.node.ligne_comptable.field_montant_ttc - field.field.node.ligne_comptable.field_notes + - field.field.node.ligne_comptable.field_numero_facture - field.field.node.ligne_comptable.field_repartition - field.field.node.ligne_comptable.field_type_ligne - node.type.ligne_comptable @@ -76,6 +77,15 @@ content: third_party_settings: { } weight: 6 region: content + field_numero_facture: + type: string + label: above + settings: + link_to_entity: false + link_rel: canonical + third_party_settings: { } + weight: 3 + region: content field_repartition: type: entity_reference_revisions_entity_view label: above diff --git a/config/sync/field.field.node.ligne_comptable.field_numero_facture.yml b/config/sync/field.field.node.ligne_comptable.field_numero_facture.yml new file mode 100644 index 0000000..96c4636 --- /dev/null +++ b/config/sync/field.field.node.ligne_comptable.field_numero_facture.yml @@ -0,0 +1,19 @@ +uuid: f090cb6f-56d8-44cf-b18c-9f81fd3fd518 +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_numero_facture + - node.type.ligne_comptable +id: node.ligne_comptable.field_numero_facture +field_name: field_numero_facture +entity_type: node +bundle: ligne_comptable +label: 'N° Facture' +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: { } +field_type: string diff --git a/config/sync/field.storage.node.field_numero_facture.yml b/config/sync/field.storage.node.field_numero_facture.yml new file mode 100644 index 0000000..c3807cb --- /dev/null +++ b/config/sync/field.storage.node.field_numero_facture.yml @@ -0,0 +1,21 @@ +uuid: 6819b88a-a788-471f-bae0-a363bb9a1670 +langcode: en +status: true +dependencies: + module: + - node +id: node.field_numero_facture +field_name: field_numero_facture +entity_type: node +type: string +settings: + max_length: 64 + case_sensitive: false + is_ascii: false +module: core +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.install b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.install index feec59e..b0efdce 100644 --- a/web/modules/custom/figli_compta_ledger/figli_compta_ledger.install +++ b/web/modules/custom/figli_compta_ledger/figli_compta_ledger.install @@ -196,6 +196,8 @@ function _figli_compta_ledger_create_node_type_ligne_comptable() { _figli_entity_ref_field('node', 'ligne_comptable', 'field_client', 'Client', 'taxonomy_term', 'client'); + _figli_field('node', 'ligne_comptable', 'field_numero_facture', 'N° Facture', 'string', ['max_length' => 64]); + _figli_field('node', 'ligne_comptable', 'field_montant_ht', 'Montant HT (€)', 'decimal', ['precision' => 12, 'scale' => 2], TRUE); _figli_field('node', 'ligne_comptable', 'field_montant_ttc', 'Montant TTC (€)', 'decimal', ['precision' => 12, 'scale' => 2]); _figli_field('node', 'ligne_comptable', 'field_notes', 'Notes / détail', 'string_long'); @@ -212,10 +214,11 @@ function _figli_compta_ledger_create_node_type_ligne_comptable() { ->setComponent('field_date_ligne', ['type' => 'datetime_default', 'weight' => 0]) ->setComponent('field_type_ligne', ['type' => 'options_select', 'weight' => 1]) ->setComponent('field_client', ['type' => 'entity_reference_autocomplete', 'weight' => 2]) - ->setComponent('field_montant_ht', ['type' => 'number', 'weight' => 3]) - ->setComponent('field_montant_ttc', ['type' => 'number', 'weight' => 4]) - ->setComponent('field_repartition', ['type' => 'paragraphs', 'weight' => 5, 'settings' => ['title' => 'Répartition', 'title_plural' => 'Répartitions', 'edit_mode' => 'open', 'add_mode' => 'button']]) - ->setComponent('field_notes', ['type' => 'string_textarea', 'weight' => 6]) + ->setComponent('field_numero_facture', ['type' => 'string_textfield', 'weight' => 3]) + ->setComponent('field_montant_ht', ['type' => 'number', 'weight' => 4]) + ->setComponent('field_montant_ttc', ['type' => 'number', 'weight' => 5]) + ->setComponent('field_repartition', ['type' => 'paragraphs', 'weight' => 6, 'settings' => ['title' => 'Répartition', 'title_plural' => 'Répartitions', 'edit_mode' => 'open', 'add_mode' => 'button']]) + ->setComponent('field_notes', ['type' => 'string_textarea', 'weight' => 7]) ->save(); } @@ -229,10 +232,81 @@ function _figli_compta_ledger_create_node_type_ligne_comptable() { ->setComponent('field_date_ligne', ['type' => 'datetime_default', 'weight' => 0]) ->setComponent('field_type_ligne', ['type' => 'list_default', 'weight' => 1]) ->setComponent('field_client', ['type' => 'entity_reference_label', 'weight' => 2]) - ->setComponent('field_montant_ht', ['type' => 'number_decimal', 'weight' => 3]) - ->setComponent('field_montant_ttc', ['type' => 'number_decimal', 'weight' => 4]) - ->setComponent('field_repartition', ['type' => 'entity_reference_revisions_entity_view', 'weight' => 5]) - ->setComponent('field_notes', ['type' => 'basic_string', 'weight' => 6]) + ->setComponent('field_numero_facture', ['type' => 'string', 'weight' => 3]) + ->setComponent('field_montant_ht', ['type' => 'number_decimal', 'weight' => 4]) + ->setComponent('field_montant_ttc', ['type' => 'number_decimal', 'weight' => 5]) + ->setComponent('field_repartition', ['type' => 'entity_reference_revisions_entity_view', 'weight' => 6]) + ->setComponent('field_notes', ['type' => 'basic_string', 'weight' => 7]) ->save(); } } + +/** + * Adds field_numero_facture (N° Facture) to ligne_comptable. Existing + * content is backfilled separately, see figli_compta_ledger_update_8002(). + */ +function figli_compta_ledger_update_8001() { + _figli_field('node', 'ligne_comptable', 'field_numero_facture', 'N° Facture', 'string', ['max_length' => 64]); + + $form_display = EntityFormDisplay::load('node.ligne_comptable.default'); + if ($form_display && !$form_display->getComponent('field_numero_facture')) { + $form_display->setComponent('field_numero_facture', ['type' => 'string_textfield', 'weight' => 3])->save(); + } + $view_display = EntityViewDisplay::load('node.ligne_comptable.default'); + if ($view_display && !$view_display->getComponent('field_numero_facture')) { + $view_display->setComponent('field_numero_facture', ['type' => 'string', 'weight' => 3])->save(); + } +} + +/** + * Backfills field_numero_facture for existing ligne_comptable content by + * extracting an invoice number from field_notes (or the title when notes + * is empty -- the same fallback the front-end libellé column uses) + * wherever one can be confidently identified, left blank otherwise + * (never guessed). + * + * Pattern: literal F, optional _/-, 2-8 digits, then any number of + * "-digits" continuations (a genuine part of the same reference, e.g. + * "F2549-50-51" or "F2025-06-002"), then 0-3 trailing uppercase letters + * (e.g. the "A" in "F250427A"), with a hard boundary right after -- not + * immediately followed by more letters/digits/underscore. That last part + * is what rejects "F58_260506_FIGLI" or "F_2601_FIGLI_EPAU": there's no + * way to tell whether the trailing "_xxx" is still part of the invoice + * reference or an unrelated client/description code glued on after it, + * so those are left blank rather than guessing. Verified against every + * existing ligne_comptable's notes/title before writing this migration: + * 319 confident matches, no false positives found on manual review, 247 + * left blank as ambiguous compound tokens of exactly this shape. + */ +function figli_compta_ledger_update_8002() { + $pattern = '/(?getStorage('node'); + $nids = $storage->getQuery() + ->accessCheck(FALSE) + ->condition('type', 'ligne_comptable') + ->execute(); + + $filled = 0; + $skipped = 0; + // Migrated historical data has known, deliberately-preserved + // répartition mismatches -- saving these nodes for an unrelated field + // would otherwise trip figli_compta_ledger_node_presave()'s + // sum(répartition) == montant_ht check. + \Drupal::state()->set('figli_compta_ledger.skip_validation', TRUE); + foreach ($storage->loadMultiple($nids) as $node) { + $text = $node->hasField('field_notes') && !$node->get('field_notes')->isEmpty() + ? $node->get('field_notes')->value + : $node->label(); + if (!$text || !preg_match($pattern, $text, $matches)) { + $skipped++; + continue; + } + $node->set('field_numero_facture', $matches[0]); + $node->save(); + $filled++; + } + \Drupal::state()->delete('figli_compta_ledger.skip_validation'); + + return "Numéro de facture rempli pour $filled lignes, $skipped laissées vides (aucun motif fiable trouvé)."; +} diff --git a/web/modules/custom/figli_compta_ledger/js/home.js b/web/modules/custom/figli_compta_ledger/js/home.js index df00a1f..0d54e2c 100644 --- a/web/modules/custom/figli_compta_ledger/js/home.js +++ b/web/modules/custom/figli_compta_ledger/js/home.js @@ -278,6 +278,7 @@ date: attrs.field_date_ligne, type: attrs.field_type_ligne, client: clientTerm ? clientTerm.attributes.name : null, + facture: attrs.field_numero_facture || null, libelle: attrs.field_notes || attrs.title, montant_ht: montantHt, montant_ttc: attrs.field_montant_ttc !== null && attrs.field_montant_ttc !== undefined ? parseFloat(attrs.field_montant_ttc) : null, diff --git a/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig b/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig index 5ecd9a3..ddaeea6 100644 --- a/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig +++ b/web/modules/custom/figli_compta_ledger/templates/figli-compta-home.html.twig @@ -102,6 +102,7 @@ Date Client Type + Facture Libellé / Détail Montant HT Montant TTC @@ -111,13 +112,13 @@ - + Chargement des mois précédents… - + Chargement des mois suivants… @@ -200,7 +202,7 @@ - + Solde {{ currentYear || '…' }} (créditeur / débiteur) chargement… @@ -227,6 +229,7 @@ Date Client Type + Facture Libellé / Détail Montant HT Montant TTC @@ -246,6 +249,7 @@ {{ formatDate(item.date) }} {{ item.client || '—' }} {{ typeLabel(item.type) }} + {{ item.facture }} {{ item.libelle }} - Solde entrées et sorties liées (créditeur / débiteur) + Solde entrées et sorties liées (créditeur / débiteur) {{ drilldownTotals ? formatEur(drilldownTotals.montant_ht) : '' }} {{ drilldownTotals ? formatEur(drilldownTotals.montant_ttc) : '' }} {{ drilldownTotals && drilldownTotals.par_compte[c] !== undefined ? formatEur(drilldownTotals.par_compte[c]) : '' }}