Add field_numero_facture (N° Facture), a Facture column, and backfill existing content
New plain string field on ligne_comptable, positioned after Client in both form and view displays. Added as a "Facture" column in both the main table and the entrée/sortie drill-down modal, right after Type. Backfilled via figli_compta_ledger_update_8002() by extracting an invoice number from field_notes (or title, same fallback the front-end libellé already uses) wherever the pattern is unambiguous: literal F, optional _/-, 2-8 digits, optional "-digits" continuations for a compound reference (e.g. "F2549-50-51"), optional 0-3 trailing uppercase letters (e.g. "F250427A"), with a hard boundary right after -- not immediately followed by more letters/digits/underscore. That last part is what skips "F58_260506_FIGLI" or "F_2601_FIGLI_EPAU": no way to tell whether the trailing "_xxx" belongs to the reference or is an unrelated client/description code glued on, so those are left blank rather than guessed, per explicit instruction to skip when unsure. Verified the pattern against every existing ligne_comptable's notes/title before writing the migration: 319 confident matches with no false positives found on manual review of the full list, 1236 left blank. Applied via drush updb, config exported.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 = '/(?<![A-Za-z0-9])F[_-]?\d{2,8}(?:-\d+)*[A-Z]{0,3}(?![A-Za-z0-9_])/';
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->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é).";
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
<th>Date</th>
|
||||
<th>Client</th>
|
||||
<th>Type</th>
|
||||
<th>Facture</th>
|
||||
<th>Libellé / Détail</th>
|
||||
<th class="amount">Montant HT</th>
|
||||
<th class="amount">Montant TTC</th>
|
||||
@@ -111,13 +112,13 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ref="topSentinel" class="figli-sentinel-row">
|
||||
<td :colspan="6 + allComptes.length + 3">
|
||||
<td :colspan="7 + allComptes.length + 3">
|
||||
<span v-if="loadingOlder">Chargement des mois précédents…</span>
|
||||
</td>
|
||||
</tr>
|
||||
<template v-for="item in groupedRows" :key="item.key">
|
||||
<tr v-if="item.isGroup" class="figli-group-row" :data-year="item.year">
|
||||
<td :colspan="6 + allComptes.length + 3">{{ item.label }} <span class="figli-note">({{ item.count }} lignes)</span></td>
|
||||
<td :colspan="7 + allComptes.length + 3">{{ item.label }} <span class="figli-note">({{ item.count }} lignes)</span></td>
|
||||
</tr>
|
||||
<tr v-else :class="{'figli-error-row': item.hasError}" :data-year="item.date ? item.date.slice(0, 4) : null">
|
||||
<td class="actions-col">
|
||||
@@ -162,6 +163,7 @@
|
||||
@click="startEditType(item)"
|
||||
>{{ typeLabel(item.type) }}</span>
|
||||
</td>
|
||||
<td>{{ item.facture }}</td>
|
||||
<td class="figli-libelle">
|
||||
{{ item.libelle }}
|
||||
<span
|
||||
@@ -191,7 +193,7 @@
|
||||
</tr>
|
||||
</template>
|
||||
<tr ref="bottomSentinel" class="figli-sentinel-row">
|
||||
<td :colspan="6 + allComptes.length + 3">
|
||||
<td :colspan="7 + allComptes.length + 3">
|
||||
<span v-if="loadingNewer">Chargement des mois suivants…</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -200,7 +202,7 @@
|
||||
<tr class="figli-totals-row">
|
||||
<td class="actions-col"></td>
|
||||
<td class="actions-col"></td>
|
||||
<td colspan="4">
|
||||
<td colspan="5">
|
||||
Solde {{ currentYear || '…' }} (créditeur / débiteur)
|
||||
<span v-if="currentYearLoading" class="figli-note">chargement…</span>
|
||||
</td>
|
||||
@@ -227,6 +229,7 @@
|
||||
<th>Date</th>
|
||||
<th>Client</th>
|
||||
<th>Type</th>
|
||||
<th>Facture</th>
|
||||
<th>Libellé / Détail</th>
|
||||
<th class="amount">Montant HT</th>
|
||||
<th class="amount">Montant TTC</th>
|
||||
@@ -246,6 +249,7 @@
|
||||
<td>{{ formatDate(item.date) }}</td>
|
||||
<td>{{ item.client || '—' }}</td>
|
||||
<td><span class="figli-badge" :class="'type-' + item.type">{{ typeLabel(item.type) }}</span></td>
|
||||
<td>{{ item.facture }}</td>
|
||||
<td class="figli-libelle">
|
||||
{{ item.libelle }}
|
||||
<span
|
||||
@@ -268,7 +272,7 @@
|
||||
<tfoot>
|
||||
<tr class="figli-totals-row">
|
||||
<td class="actions-col"></td>
|
||||
<td colspan="4">Solde entrées et sorties liées (créditeur / débiteur)</td>
|
||||
<td colspan="5">Solde entrées et sorties liées (créditeur / débiteur)</td>
|
||||
<td class="amount">{{ drilldownTotals ? formatEur(drilldownTotals.montant_ht) : '' }}</td>
|
||||
<td class="amount">{{ drilldownTotals ? formatEur(drilldownTotals.montant_ttc) : '' }}</td>
|
||||
<td v-for="c in modalComptes" :key="c" class="amount compte-col" :class="drilldownTotals ? soldeClass(drilldownTotals.par_compte[c]) : ''">{{ drilldownTotals && drilldownTotals.par_compte[c] !== undefined ? formatEur(drilldownTotals.par_compte[c]) : '' }}</td>
|
||||
|
||||
Reference in New Issue
Block a user