Import de relevé bancaire CSV en libre-service (/lignes/importer-releve)
Chaque transaction du relevé devient une ligne « à trier » : type autre, répartition vide (liseré rouge existant via field_ecart), tag field_flag « IMP AAMMJJ » (liseré ambre + filtre existants), client rapproché par mots (ClientMatcher : séquence contiguë, sinon mot significatif unique -- jamais en sous-chaîne, jamais auto-créé). - src/Import/ : CsvReleveParser (ISO-8859-1 confirmé, en-tête strict, fgetcsv avec escape '' explicite -- dépréciation PHP 8.4, rejets propres avec n° de ligne), ReleveImportBatch (Batch API par lots de 25, dédoublonnage COMPTÉ par empreinte field_import_fitid -- max(0, k−m) importe les vrais doublons légitimes et dédoublonne à travers des fichiers qui se chevauchent --, totaux de contrôle au centime sur la page de résultat, résumé en tempstore privé). - ReleveUploadForm : upload private://releves (fichier conservé + usage, hors de portée du cron), parse en validateForm(), batch, redirection vers la page de résultat. - field_montant_releve : référence bancaire immuable, écrite une fois à l'import et jamais par presave ; affichée en TEXTE sous Montant TTC (widget remplacé par un #type item -- un item ne soumet rien et extractFormValues() saute le champ sans valeur soumise, la valeur survit donc à chaque save) ; masquée sur les lignes sans montant. - SkipValidationContext : contournement du contrôle de répartition requête-scopé (ferme le trou de concurrence de l'ancien state global, AUDIT-2026-09-09 §2.2) -- presave honore le service (state gardé pour compat), updateType/updateField basculent dessus. - Permissions (AUDIT §2.2 priorité 1) : access figli ledger sur toutes les routes du module + autocomplete (RouteSubscriber), import réservé Éditeur/Admin, access content retiré du rôle Authenticated (config/sync re-exportée pour les 4 rôles). - Gin : hook_gin_ignore_sticky_form_actions() -- sans ça, le bouton Importer partait dans la barre sticky du chrome masqué. - install : 8011 champs, 8012 index sur les empreintes, 8013/8014 montant_releve sur le formulaire sous le TTC (poids renumérotés). - /lignes : boutons + Ajouter / Importer / Historique dans le footer sticky (compacts), footer colspan dès la première colonne, badges de signalement qui reviennent à la ligne au lieu de déborder.
This commit is contained in:
@@ -14,12 +14,6 @@
|
||||
{% verbatim %}
|
||||
<div id="figli-home-app">
|
||||
<div class="figli-toolbar">
|
||||
<a href="/node/add/ligne_comptable" class="button button--primary" @click.prevent="openAddForm">+ Ajouter une ligne</a>
|
||||
{% endverbatim %}
|
||||
{% if can_view_history %}
|
||||
<a href="{{ path('figli_compta_ledger.history') }}" class="button">Historique</a>
|
||||
{% endif %}
|
||||
{% verbatim %}
|
||||
|
||||
<label>Compte
|
||||
<span class="figli-filter-row">
|
||||
@@ -265,8 +259,16 @@
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="figli-totals-row">
|
||||
<td class="actions-col"></td>
|
||||
<td colspan="6">
|
||||
<td colspan="7">
|
||||
<a href="/node/add/ligne_comptable" class="button button--primary" @click.prevent="openAddForm">+ Ajouter une ligne</a>
|
||||
{% endverbatim %}
|
||||
{% if can_import_releve %}
|
||||
<a href="{{ path('figli_compta_ledger.releve_import_form') }}" class="button">Importer un relevé</a>
|
||||
{% endif %}
|
||||
{% if can_view_history %}
|
||||
<a href="{{ path('figli_compta_ledger.history') }}" class="button">Historique</a>
|
||||
{% endif %}
|
||||
{% verbatim %}
|
||||
Solde {{ currentYear || '…' }} (créditeur / débiteur)
|
||||
<span v-if="currentYearLoading" class="figli-note">chargement…</span>
|
||||
</td>
|
||||
@@ -337,8 +339,7 @@
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="figli-totals-row">
|
||||
<td class="actions-col"></td>
|
||||
<td colspan="5">Solde entrées et sorties liées (créditeur / débiteur)</td>
|
||||
<td colspan="6">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>
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
{#
|
||||
Résultat d'import d'un relevé bancaire (ReleveImportResultController).
|
||||
Page simple sans Vue : Twig rend tout, les chiffres viennent du tempstore
|
||||
privé (une seule lecture, purgée aussitôt). Mêmes classes de navigation
|
||||
que les autres pages du module (figli-page-nav, admin-chrome.css).
|
||||
#}
|
||||
<nav class="figli-page-nav">
|
||||
<a href="{{ path('figli_compta_ledger.home') }}">Grand livre</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard') }}">SAS</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_repartition') }}">Répartition/Soldes</a>
|
||||
<a href="{{ path('figli_compta_ledger.dashboard_compte') }}">Par compte</a>
|
||||
</nav>
|
||||
<div class="figli-releve-result">
|
||||
<h2>Import terminé — {{ summary.file_name }}</h2>
|
||||
<p class="figli-releve-tag">Chaque ligne créée porte le signalement
|
||||
<span class="figli-flag-badge">{{ summary.tag }}</span> et apparaît sur
|
||||
<a href="{{ lignes_url }}">/lignes</a> avec un liseré rouge (répartition à faire) tant qu'elle n'a pas été triée.</p>
|
||||
|
||||
<div class="figli-releve-stats">
|
||||
<div class="figli-releve-stat">
|
||||
<span class="figli-releve-stat-value">{{ summary.created }}</span>
|
||||
<span class="figli-releve-stat-label">lignes créées</span>
|
||||
</div>
|
||||
<div class="figli-releve-stat">
|
||||
<span class="figli-releve-stat-value">{{ summary.duplicates }}</span>
|
||||
<span class="figli-releve-stat-label">doublons déjà en base (ignorés)</span>
|
||||
</div>
|
||||
<div class="figli-releve-stat">
|
||||
<span class="figli-releve-stat-value">{{ summary.matched }}</span>
|
||||
<span class="figli-releve-stat-label">clients rapprochés</span>
|
||||
</div>
|
||||
<div class="figli-releve-stat">
|
||||
<span class="figli-releve-stat-value">{{ summary.unmatched }}</span>
|
||||
<span class="figli-releve-stat-label">sans client (à remplir)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Totaux de contrôle</h3>
|
||||
<table class="figli-releve-totals">
|
||||
<tbody>
|
||||
<tr><th scope="row">Somme des mouvements du fichier</th><td class="figli-releve-amount">{{ summary.file_total }}</td></tr>
|
||||
<tr><th scope="row">Somme des lignes créées</th><td class="figli-releve-amount">{{ summary.created_total }}</td></tr>
|
||||
<tr><th scope="row">Somme des doublons ignorés</th><td class="figli-releve-amount">{{ summary.duplicates_total }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% if summary.totals_ok %}
|
||||
<p class="figli-releve-ok">Équilibre vérifié au centime : aucun mouvement perdu ni dupliqué.</p>
|
||||
{% else %}
|
||||
<p class="figli-releve-alert">Anomalie de parsing : la somme des mouvements du fichier ne correspond pas à « créées + doublons ». Vérifiez le fichier source et les lignes importées avant de trier le lot.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if summary.errors_count %}
|
||||
<h3>Erreurs ({{ summary.errors_count }})</h3>
|
||||
<ul class="figli-releve-errors">
|
||||
{% for e in summary.errors %}<li>{{ e }}</li>{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if summary.duplicates_list %}
|
||||
<h3>Doublons ignorés ({{ summary.duplicates_list|length }})</h3>
|
||||
<p>Ces transactions figuraient déjà en base (empreinte identique, toutes provenances confondues). Vérifiez la liste si vous attendiez un mouvement.</p>
|
||||
<div class="figli-releve-dup-wrap">
|
||||
<table class="figli-releve-dups">
|
||||
<thead>
|
||||
<tr><th scope="col">Date</th><th scope="col">Montant</th><th scope="col">Libellé</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for d in summary.duplicates_list %}
|
||||
<tr><td>{{ d.date }}</td><td class="figli-releve-amount">{{ d.montant }}</td><td>{{ d.libelle }}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="figli-releve-actions">
|
||||
<a class="button button--primary" href="{{ lignes_url }}">Voir les lignes importées sur /lignes</a>
|
||||
<a class="button" href="{{ import_url }}">Importer un autre relevé</a>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user