Sync Indy integre dans l'interface web + anti-doublon
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user