Gestion des depenses ventilees (multi-taux TVA) a l'import Indy
Deploy via Portainer / deploy (push) Successful in 0s
Deploy via Portainer / deploy (push) Successful in 0s
This commit is contained in:
+64
-12
@@ -78,16 +78,18 @@ def _match_cat(text, cats):
|
||||
|
||||
|
||||
def _row(dt, libelle, montant, taux=Decimal("0"), regime=RegimeTva.NATIONALE,
|
||||
categorie=None, activite="", indy_id="", n=0):
|
||||
categorie=None, activite="", indy_id="", n=0, groupe="", n_parts=1):
|
||||
neutre = bool(categorie and categorie.exclure_calculs)
|
||||
if montant > 0:
|
||||
typ = "Mouvement neutre" if neutre else "Vente"
|
||||
else:
|
||||
typ = "Dépense neutre" if neutre else "Dépense"
|
||||
if n_parts > 1:
|
||||
typ += " (ventilée)"
|
||||
return {"n": n, "date": dt.isoformat() if dt else "", "libelle": libelle,
|
||||
"categorie_id": categorie.pk if categorie else "",
|
||||
"taux": taux, "regime": regime, "montant": montant, "type": typ,
|
||||
"activite": activite, "indy_id": indy_id}
|
||||
"activite": activite, "indy_id": indy_id, "groupe": groupe}
|
||||
|
||||
|
||||
# ---- CSV ----------------------------------------------------------------
|
||||
@@ -248,7 +250,7 @@ def _parse_indy(entreprise, token, date_debut, date_fin, cats_par_nom):
|
||||
cat = cats_par_nom.get((ln["categorie_nom"] or "").strip().lower())
|
||||
lignes.append(_row(dt, ln["libelle"], ln["montant"], ln["taux"],
|
||||
_regime(ln["regime"]), cat, ln["activite"] or "",
|
||||
ln["indy_id"], n))
|
||||
ln["indy_id"], n, ln.get("groupe", ""), ln.get("n_parts", 1)))
|
||||
return lignes, []
|
||||
|
||||
|
||||
@@ -262,6 +264,17 @@ def _deja_importe(entreprise, indy_id):
|
||||
or Paiement.objects.filter(entreprise=entreprise, indy_id=indy_id).exists())
|
||||
|
||||
|
||||
def _deja_importe_groupe(entreprise, groupe):
|
||||
"""Vrai si la transaction Indy (ou une de ses parts ventilées) est déjà là."""
|
||||
if not groupe:
|
||||
return False
|
||||
from django.db.models import Q
|
||||
q = Q(indy_id=groupe) | Q(indy_id__startswith=f"{groupe}#")
|
||||
return (Facture.objects.filter(entreprise=entreprise).filter(q).exists()
|
||||
or Depense.objects.filter(entreprise=entreprise).filter(q).exists()
|
||||
or Paiement.objects.filter(entreprise=entreprise).filter(q).exists())
|
||||
|
||||
|
||||
# ---- Création -----------------------------------------------------------
|
||||
|
||||
def _creer(entreprise, dt, libelle, montant, taux, regime, categorie,
|
||||
@@ -295,6 +308,29 @@ def _creer(entreprise, dt, libelle, montant, taux, regime, categorie,
|
||||
return "depense"
|
||||
|
||||
|
||||
def _creer_groupe(entreprise, rows):
|
||||
"""rows = lignes d'une même transaction. Si ≥2 dépenses -> une opération
|
||||
bancaire (le total) qui porte les dépenses ventilées. Sinon création simple."""
|
||||
depenses = [r for r in rows if r["montant"] < 0]
|
||||
if len(rows) >= 2 and len(depenses) == len(rows):
|
||||
total = sum(-r["montant"] for r in rows)
|
||||
p = Paiement.objects.create(
|
||||
entreprise=entreprise, date=rows[0]["dt"], libelle=rows[0]["libelle"],
|
||||
montant_total=total, sens="debit", indy_id=rows[0].get("groupe", ""))
|
||||
for r in rows:
|
||||
Depense.objects.create(
|
||||
entreprise=entreprise, paiement=p, date=r["dt"], libelle=r["libelle"],
|
||||
categorie=r["categorie"], activite=r["activite"], regime_tva=r["regime"],
|
||||
taux_tva=r["taux"], montant_ttc=-r["montant"], indy_id=r["indy_id"])
|
||||
return {"dépense (ventilée)": len(rows)}
|
||||
compteur = {}
|
||||
for r in rows:
|
||||
k = _creer(entreprise, r["dt"], r["libelle"], r["montant"], r["taux"],
|
||||
r["regime"], r["categorie"], r["activite"], r["indy_id"])
|
||||
compteur[k] = compteur.get(k, 0) + 1
|
||||
return compteur
|
||||
|
||||
|
||||
@login_required
|
||||
def import_csv(request):
|
||||
entreprises = Entreprise.accessibles(request.user)
|
||||
@@ -323,24 +359,40 @@ def import_csv(request):
|
||||
montants = request.POST.getlist("l_montant")
|
||||
activites = request.POST.getlist("l_activite")
|
||||
indy_ids = request.POST.getlist("l_indy_id")
|
||||
groupes = request.POST.getlist("l_groupe")
|
||||
cats_by_id = {str(c.pk): c for c in Categorie.objects.all()}
|
||||
compteur = {}
|
||||
ignores = 0
|
||||
# 1) Construire les lignes valides, en préservant l'ordre des groupes.
|
||||
groupes_ordre = []
|
||||
par_groupe = {}
|
||||
for i in range(len(libelles)):
|
||||
dt = _date(dates[i]) if i < len(dates) else None
|
||||
montant = _dec(montants[i] if i < len(montants) else "0")
|
||||
iid = indy_ids[i] if i < len(indy_ids) else ""
|
||||
if dt is None or montant == 0:
|
||||
continue
|
||||
if iid and _deja_importe(entreprise, iid):
|
||||
ignores += 1
|
||||
iid = indy_ids[i] if i < len(indy_ids) else ""
|
||||
grp = (groupes[i] if i < len(groupes) else "") or ""
|
||||
cle = grp or f"_solo_{i}" # lignes sans groupe = isolées
|
||||
if cle not in par_groupe:
|
||||
par_groupe[cle] = []
|
||||
groupes_ordre.append((cle, grp))
|
||||
par_groupe[cle].append({
|
||||
"dt": dt, "montant": montant,
|
||||
"libelle": (libelles[i] or "").strip() or "(sans libellé)",
|
||||
"taux": _dec(taux[i] if i < len(taux) else "0"),
|
||||
"regime": _regime(regimes[i] if i < len(regimes) else ""),
|
||||
"categorie": cats_by_id.get(cat_ids[i] if i < len(cat_ids) else ""),
|
||||
"activite": _activite(activites[i] if i < len(activites) else ""),
|
||||
"indy_id": iid, "groupe": grp})
|
||||
# 2) Créer groupe par groupe (anti-doublon par transaction).
|
||||
for cle, grp in groupes_ordre:
|
||||
rows = par_groupe[cle]
|
||||
if grp and _deja_importe_groupe(entreprise, grp):
|
||||
ignores += len(rows)
|
||||
continue
|
||||
k = _creer(entreprise, dt, (libelles[i] or "").strip() or "(sans libellé)",
|
||||
montant, _dec(taux[i] if i < len(taux) else "0"),
|
||||
_regime(regimes[i] if i < len(regimes) else ""),
|
||||
cats_by_id.get(cat_ids[i] if i < len(cat_ids) else ""),
|
||||
_activite(activites[i] if i < len(activites) else ""), iid)
|
||||
compteur[k] = compteur.get(k, 0) + 1
|
||||
for k, v in _creer_groupe(entreprise, rows).items():
|
||||
compteur[k] = compteur.get(k, 0) + v
|
||||
if ignores:
|
||||
compteur["déjà importées (ignorées)"] = ignores
|
||||
ctx["resultat"] = compteur
|
||||
|
||||
Reference in New Issue
Block a user