""" Logique fiscale — JB Compta. 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, 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(v): return (v or Decimal("0")).quantize(CENT) # ---- 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_periode(entreprise, annee, mois=None): qs = Depense.objects.filter(entreprise=entreprise, date__year=annee) if mois: qs = qs.filter(date__month=mois) return qs # ---- 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, mois=None): return _q(factures_encaissees(entreprise, annee, mois).aggregate( s=Sum("montant_ht"))["s"]) # ---- TVA ---------------------------------------------------------------- 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, 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, 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, mois=None): return _q(tva_sur_ventes(entreprise, annee, mois) + tva_autoliquidee(entreprise, annee, mois)) 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): 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): 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: 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 _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)] 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, "bareme_manquant": any(l["bareme"] is None and l["ca_ht"] for l in lignes), "ca_ht": ca_ht(entreprise, annee), "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") return sorted({d.year for d in qs}, reverse=True)