Multi-utilisateur : droits par entreprise + renommage Operation bancaire

This commit is contained in:
2026-06-30 08:33:05 +02:00
parent 80042faf0f
commit 688fa3fc97
9 changed files with 157 additions and 71 deletions
+20 -36
View File
@@ -1,8 +1,9 @@
"""
Saisie unifiée : une opération bancaire signée.
- Montant négatif -> dépense(s) (éclatement possible).
- Montant positif -> facture (vente) avec création de client à la volée,
- Montant positif -> facture (vente) + création de client à la volée,
OU mouvement neutre (ex. remboursement de TVA).
Cloisonnée : un utilisateur ne voit que ses entreprises.
"""
from decimal import Decimal, InvalidOperation
from datetime import date
@@ -37,7 +38,7 @@ def _numero_unique(entreprise, annee):
def _context(entreprises, post=None):
return {
"entreprises": entreprises,
"clients": Client.objects.select_related("entreprise"),
"clients": Client.objects.filter(entreprise__in=entreprises).select_related("entreprise"),
"activites": Activite.choices,
"regimes": RegimeTva.choices,
"categories_depense": Categorie.objects.filter(actif=True).exclude(usage="vente"),
@@ -49,7 +50,7 @@ def _context(entreprises, post=None):
@login_required
def saisie_paiement(request):
entreprises = Entreprise.objects.all()
entreprises = Entreprise.accessibles(request.user)
if request.method != "POST":
return render(request, "compta/saisie_paiement.html", _context(entreprises))
@@ -61,34 +62,28 @@ def saisie_paiement(request):
erreurs = []
if not entreprise:
erreurs.append("Sélectionne une entreprise.")
erreurs.append("Sélectionne une entreprise autorisée.")
if not libelle:
erreurs.append("Renseigne le libellé du paiement.")
erreurs.append("Renseigne le libellé de l'opération.")
if montant_total == 0:
erreurs.append("Le montant ne peut pas être nul.")
if erreurs:
for e in erreurs:
messages.error(request, e)
return render(request, "compta/saisie_paiement.html",
_context(entreprises, request.POST))
return render(request, "compta/saisie_paiement.html", _context(entreprises, request.POST))
cats = {str(c.pk): c for c in Categorie.objects.all()}
# ---------------------------------------------------------------
# CRÉDIT (montant positif) -> facture (vente) ou mouvement neutre
# ---------------------------------------------------------------
# --- CRÉDIT (positif) -> facture (vente) ou mouvement neutre ---
if montant_total > 0:
cat = cats.get(request.POST.get("v_categorie"))
if cat and cat.exclure_calculs:
# Mouvement neutre (ex. remboursement de TVA) : pas de CA.
Paiement.objects.create(
entreprise=entreprise, date=d, libelle=libelle,
montant_total=montant_total, sens="credit", categorie=cat)
messages.success(request, f"Mouvement neutre « {libelle} » enregistré.")
return redirect("saisie_paiement")
# Vente -> facture encaissée
client = None
nouveau = (request.POST.get("v_client_nouveau") or "").strip()
if nouveau:
@@ -98,29 +93,23 @@ def saisie_paiement(request):
entreprise=entreprise, pk=request.POST.get("v_client")).first()
if not client:
messages.error(request, "Choisis un client existant ou saisis-en un nouveau.")
return render(request, "compta/saisie_paiement.html",
_context(entreprises, request.POST))
return render(request, "compta/saisie_paiement.html", _context(entreprises, request.POST))
activite = request.POST.get("v_activite") or entreprise.activite
taux = _dec(request.POST.get("v_taux"), "0")
designation = (request.POST.get("v_designation") or libelle).strip()
ttc = montant_total
ht = (ttc / (Decimal("1") + taux / Decimal("100"))).quantize(Decimal("0.01"))
f = Facture.objects.create(
entreprise=entreprise, client=client,
numero=_numero_unique(entreprise, annee), activite=activite,
date_emission=d, date_encaissement=d, statut="encaissee")
LigneFacture.objects.create(
facture=f, designation=designation, quantite=Decimal("1"),
prix_unitaire_ht=ht, taux_tva=taux)
entreprise=entreprise, client=client, numero=_numero_unique(entreprise, annee),
activite=activite, date_emission=d, date_encaissement=d, statut="encaissee")
LigneFacture.objects.create(facture=f, designation=designation, quantite=Decimal("1"),
prix_unitaire_ht=ht, taux_tva=taux)
f.recompute_totaux()
messages.success(request, f"Vente « {libelle} » enregistrée (facture {f.numero}).")
return redirect("saisie_paiement")
# ---------------------------------------------------------------
# DÉBIT (montant négatif) -> dépense(s), éclatement possible
# ---------------------------------------------------------------
# --- DÉBIT (négatif) -> dépense(s), éclatement possible ---
total_abs = -montant_total
libelles = request.POST.getlist("l_libelle")
categories = request.POST.getlist("l_categorie")
@@ -149,24 +138,19 @@ def saisie_paiement(request):
somme = sum((l["montant"] for l in lignes), Decimal("0"))
if not lignes:
messages.error(request, "Ajoute au moins une ligne de dépense.")
return render(request, "compta/saisie_paiement.html",
_context(entreprises, request.POST))
return render(request, "compta/saisie_paiement.html", _context(entreprises, request.POST))
if abs(somme - total_abs) >= Decimal("0.01"):
messages.error(request,
f"La somme des lignes ({somme} €) ne correspond pas au montant "
f"({total_abs} €). Écart : {somme - total_abs} €.")
return render(request, "compta/saisie_paiement.html",
_context(entreprises, request.POST))
return render(request, "compta/saisie_paiement.html", _context(entreprises, request.POST))
paiement = Paiement.objects.create(
entreprise=entreprise, date=d, libelle=libelle,
montant_total=total_abs, sens="debit")
entreprise=entreprise, date=d, libelle=libelle, montant_total=total_abs, sens="debit")
for l in lignes:
Depense.objects.create(
entreprise=entreprise, paiement=paiement, date=d,
libelle=l["libelle"], categorie=l["categorie"],
activite=l["activite"] or "", regime_tva=l["regime"],
entreprise=entreprise, paiement=paiement, date=d, libelle=l["libelle"],
categorie=l["categorie"], activite=l["activite"] or "", regime_tva=l["regime"],
taux_tva=l["taux"], montant_ttc=l["montant"], montant_tva=l["tva"])
messages.success(request,
f"Paiement « {libelle} » enregistré ({len(lignes)} dépense(s)).")
messages.success(request, f"Opération « {libelle} » enregistrée ({len(lignes)} dépense(s)).")
return redirect("saisie_paiement")