Entreprise
diff --git a/compta/urls.py b/compta/urls.py
index cc0d9ff..152c844 100644
--- a/compta/urls.py
+++ b/compta/urls.py
@@ -2,6 +2,7 @@ from django.urls import path
from . import views
from . import views_saisie
from . import views_import
+from . import views_bookmarklet
urlpatterns = [
path("dashboard/", views.dashboard, name="dashboard"),
@@ -10,6 +11,7 @@ urlpatterns = [
path("declaration/tva/", views.declaration_tva, name="declaration_tva"),
path("saisie/", views_saisie.saisie_paiement, name="saisie_paiement"),
path("import/", views_import.import_csv, name="import_csv"),
+ path("import/bookmarklet/", views_bookmarklet.bookmarklet, name="bookmarklet"),
path("export/ventes/", views.export_ventes, name="export_ventes"),
path("export/tva/", views.export_tva, name="export_tva"),
path("export/urssaf/", views.export_urssaf, name="export_urssaf"),
diff --git a/indy-mcp/bookmarklet.html b/indy-mcp/bookmarklet.html
index f2ebd13..e3f724c 100644
--- a/indy-mcp/bookmarklet.html
+++ b/indy-mcp/bookmarklet.html
@@ -20,35 +20,22 @@
Favori « Indy → CSV »
-
Un clic sur la page Indy → tes transactions sont copiées au format CSV,
- prêtes à coller dans JB Compta. Pas de token à chercher, la 2FA est déjà passée,
- et tout se fait dans ton navigateur (donc pas de souci d'accès serveur).
+
Identique à la page servie par l'app (/import/bookmarklet/). Un clic sur Indy →
+ CSV copié, prêt à coller dans JB Compta. Pas de token, 2FA déjà passée, tout dans le navigateur.
-
Installation (30 s)
+
Installation
- Affiche ta barre de favoris (Ctrl+Maj+B dans Chrome).
- Glisse-dépose ce bouton dans la barre de favoris :
- Indy → CSV
- Va sur app.indy.fr (connecté), puis clique le favori .
- Saisis la période → le CSV est copié → colle-le dans /import/ de JB Compta.
+ Affiche la barre de favoris (Ctrl+Maj+B).
+ Glisse ce bouton dans la barre : Indy → CSV
+ Sur app.indy.fr (connecté), clique le favori, choisis la période (JJ/MM/AAAA).
+ Colle le CSV dans /import/.
-
Si le glisser-déposer ne marche pas : crée un favori manuellement,
- puis colle le code ci-dessous comme URL du favori.
-
-
Code (installation manuelle)
+
Si le glisser-déposer échoue, crée un favori manuel et colle ce code comme URL :
-
Notes
-
- À utiliser uniquement quand l'onglet actif est app.indy.fr.
- Aucun secret n'est stocké : le favori lit ta session en cours.
- Si une alerte « Erreur Indy » apparaît, reconnecte-toi à Indy et réessaie.
- Le mapping des catégories (compte comptable → catégorie) est le même que dans l'app.
-
-
diff --git a/indy-mcp/bookmarklet.js b/indy-mcp/bookmarklet.js
index b229020..f88efb9 100644
--- a/indy-mcp/bookmarklet.js
+++ b/indy-mcp/bookmarklet.js
@@ -1,13 +1,15 @@
/*
* Bookmarklet « Indy → CSV » — version lisible (documentation).
- * Le bouton prêt à installer est dans bookmarklet.html.
+ * Le bouton prêt à installer est dans bookmarklet.html, et la même page est servie
+ * par l'application à /import/bookmarklet/.
*
- * Usage : être connecté sur app.indy.fr, cliquer le favori. Il demande une
- * période, récupère les transactions (dans TON navigateur = autorisé, 2FA déjà
- * passée, pas de problème Cloudflare ni d'accès serveur), construit le CSV au
- * format JB Compta et le copie dans le presse-papier. Tu le colles dans /import/.
+ * Usage : être connecté sur app.indy.fr, cliquer le favori. Une petite fenêtre
+ * s'ouvre avec deux SÉLECTEURS DE DATE (format JJ/MM/AAAA). Il récupère ensuite les
+ * transactions (dans TON navigateur = autorisé, 2FA déjà passée, pas de souci serveur
+ * ni Cloudflare), construit le CSV au format JB Compta et le copie dans le presse-papier.
*/
(async () => {
+ // --- mapping compte PCG -> catégorie (identique à compta/indy.py) ---
const CC = [
["701", "Vente de produits finis"], ["707", "Vente de produits finis"],
["706", "Prestation de services"],
@@ -30,12 +32,38 @@
if (!ss.length) return null;
let m = ss[0]; for (const s of ss) if (Math.abs(s.amount_in_cents || 0) > Math.abs(m.amount_in_cents || 0)) m = s;
const num = String((m.accounting_account || {}).number || "");
- let rg = "", tx2 = 0;
- if (m.tva_intracom && m.tva_intracom.type === "eu") { rg = "intracom"; tx2 = (m.tva_intracom.rate || 0) / 100; }
- else { tx2 = (m.tva_rate || 0) / 100; }
+ let rg = "", t2 = 0;
+ if (m.tva_intracom && m.tva_intracom.type === "eu") { rg = "intracom"; t2 = (m.tva_intracom.rate || 0) / 100; }
+ else { t2 = (m.tva_rate || 0) / 100; }
const lib = (tx.description || tx.raw_description || "").replace(/;/g, " ");
- return [tx.date || "", lib, cat(num), act(num), String(tx2), rg, mt].join(";");
+ return [tx.date || "", lib, cat(num), act(num), String(t2), rg, mt].join(";");
};
+
+ // --- fenêtre avec deux sélecteurs de date (input type=date -> JJ/MM/AAAA) ---
+ const mk = (t, css) => { const e = document.createElement(t); if (css) e.style.cssText = css; return e; };
+ const askDates = () => new Promise(res => {
+ const t = new Date(), y = t.getFullYear(),
+ mo = String(t.getMonth() + 1).padStart(2, "0"),
+ ld = String(new Date(y, t.getMonth() + 1, 0).getDate()).padStart(2, "0");
+ const ov = mk("div", "position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:2147483647;display:flex;align-items:center;justify-content:center");
+ const bx = mk("div", "background:#fff;color:#111;padding:20px;border-radius:12px;font-family:sans-serif;min-width:280px");
+ const h = mk("div", "font-weight:700;margin-bottom:12px"); h.textContent = "Indy vers CSV : choisis la periode";
+ const l1 = mk("div", "font-size:12px;color:#555"); l1.textContent = "Du";
+ const i1 = mk("input", "width:100%;margin:4px 0 10px;padding:6px"); i1.type = "date"; i1.value = y + "-" + mo + "-01";
+ const l2 = mk("div", "font-size:12px;color:#555"); l2.textContent = "Au";
+ const i2 = mk("input", "width:100%;margin:4px 0 14px;padding:6px"); i2.type = "date"; i2.value = y + "-" + mo + "-" + ld;
+ const row = mk("div", "text-align:right");
+ const an = mk("button", "margin-right:8px;padding:6px 12px"); an.textContent = "Annuler";
+ const ok = mk("button", "background:#38bdf8;border:none;padding:6px 14px;border-radius:6px;cursor:pointer"); ok.textContent = "Recuperer";
+ row.appendChild(an); row.appendChild(ok);
+ [h, l1, i1, l2, i2, row].forEach(x => bx.appendChild(x));
+ ov.appendChild(bx); document.body.appendChild(ov);
+ ok.onclick = () => { const a = i1.value, b = i2.value; document.body.removeChild(ov); res([a, b]); };
+ an.onclick = () => { document.body.removeChild(ov); res([null, null]); };
+ });
+ const [d1, d2] = await askDates(); if (!d1 || !d2) return;
+
+ // --- token Firebase (depuis IndexedDB) ---
const getTok = () => new Promise(r => {
const o = indexedDB.open("firebaseLocalStorageDb");
o.onsuccess = () => { try {
@@ -45,11 +73,11 @@
} catch (e) { r(null); } };
o.onerror = () => r(null);
});
- const d1 = prompt("Date début (AAAA-MM-JJ)"); if (!d1) return;
- const d2 = prompt("Date fin (AAAA-MM-JJ)"); if (!d2) return;
const tk = await getTok();
const H = { "Accept": "application/json", "x-client-app-type": "web" };
if (tk) H["Authorization"] = "Bearer " + tk;
+
+ // --- fetch paginé ---
let all = [], pg = 1, tot = 1e9;
try {
while (all.length < tot) {
@@ -61,9 +89,11 @@
const t = j.transactions || []; if (!t.length) break;
all = all.concat(t); pg++; if (pg > 60) break;
}
- } catch (e) { alert("Erreur Indy : " + e.message + "\nEs-tu bien connecté sur app.indy.fr ?"); return; }
+ } catch (e) { alert("Erreur Indy : " + e.message); return; }
+
const rows = all.map(norm).filter(Boolean);
- const csv = "date;libelle;categorie;activite;taux_tva;regime;montant\n" + rows.join("\n");
- try { await navigator.clipboard.writeText(csv); alert(rows.length + " opérations -> CSV copié ! Colle-le dans JB Compta (/import/)."); }
+ const NL = String.fromCharCode(10);
+ const csv = "date;libelle;categorie;activite;taux_tva;regime;montant" + NL + rows.join(NL);
+ try { await navigator.clipboard.writeText(csv); alert(rows.length + " operations -> CSV copie ! Colle-le dans JB Compta (/import/)."); }
catch (e) { window.prompt("Copie ce CSV (Ctrl+C) :", csv); }
})();