Saisie unifiée + 4 vues dashboard (mensuel, déclarations URSSAF/TVA)
This commit is contained in:
+185
-97
@@ -1,96 +1,110 @@
|
||||
"""
|
||||
Logique fiscale — JB Compta.
|
||||
|
||||
Bases de calcul : CA HORS TAXES (HT) encaissé.
|
||||
- Base des cotisations URSSAF et du versement libératoire (assujetti TVA).
|
||||
- En franchise de TVA, HT = TTC : le calcul reste correct.
|
||||
|
||||
Multi-activité : une entreprise peut exercer plusieurs activités (ex. Vente BIC
|
||||
+ Prestation BIC). Taux URSSAF, abattements, taux VL et seuils diffèrent par
|
||||
activité, donc tout est ventilé par activité puis totalisé.
|
||||
|
||||
TVA :
|
||||
- TVA collectée = TVA sur ventes encaissées + TVA auto-liquidée (achats UE/import).
|
||||
- TVA déductible = TVA facturée sur achats nationaux + TVA auto-liquidée.
|
||||
- L'autoliquidation s'ajoute aux deux côtés (effet net souvent nul), mais doit
|
||||
apparaître sur la CA3 : on l'expose donc séparément.
|
||||
Bases : CA HORS TAXES (HT) encaissé.
|
||||
TVA collectée = TVA ventes + TVA auto-liquidée (UE/import).
|
||||
TVA déductible = TVA achats nationaux + TVA auto-liquidée. Neutres exclus.
|
||||
"""
|
||||
from decimal import Decimal
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import TruncMonth
|
||||
|
||||
from .models import Facture, Depense, Bareme, StatutFacture, Activite
|
||||
from .models import (Facture, Depense, LigneFacture, Bareme, StatutFacture,
|
||||
Activite, RegimeTva)
|
||||
|
||||
CENT = Decimal("0.01")
|
||||
MOIS_FR = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet",
|
||||
"Août", "Septembre", "Octobre", "Novembre", "Décembre"]
|
||||
|
||||
|
||||
def _q(value):
|
||||
return (value or Decimal("0")).quantize(CENT)
|
||||
def _q(v):
|
||||
return (v or Decimal("0")).quantize(CENT)
|
||||
|
||||
|
||||
def factures_encaissees(entreprise, annee):
|
||||
return Facture.objects.filter(
|
||||
entreprise=entreprise,
|
||||
statut=StatutFacture.ENCAISSEE,
|
||||
date_encaissement__year=annee,
|
||||
)
|
||||
# ---- Sélections de période ---------------------------------------------
|
||||
|
||||
def factures_encaissees(entreprise, annee, mois=None):
|
||||
qs = Facture.objects.filter(entreprise=entreprise, statut=StatutFacture.ENCAISSEE,
|
||||
date_encaissement__year=annee)
|
||||
if mois:
|
||||
qs = qs.filter(date_encaissement__month=mois)
|
||||
return qs
|
||||
|
||||
|
||||
def depenses_annee(entreprise, annee):
|
||||
return Depense.objects.filter(entreprise=entreprise, date__year=annee)
|
||||
def depenses_periode(entreprise, annee, mois=None):
|
||||
qs = Depense.objects.filter(entreprise=entreprise, date__year=annee)
|
||||
if mois:
|
||||
qs = qs.filter(date__month=mois)
|
||||
return qs
|
||||
|
||||
|
||||
def ca_ht_par_activite(entreprise, annee):
|
||||
"""Dict {activite: CA HT encaissé}."""
|
||||
rows = (
|
||||
factures_encaissees(entreprise, annee)
|
||||
.values("activite")
|
||||
.annotate(total=Sum("montant_ht"))
|
||||
)
|
||||
# ---- CA -----------------------------------------------------------------
|
||||
|
||||
def ca_ht_par_activite(entreprise, annee, mois=None):
|
||||
rows = factures_encaissees(entreprise, annee, mois).values("activite").annotate(
|
||||
total=Sum("montant_ht"))
|
||||
return {r["activite"]: _q(r["total"]) for r in rows}
|
||||
|
||||
|
||||
def ca_ht(entreprise, annee):
|
||||
agg = factures_encaissees(entreprise, annee).aggregate(s=Sum("montant_ht"))
|
||||
return _q(agg["s"])
|
||||
def ca_ht(entreprise, annee, mois=None):
|
||||
return _q(factures_encaissees(entreprise, annee, mois).aggregate(
|
||||
s=Sum("montant_ht"))["s"])
|
||||
|
||||
|
||||
# ---- TVA ----------------------------------------------------------------
|
||||
|
||||
def tva_sur_ventes(entreprise, annee):
|
||||
agg = factures_encaissees(entreprise, annee).aggregate(s=Sum("montant_tva"))
|
||||
return _q(agg["s"])
|
||||
def tva_sur_ventes(entreprise, annee, mois=None):
|
||||
return _q(factures_encaissees(entreprise, annee, mois).aggregate(
|
||||
s=Sum("montant_tva"))["s"])
|
||||
|
||||
|
||||
def tva_autoliquidee(entreprise, annee):
|
||||
"""TVA auto-liquidée sur les achats UE/import (intracom + import)."""
|
||||
total = sum((d.tva_autoliquidee for d in depenses_annee(entreprise, annee)),
|
||||
def tva_autoliquidee(entreprise, annee, mois=None):
|
||||
total = sum((d.tva_autoliquidee for d in depenses_periode(entreprise, annee, mois)),
|
||||
Decimal("0"))
|
||||
return _q(total)
|
||||
|
||||
|
||||
def tva_deductible(entreprise, annee):
|
||||
"""TVA récupérable : facturée (national) + auto-liquidée (UE/import)."""
|
||||
total = sum((d.tva_deductible_value for d in depenses_annee(entreprise, annee)),
|
||||
def tva_deductible(entreprise, annee, mois=None):
|
||||
total = sum((d.tva_deductible_value for d in depenses_periode(entreprise, annee, mois)),
|
||||
Decimal("0"))
|
||||
return _q(total)
|
||||
|
||||
|
||||
def tva_collectee(entreprise, annee):
|
||||
"""TVA due : sur ventes + auto-liquidée sur achats UE/import."""
|
||||
return _q(tva_sur_ventes(entreprise, annee) + tva_autoliquidee(entreprise, annee))
|
||||
def tva_collectee(entreprise, annee, mois=None):
|
||||
return _q(tva_sur_ventes(entreprise, annee, mois)
|
||||
+ tva_autoliquidee(entreprise, annee, mois))
|
||||
|
||||
|
||||
# ---- CA mensuel ---------------------------------------------------------
|
||||
def tva_par_taux(entreprise, annee, mois=None):
|
||||
"""Ventilation des ventes encaissées par taux de TVA : {taux: (base_ht, tva)}."""
|
||||
lignes = LigneFacture.objects.filter(
|
||||
facture__in=factures_encaissees(entreprise, annee, mois))
|
||||
par_taux = {}
|
||||
for l in lignes.select_related("facture"):
|
||||
base = l.montant_ht
|
||||
taux = l.taux_tva
|
||||
b, t = par_taux.get(taux, (Decimal("0"), Decimal("0")))
|
||||
par_taux[taux] = (b + base, t + base * taux / Decimal("100"))
|
||||
return {taux: (_q(b), _q(t)) for taux, (b, t) in sorted(par_taux.items())}
|
||||
|
||||
|
||||
def intracom_periode(entreprise, annee, mois=None):
|
||||
"""Acquisitions UE/import auto-liquidées : (base_ht, tva)."""
|
||||
base = Decimal("0")
|
||||
tva = Decimal("0")
|
||||
for d in depenses_periode(entreprise, annee, mois):
|
||||
if d.autoliquidee and not d.neutre:
|
||||
base += d.montant_ht
|
||||
tva += d.tva_autoliquidee
|
||||
return _q(base), _q(tva)
|
||||
|
||||
|
||||
# ---- CA mensuel (graphe) ------------------------------------------------
|
||||
|
||||
def ca_ht_mensuel(entreprise, annee):
|
||||
"""Liste de 12 valeurs (index 0 = janvier) du CA HT encaissé (toutes activités)."""
|
||||
rows = (
|
||||
factures_encaissees(entreprise, annee)
|
||||
.annotate(mois=TruncMonth("date_encaissement"))
|
||||
.values("mois")
|
||||
.annotate(total=Sum("montant_ht"))
|
||||
)
|
||||
rows = (factures_encaissees(entreprise, annee)
|
||||
.annotate(mois=TruncMonth("date_encaissement"))
|
||||
.values("mois").annotate(total=Sum("montant_ht")))
|
||||
mensuel = [Decimal("0")] * 12
|
||||
for r in rows:
|
||||
if r["mois"]:
|
||||
@@ -101,7 +115,6 @@ def ca_ht_mensuel(entreprise, annee):
|
||||
# ---- Détail par activité ------------------------------------------------
|
||||
|
||||
def detail_activite(activite, ca, bareme):
|
||||
"""Calculs fiscaux pour une activité donnée."""
|
||||
label = dict(Activite.choices).get(activite, activite)
|
||||
d = {"activite": activite, "label": label, "ca_ht": ca, "bareme": bareme}
|
||||
if bareme:
|
||||
@@ -111,72 +124,147 @@ def detail_activite(activite, ca, bareme):
|
||||
"taux_vl": bareme.taux_vl,
|
||||
"versement_liberatoire": _q(ca * bareme.taux_vl / Decimal("100")),
|
||||
"abattement_fiscal": bareme.abattement_fiscal,
|
||||
"revenu_imposable": _q(
|
||||
ca * (Decimal("100") - bareme.abattement_fiscal) / Decimal("100")),
|
||||
"revenu_imposable": _q(ca * (Decimal("100") - bareme.abattement_fiscal) / Decimal("100")),
|
||||
"seuil_ca": bareme.seuil_ca,
|
||||
"pct_seuil_ca": (_q(ca / bareme.seuil_ca * Decimal("100"))
|
||||
if bareme.seuil_ca else Decimal("0")),
|
||||
"pct_seuil_ca": (_q(ca / bareme.seuil_ca * Decimal("100")) if bareme.seuil_ca else Decimal("0")),
|
||||
"seuil_tva_base": bareme.seuil_tva_base,
|
||||
"depasse_seuil_tva": ca > bareme.seuil_tva_base,
|
||||
"depasse_seuil_ca": ca > bareme.seuil_ca,
|
||||
})
|
||||
else:
|
||||
d.update({
|
||||
"taux_urssaf": None, "cotisations": None,
|
||||
"taux_vl": None, "versement_liberatoire": None,
|
||||
"abattement_fiscal": None, "revenu_imposable": None,
|
||||
"seuil_ca": None, "pct_seuil_ca": None,
|
||||
"seuil_tva_base": None, "depasse_seuil_tva": False,
|
||||
"depasse_seuil_ca": False,
|
||||
})
|
||||
for k in ("taux_urssaf", "cotisations", "taux_vl", "versement_liberatoire",
|
||||
"abattement_fiscal", "revenu_imposable", "seuil_ca", "pct_seuil_ca",
|
||||
"seuil_tva_base"):
|
||||
d[k] = None
|
||||
d["depasse_seuil_tva"] = d["depasse_seuil_ca"] = False
|
||||
return d
|
||||
|
||||
|
||||
def synthese(entreprise, annee):
|
||||
"""Tableau de bord chiffré, ventilé par activité puis totalisé."""
|
||||
ca_par_act = ca_ht_par_activite(entreprise, annee)
|
||||
|
||||
activites = set(ca_par_act) | {entreprise.activite}
|
||||
def _lignes_activite(entreprise, annee, mois=None):
|
||||
ca_par = ca_ht_par_activite(entreprise, annee, mois)
|
||||
activites = set(ca_par) | {entreprise.activite}
|
||||
baremes = {b.activite: b for b in Bareme.objects.filter(annee=annee)}
|
||||
return [detail_activite(a, ca_par.get(a, Decimal("0")), baremes.get(a))
|
||||
for a in sorted(activites)]
|
||||
|
||||
lignes = []
|
||||
for act in sorted(activites):
|
||||
ca = ca_par_act.get(act, Decimal("0"))
|
||||
lignes.append(detail_activite(act, ca, baremes.get(act)))
|
||||
|
||||
def somme(cle):
|
||||
vals = [l[cle] for l in lignes if l.get(cle) is not None]
|
||||
return _q(sum(vals, Decimal("0"))) if vals else None
|
||||
def _somme(lignes, cle):
|
||||
vals = [l[cle] for l in lignes if l.get(cle) is not None]
|
||||
return _q(sum(vals, Decimal("0"))) if vals else None
|
||||
|
||||
|
||||
# ---- Vue globale (annuelle) --------------------------------------------
|
||||
|
||||
def synthese(entreprise, annee):
|
||||
lignes = _lignes_activite(entreprise, annee)
|
||||
tva_ventes = tva_sur_ventes(entreprise, annee)
|
||||
tva_autoliq = tva_autoliquidee(entreprise, annee)
|
||||
tva_ded = tva_deductible(entreprise, annee)
|
||||
tva_col = _q(tva_ventes + tva_autoliq)
|
||||
|
||||
return {
|
||||
"entreprise": entreprise,
|
||||
"annee": annee,
|
||||
"lignes_activite": lignes,
|
||||
"entreprise": entreprise, "annee": annee, "lignes_activite": lignes,
|
||||
"bareme_manquant": any(l["bareme"] is None and l["ca_ht"] for l in lignes),
|
||||
"ca_ht": ca_ht(entreprise, annee),
|
||||
"cotisations_urssaf": somme("cotisations"),
|
||||
"versement_liberatoire": (
|
||||
somme("versement_liberatoire") if entreprise.versement_liberatoire else None),
|
||||
"revenu_imposable": somme("revenu_imposable"),
|
||||
"tva_sur_ventes": tva_ventes,
|
||||
"tva_autoliquidee": tva_autoliq,
|
||||
"tva_collectee": tva_col,
|
||||
"tva_deductible": tva_ded,
|
||||
"cotisations_urssaf": _somme(lignes, "cotisations"),
|
||||
"versement_liberatoire": (_somme(lignes, "versement_liberatoire")
|
||||
if entreprise.versement_liberatoire else None),
|
||||
"revenu_imposable": _somme(lignes, "revenu_imposable"),
|
||||
"tva_sur_ventes": tva_ventes, "tva_autoliquidee": tva_autoliq,
|
||||
"tva_collectee": tva_col, "tva_deductible": tva_ded,
|
||||
"tva_a_reverser": _q(tva_col - tva_ded),
|
||||
"ca_mensuel": ca_ht_mensuel(entreprise, annee),
|
||||
}
|
||||
|
||||
|
||||
# ---- Vue mensuelle ------------------------------------------------------
|
||||
|
||||
def tableau_mensuel(entreprise, annee):
|
||||
baremes = {b.activite: b for b in Bareme.objects.filter(annee=annee)}
|
||||
rows = []
|
||||
for m in range(1, 13):
|
||||
ca_par = ca_ht_par_activite(entreprise, annee, m)
|
||||
cot = Decimal("0")
|
||||
vl = Decimal("0")
|
||||
for act, ca in ca_par.items():
|
||||
b = baremes.get(act)
|
||||
if b:
|
||||
cot += ca * b.taux_urssaf / Decimal("100")
|
||||
vl += ca * b.taux_vl / Decimal("100")
|
||||
tva_col = tva_collectee(entreprise, annee, m)
|
||||
tva_ded = tva_deductible(entreprise, annee, m)
|
||||
rows.append({
|
||||
"mois": m, "nom": MOIS_FR[m - 1],
|
||||
"ca_ht": ca_ht(entreprise, annee, m),
|
||||
"cotisations": _q(cot),
|
||||
"versement_liberatoire": _q(vl) if entreprise.versement_liberatoire else None,
|
||||
"tva_collectee": tva_col, "tva_deductible": tva_ded,
|
||||
"tva_a_reverser": _q(tva_col - tva_ded),
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
# ---- Déclaration URSSAF (période) --------------------------------------
|
||||
|
||||
def declaration_urssaf(entreprise, annee, mois=None):
|
||||
lignes = _lignes_activite(entreprise, annee, mois)
|
||||
return {
|
||||
"entreprise": entreprise, "annee": annee, "mois": mois,
|
||||
"mois_nom": MOIS_FR[mois - 1] if mois else "Année entière",
|
||||
"lignes": lignes,
|
||||
"ca_ht": ca_ht(entreprise, annee, mois),
|
||||
"cotisations": _somme(lignes, "cotisations"),
|
||||
"versement_liberatoire": (_somme(lignes, "versement_liberatoire")
|
||||
if entreprise.versement_liberatoire else None),
|
||||
}
|
||||
|
||||
|
||||
# ---- Déclaration TVA (période) -----------------------------------------
|
||||
|
||||
def declaration_tva(entreprise, annee, mois=None):
|
||||
par_taux = tva_par_taux(entreprise, annee, mois)
|
||||
intra_base, intra_tva = intracom_periode(entreprise, annee, mois)
|
||||
tva_ventes = sum((t for _, t in par_taux.values()), Decimal("0"))
|
||||
tva_ded = tva_deductible(entreprise, annee, mois)
|
||||
tva_brute = _q(tva_ventes + intra_tva)
|
||||
a_payer = _q(tva_brute - tva_ded)
|
||||
|
||||
def couple(taux):
|
||||
return par_taux.get(Decimal(taux), (Decimal("0.00"), Decimal("0.00")))
|
||||
|
||||
b20, t20 = couple("20.00")
|
||||
b10, t10 = couple("10.00")
|
||||
b55, t55 = couple("5.50")
|
||||
base_taxable = _q(sum((b for b, _ in par_taux.values()), Decimal("0")))
|
||||
|
||||
return {
|
||||
"entreprise": entreprise, "annee": annee, "mois": mois,
|
||||
"mois_nom": MOIS_FR[mois - 1] if mois else "Année entière",
|
||||
"par_taux": [{"taux": k, "base": v[0], "tva": v[1]} for k, v in par_taux.items()],
|
||||
# Résumé simplifié
|
||||
"tva_sur_ventes": _q(tva_ventes),
|
||||
"tva_autoliquidee": intra_tva,
|
||||
"tva_collectee": tva_brute,
|
||||
"tva_deductible": tva_ded,
|
||||
"tva_a_payer": a_payer,
|
||||
"credit_tva": _q(-a_payer) if a_payer < 0 else Decimal("0.00"),
|
||||
# Champs CA3
|
||||
"ca3": {
|
||||
"l01_base_taxable": base_taxable,
|
||||
"l08_base": b20, "l08_tva": t20,
|
||||
"l9B_base": b10, "l9B_tva": t10,
|
||||
"l09_base": b55, "l09_tva": t55,
|
||||
"l03_intra_base": intra_base,
|
||||
"l17_intra_tva": intra_tva,
|
||||
"l16_tva_brute": tva_brute,
|
||||
"l20_tva_deductible": tva_ded,
|
||||
"l28_total_deductible": tva_ded,
|
||||
"l32_a_payer": a_payer if a_payer > 0 else Decimal("0.00"),
|
||||
"l25_credit": _q(-a_payer) if a_payer < 0 else Decimal("0.00"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def annees_disponibles(entreprise):
|
||||
qs = (
|
||||
Facture.objects.filter(entreprise=entreprise,
|
||||
statut=StatutFacture.ENCAISSEE,
|
||||
date_encaissement__isnull=False)
|
||||
.dates("date_encaissement", "year")
|
||||
)
|
||||
qs = Facture.objects.filter(entreprise=entreprise, statut=StatutFacture.ENCAISSEE,
|
||||
date_encaissement__isnull=False).dates("date_encaissement", "year")
|
||||
return sorted({d.year for d in qs}, reverse=True)
|
||||
|
||||
Reference in New Issue
Block a user