Files
jb-compta/compta/templates/compta/import_csv.html
T
admin 8524f11ec8
Deploy via Portainer / deploy (push) Successful in 0s
modify csv import
2026-07-15 10:37:39 +02:00

159 lines
7.2 KiB
HTML

{% extends "compta/base.html" %}
{% load l10n %}
{% block titre %}Importer{% endblock %}
{% block extrastyle %}
<style>
.ic label { display:block; font-size:12px; color:var(--muted); margin:10px 0 4px; }
.ic input, .ic select, .ic textarea { width:100%; background:var(--panel); }
.ic textarea { min-height:90px; font-family:ui-monospace,monospace; }
.ic .card { margin-bottom:16px; }
.ic .card.indy { border-color:#0e7490; }
.grid3 { display:grid; grid-template-columns:2fr 1fr 1fr; gap:12px; }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
@media(max-width:700px){ .grid3,.grid2{grid-template-columns:1fr;} }
.ic .actions { display:flex; gap:12px; margin-top:14px; flex-wrap:wrap; }
.ic table { font-size:13px; }
.ic th, .ic td { padding:5px 7px; text-align:left; }
.ic td input, .ic td select { padding:6px 8px; }
.ic .num { text-align:right; }
code { background:var(--panel); padding:1px 6px; border-radius:5px; }
</style>
{% endblock %}
{% block topbar %}
<h1>Importer un relevé</h1>
<div class="sub">Synchronisation Indy, CSV, ou capture d'écran (OCR) — avec aperçu éditable et anti-doublon.</div>
{% endblock %}
{% block content %}
<div class="ic">
{% if erreurs %}{% for e in erreurs %}<div class="msg bad">{{ e }}</div>{% endfor %}{% endif %}
{% if resultat %}
<div class="msg">Import terminé :
{% for k, v in resultat.items %}{{ v }} {{ k }}{% if not forloop.last %} · {% endif %}{% endfor %}.
Voir <a href="/gestion/">Entrées / sorties</a>.</div>
{% endif %}
{% if ocr_debug %}
<details class="card">
<summary style="cursor:pointer;color:var(--muted)">Texte lu par l'OCR (debug — {{ ocr_debug|length }} ligne(s))</summary>
<pre style="white-space:pre-wrap;font-size:12px;color:var(--muted);margin-top:8px">{% for l in ocr_debug %}{{ l }}
{% endfor %}</pre>
</details>
{% endif %}
{% if not lignes %}
<!-- ===== Synchroniser depuis Indy ===== -->
<form method="post" class="card indy">
{% csrf_token %}
<h2>① Synchroniser depuis Indy (recommandé)</h2>
<div class="sub">Récupère tes transactions directement depuis l'API Indy. Anti-doublon automatique.
· <a href="/import/bookmarklet/">Installer le favori « Indy → CSV »</a> (le plus simple, depuis ton navigateur)</div>
<div class="grid3">
<div>
<label>Entreprise</label>
<select name="entreprise" required>
{% for e in entreprises %}<option value="{{ e.pk }}" {% if e.pk == entreprise_id %}selected{% endif %}>{{ e.nom }}</option>{% endfor %}
</select>
</div>
<div><label>Du</label><input type="date" name="date_debut"></div>
<div><label>Au</label><input type="date" name="date_fin"></div>
</div>
<label>Token Indy (Bearer, sans « Bearer ») — mémorisé sur l'entreprise, expire ~1 h</label>
<textarea name="indy_token" placeholder="eyJ..."></textarea>
<div class="actions">
<button type="submit" name="action" value="indy" class="btn-primary">Récupérer depuis Indy</button>
</div>
</form>
<!-- ===== CSV / capture ===== -->
<form method="post" enctype="multipart/form-data" class="card" id="srcForm">
{% csrf_token %}
<h2>② Ou : CSV / capture d'écran (OCR)</h2>
<div class="grid3">
<div>
<label>Entreprise</label>
<select name="entreprise" required>
{% for e in entreprises %}<option value="{{ e.pk }}" {% if e.pk == entreprise_id %}selected{% endif %}>{{ e.nom }}</option>{% endfor %}
</select>
</div>
<div><label>Année (OCR)</label><input type="number" name="annee" value="{{ annee_defaut }}"></div>
<div>
<label>Photo / capture — fichier, photo, ou <b>coller</b> (Ctrl/Cmd+V)</label>
<input type="file" name="image" id="imgInput" accept="image/*" capture="environment">
<div id="pasteInfo" class="sub" style="margin-top:4px"></div>
</div>
</div>
<label>…ou fichier CSV</label>
<input type="file" name="fichier" accept=".csv,.txt">
<label>…ou colle des lignes CSV <span class="muted">(date ; libelle ; categorie ; activite ; taux_tva ; regime ; montant ; groupe*) — *groupe optionnel : mêmes valeurs = dépense ventilée</span></label>
<textarea name="texte" placeholder="2026-06-25;ALTISSIMO;Vente de produits finis;vente;20;;168.40">{{ texte }}</textarea>
<div class="actions">
<button type="submit" name="action" value="preview" class="btn-primary">Analyser / Aperçu</button>
</div>
</form>
{% endif %}
{% if lignes %}
<form method="post" class="card">
{% csrf_token %}
<input type="hidden" name="entreprise" value="{{ entreprise_id }}">
<h2>Aperçu éditable — {{ lignes|length }} ligne(s){% if source == 'indy' %} (Indy){% elif source == 'ocr' %} (OCR){% endif %}</h2>
<div class="table-wrap">
<table>
<thead><tr><th>Date</th><th>Libellé</th><th>Catégorie</th><th>Régime TVA</th><th>Taux %</th><th>Montant €</th><th>Type</th></tr></thead>
<tbody>
{% for l in lignes %}
<tr>
<td><input type="date" name="l_date" value="{{ l.date }}" style="width:140px"></td>
<td><input type="text" name="l_libelle" value="{{ l.libelle }}"></td>
<td>
<select name="l_categorie">
<option value=""></option>
{% for c in categories %}<option value="{{ c.pk }}" {% if c.pk == l.categorie_id %}selected{% endif %}>{{ c.nom }}</option>{% endfor %}
</select>
</td>
<td>
<select name="l_regime">
{% for val,lab in regimes %}<option value="{{ val }}" {% if val == l.regime %}selected{% endif %}>{{ lab }}</option>{% endfor %}
</select>
</td>
<td><input type="number" step="0.01" name="l_taux" value="{{ l.taux|unlocalize }}" class="num" style="width:70px"></td>
<td><input type="number" step="0.01" name="l_montant" value="{{ l.montant|unlocalize }}" class="num" style="width:100px"></td>
<td>{{ l.type }}</td>
<input type="hidden" name="l_activite" value="{{ l.activite }}">
<input type="hidden" name="l_indy_id" value="{{ l.indy_id }}">
<input type="hidden" name="l_groupe" value="{{ l.groupe }}">
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="actions">
<button type="submit" name="action" value="save" class="btn-primary">Enregistrer ces opérations</button>
<a href="/import/"><button type="button">Recommencer</button></a>
</div>
</form>
{% endif %}
</div>
{% endblock %}
{% block scripts %}
<script>
window.addEventListener('paste', function(e){
var input = document.getElementById('imgInput');
if(!input || !e.clipboardData) return;
for(var i=0; i<e.clipboardData.items.length; i++){
var it = e.clipboardData.items[i];
if(it.type && it.type.indexOf('image') === 0){
var dt = new DataTransfer(); dt.items.add(it.getAsFile()); input.files = dt.files;
var info = document.getElementById('pasteInfo');
if(info){ info.textContent = 'Image collée — clique « Analyser ».'; info.style.color = '#34d399'; }
e.preventDefault(); break;
}
}
});
</script>
{% endblock %}