Sync Indy integre dans l'interface web + anti-doublon

This commit is contained in:
2026-06-30 14:08:35 +02:00
parent 458f80f201
commit 75af558b49
13 changed files with 665 additions and 48 deletions
+35 -14
View File
@@ -10,16 +10,18 @@
* { box-sizing:border-box; }
body { margin:0; font-family:system-ui,Segoe UI,Roboto,sans-serif;
background:var(--bg); color:var(--txt); padding:24px; }
h1 { margin:0 0 4px; font-size:22px; } h2{font-size:15px;color:var(--muted);}
h1 { margin:0 0 4px; font-size:22px; } h2{font-size:15px;color:var(--muted);margin:0 0 8px;}
a { color:var(--accent); }
.sub { color:var(--muted); margin-bottom:14px; }
.card { background:var(--card); border:1px solid #334155; border-radius:12px; padding:16px; margin-bottom:16px; }
.card.indy { border-color:#0e7490; }
label { display:block; font-size:12px; color:var(--muted); margin:8px 0 4px; }
input, select, textarea { background:#0f172a; color:var(--txt); border:1px solid #334155;
border-radius:8px; padding:8px 10px; font-size:14px; width:100%; }
textarea { min-height:110px; font-family:ui-monospace,monospace; }
textarea { min-height:90px; font-family:ui-monospace,monospace; }
.grid3 { display:grid; grid-template-columns:2fr 1fr 1fr; gap:12px; }
@media(max-width:700px){ .grid3{grid-template-columns:1fr;} }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
@media(max-width:700px){ .grid3,.grid2{grid-template-columns:1fr;} }
button { cursor:pointer; border-radius:8px; border:1px solid #334155; background:var(--card);
color:var(--txt); padding:9px 16px; font-size:14px; }
button.primary { background:var(--accent); color:#04263a; border:none; font-weight:600; }
@@ -53,8 +55,11 @@
{% endif %}
{% if not lignes %}
<form method="post" enctype="multipart/form-data" class="card" id="srcForm">
<!-- ===== 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.</div>
<div class="grid3">
<div>
<label>Entreprise</label>
@@ -62,12 +67,30 @@
{% 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="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>Année (pour l'OCR)</label>
<input type="number" name="annee" value="{{ annee_defaut }}">
<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 (OCR) — fichier, appareil photo, ou <b>coller</b> (Ctrl/Cmd+V)</label>
<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>
@@ -79,7 +102,6 @@
<div class="actions">
<button type="submit" name="action" value="preview" class="primary">Analyser / Aperçu</button>
</div>
<div class="sub" style="margin-top:8px">L'OCR pré-remplit les lignes ; tu corriges la catégorie et le taux dans l'aperçu avant d'enregistrer.</div>
</form>
{% endif %}
@@ -87,7 +109,7 @@
<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 == 'ocr' %}(OCR){% endif %}</h2>
<h2>Aperçu éditable — {{ lignes|length }} ligne(s){% if source == 'indy' %} (Indy){% elif source == 'ocr' %} (OCR){% endif %}</h2>
<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>
@@ -109,6 +131,8 @@
<td><input type="number" step="0.01" name="l_taux" value="{{ l.taux }}" class="num" style="width:70px"></td>
<td><input type="number" step="0.01" name="l_montant" value="{{ l.montant }}" 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 }}">
</tr>
{% endfor %}
</tbody>
@@ -127,13 +151,10 @@
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 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;
e.preventDefault(); break;
}
}
});