""" 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. """ 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 CENT = Decimal("0.01") def _q(value): return (value or Decimal("0")).quantize(CENT) def factures_encaissees(entreprise, annee): return Facture.objects.filter( entreprise=entreprise, statut=StatutFacture.ENCAISSEE, date_encaissement__year=annee, ) def depenses_annee(entreprise, annee): return Depense.objects.filter(entreprise=entreprise, date__year=annee) def ca_ht_par_activite(entreprise, annee): """Dict {activite: CA HT encaissé}.""" rows = ( factures_encaissees(entreprise, annee) .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"]) # ---- TVA ---------------------------------------------------------------- def tva_sur_ventes(entreprise, annee): agg = factures_encaissees(entreprise, annee).aggregate(s=Sum("montant_tva")) return _q(agg["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)), 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)), 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)) # ---- CA mensuel --------------------------------------------------------- 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")) ) mensuel = [Decimal("0")] * 12 for r in rows: if r["mois"]: mensuel[r["mois"].month - 1] = _q(r["total"]) return mensuel # ---- 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: d.update({ "taux_urssaf": bareme.taux_urssaf, "cotisations": _q(ca * bareme.taux_urssaf / Decimal("100")), "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")), "seuil_ca": bareme.seuil_ca, "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, }) 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} baremes = {b.activite: b for b in Bareme.objects.filter(annee=annee)} 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 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, "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, "tva_a_reverser": _q(tva_col - tva_ded), "ca_mensuel": ca_ht_mensuel(entreprise, annee), } def annees_disponibles(entreprise): 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)