diff --git a/compta/__pycache__/models.cpython-310.pyc b/compta/__pycache__/models.cpython-310.pyc
index 0b5461e..42213d9 100644
Binary files a/compta/__pycache__/models.cpython-310.pyc and b/compta/__pycache__/models.cpython-310.pyc differ
diff --git a/compta/__pycache__/services.cpython-310.pyc b/compta/__pycache__/services.cpython-310.pyc
index 18f3e3e..cbcd675 100644
Binary files a/compta/__pycache__/services.cpython-310.pyc and b/compta/__pycache__/services.cpython-310.pyc differ
diff --git a/compta/__pycache__/urls.cpython-310.pyc b/compta/__pycache__/urls.cpython-310.pyc
index accb77d..59866f7 100644
Binary files a/compta/__pycache__/urls.cpython-310.pyc and b/compta/__pycache__/urls.cpython-310.pyc differ
diff --git a/compta/__pycache__/views.cpython-310.pyc b/compta/__pycache__/views.cpython-310.pyc
index 759d18e..329267b 100644
Binary files a/compta/__pycache__/views.cpython-310.pyc and b/compta/__pycache__/views.cpython-310.pyc differ
diff --git a/compta/__pycache__/views_saisie.cpython-310.pyc b/compta/__pycache__/views_saisie.cpython-310.pyc
index acb90aa..0c915b5 100644
Binary files a/compta/__pycache__/views_saisie.cpython-310.pyc and b/compta/__pycache__/views_saisie.cpython-310.pyc differ
diff --git a/compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py b/compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py
new file mode 100644
index 0000000..0813f64
--- /dev/null
+++ b/compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py
@@ -0,0 +1,34 @@
+# Generated by Django 5.2.15 on 2026-06-29 22:49
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('compta', '0005_categorie_entreprise_vue_tva_alter_depense_categorie'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='paiement',
+ name='categorie',
+ field=models.ForeignKey(blank=True, help_text='Surtout pour les mouvements neutres (remboursement TVA, virement…).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paiements', to='compta.categorie'),
+ ),
+ migrations.AlterField(
+ model_name='depense',
+ name='paiement',
+ field=models.ForeignKey(blank=True, help_text='Opération bancaire dont cette dépense fait partie.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='depenses', to='compta.paiement'),
+ ),
+ migrations.AlterField(
+ model_name='entreprise',
+ name='activite',
+ field=models.CharField(choices=[('vente', 'Vente de marchandises (BIC)'), ('service_bic', 'Prestation de services (BIC)'), ('service_bnc', 'Prestation de services / libéral (BNC)')], default='service_bnc', help_text='Activité par défaut des nouvelles factures.', max_length=20, verbose_name='Activité principale'),
+ ),
+ migrations.AlterField(
+ model_name='facture',
+ name='date_encaissement',
+ field=models.DateField(blank=True, help_text="Le CA micro est compté à l'encaissement.", null=True),
+ ),
+ ]
diff --git a/compta/migrations/__pycache__/0001_initial.cpython-310.pyc b/compta/migrations/__pycache__/0001_initial.cpython-310.pyc
deleted file mode 100644
index 665cab3..0000000
Binary files a/compta/migrations/__pycache__/0001_initial.cpython-310.pyc and /dev/null differ
diff --git a/compta/migrations/__pycache__/0002_depense_activite_alter_entreprise_activite.cpython-310.pyc b/compta/migrations/__pycache__/0002_depense_activite_alter_entreprise_activite.cpython-310.pyc
deleted file mode 100644
index 67c4a01..0000000
Binary files a/compta/migrations/__pycache__/0002_depense_activite_alter_entreprise_activite.cpython-310.pyc and /dev/null differ
diff --git a/compta/migrations/__pycache__/0003_depense_regime_tva_depense_taux_tva_and_more.cpython-310.pyc b/compta/migrations/__pycache__/0003_depense_regime_tva_depense_taux_tva_and_more.cpython-310.pyc
deleted file mode 100644
index afc3840..0000000
Binary files a/compta/migrations/__pycache__/0003_depense_regime_tva_depense_taux_tva_and_more.cpython-310.pyc and /dev/null differ
diff --git a/compta/migrations/__pycache__/0004_paiement_depense_paiement.cpython-310.pyc b/compta/migrations/__pycache__/0004_paiement_depense_paiement.cpython-310.pyc
deleted file mode 100644
index 3ce7cc6..0000000
Binary files a/compta/migrations/__pycache__/0004_paiement_depense_paiement.cpython-310.pyc and /dev/null differ
diff --git a/compta/migrations/__pycache__/0005_categorie_entreprise_vue_tva_alter_depense_categorie.cpython-310.pyc b/compta/migrations/__pycache__/0005_categorie_entreprise_vue_tva_alter_depense_categorie.cpython-310.pyc
deleted file mode 100644
index 3973bf4..0000000
Binary files a/compta/migrations/__pycache__/0005_categorie_entreprise_vue_tva_alter_depense_categorie.cpython-310.pyc and /dev/null differ
diff --git a/compta/migrations/__pycache__/__init__.cpython-310.pyc b/compta/migrations/__pycache__/__init__.cpython-310.pyc
deleted file mode 100644
index a5e4238..0000000
Binary files a/compta/migrations/__pycache__/__init__.cpython-310.pyc and /dev/null differ
diff --git a/compta/models.py b/compta/models.py
index 4a9e2c6..40535d7 100644
--- a/compta/models.py
+++ b/compta/models.py
@@ -1,9 +1,5 @@
"""
Modèles de données — JB Compta.
-
-Conçus pour rester valables si le statut évolue : les taux et seuils
-ne sont jamais codés en dur, ils vivent dans le modèle `Bareme`
-(par activité et par année).
"""
from decimal import Decimal
@@ -25,24 +21,14 @@ class StatutFacture(models.TextChoices):
class RegimeTva(models.TextChoices):
- """Régime de TVA d'une dépense (achat)."""
NATIONALE = "nationale", "Nationale (TVA française facturée)"
INTRACOM = "intracom", "Intracommunautaire (autoliquidation)"
IMPORT = "import", "Import hors UE (autoliquidation)"
class Categorie(models.Model):
- """Catégorie globale (partagée entre entreprises).
-
- Une catégorie « neutre » (exclure_calculs=True) sert au suivi de trésorerie
- mais n'entre PAS dans les calculs fiscaux : paiement d'URSSAF, de TVA,
- remboursement de TVA, prélèvement personnel, virement interne…
- """
- USAGE = [
- ("depense", "Dépense"),
- ("vente", "Vente"),
- ("neutre", "Mouvement neutre"),
- ]
+ """Catégorie globale. exclure_calculs=True => mouvement neutre (hors fiscal)."""
+ USAGE = [("depense", "Dépense"), ("vente", "Vente"), ("neutre", "Mouvement neutre")]
nom = models.CharField(max_length=100, unique=True)
usage = models.CharField(max_length=10, choices=USAGE, default="depense")
exclure_calculs = models.BooleanField(
@@ -62,21 +48,14 @@ class Categorie(models.Model):
class Bareme(models.Model):
- """Taux et seuils officiels, par activité et par année (paramétrable)."""
activite = models.CharField(max_length=20, choices=Activite.choices)
annee = models.PositiveIntegerField()
- taux_urssaf = models.DecimalField(
- "Taux cotisations URSSAF (%)", max_digits=5, decimal_places=2)
- abattement_fiscal = models.DecimalField(
- "Abattement forfaitaire (%)", max_digits=5, decimal_places=2)
- taux_vl = models.DecimalField(
- "Taux versement libératoire (%)", max_digits=4, decimal_places=2)
- seuil_ca = models.DecimalField(
- "Plafond CA micro (€)", max_digits=12, decimal_places=2)
- seuil_tva_base = models.DecimalField(
- "Seuil franchise TVA (€)", max_digits=12, decimal_places=2)
- seuil_tva_majore = models.DecimalField(
- "Seuil franchise TVA majoré (€)", max_digits=12, decimal_places=2)
+ taux_urssaf = models.DecimalField("Taux cotisations URSSAF (%)", max_digits=5, decimal_places=2)
+ abattement_fiscal = models.DecimalField("Abattement forfaitaire (%)", max_digits=5, decimal_places=2)
+ taux_vl = models.DecimalField("Taux versement libératoire (%)", max_digits=4, decimal_places=2)
+ seuil_ca = models.DecimalField("Plafond CA micro (€)", max_digits=12, decimal_places=2)
+ seuil_tva_base = models.DecimalField("Seuil franchise TVA (€)", max_digits=12, decimal_places=2)
+ seuil_tva_majore = models.DecimalField("Seuil franchise TVA majoré (€)", max_digits=12, decimal_places=2)
class Meta:
verbose_name = "Barème"
@@ -95,19 +74,14 @@ class Entreprise(models.Model):
activite = models.CharField(
"Activité principale", max_length=20, choices=Activite.choices,
default=Activite.SERVICE_BNC,
- help_text="Activité par défaut des nouvelles factures. "
- "Une entreprise peut avoir plusieurs activités : "
- "chaque facture/dépense porte la sienne.")
+ help_text="Activité par défaut des nouvelles factures.")
franchise_tva = models.BooleanField(
"En franchise de TVA", default=False,
help_text="Décochez si vous facturez et déclarez la TVA.")
taux_tva_defaut = models.DecimalField(
- "Taux de TVA par défaut (%)", max_digits=4, decimal_places=2,
- default=Decimal("20.00"))
- versement_liberatoire = models.BooleanField(
- "Option versement libératoire", default=False)
- num_tva_intracom = models.CharField(
- "N° TVA intracommunautaire", max_length=20, blank=True)
+ "Taux de TVA par défaut (%)", max_digits=4, decimal_places=2, default=Decimal("20.00"))
+ versement_liberatoire = models.BooleanField("Option versement libératoire", default=False)
+ num_tva_intracom = models.CharField("N° TVA intracommunautaire", max_length=20, blank=True)
vue_tva = models.CharField(
"Mode d'affichage de la déclaration TVA", max_length=10,
choices=VUE_TVA, default="simplifie")
@@ -128,8 +102,7 @@ class Entreprise(models.Model):
class Client(models.Model):
- entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE,
- related_name="clients")
+ entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="clients")
nom = models.CharField(max_length=200)
email = models.EmailField(blank=True)
siret = models.CharField(max_length=20, blank=True)
@@ -146,25 +119,18 @@ class Client(models.Model):
class Facture(models.Model):
- entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE,
- related_name="factures")
- client = models.ForeignKey(Client, on_delete=models.PROTECT,
- related_name="factures")
+ entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="factures")
+ client = models.ForeignKey(Client, on_delete=models.PROTECT, related_name="factures")
numero = models.CharField(max_length=50)
activite = models.CharField(max_length=20, choices=Activite.choices)
date_emission = models.DateField()
date_encaissement = models.DateField(
null=True, blank=True,
- help_text="À renseigner une fois la facture payée. "
- "Le CA micro est compté à l'encaissement.")
- statut = models.CharField(max_length=12, choices=StatutFacture.choices,
- default=StatutFacture.BROUILLON)
- montant_ht = models.DecimalField(max_digits=12, decimal_places=2,
- default=Decimal("0"))
- montant_tva = models.DecimalField(max_digits=12, decimal_places=2,
- default=Decimal("0"))
- montant_ttc = models.DecimalField(max_digits=12, decimal_places=2,
- default=Decimal("0"))
+ help_text="Le CA micro est compté à l'encaissement.")
+ statut = models.CharField(max_length=12, choices=StatutFacture.choices, default=StatutFacture.BROUILLON)
+ montant_ht = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal("0"))
+ montant_tva = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal("0"))
+ montant_ttc = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal("0"))
notes = models.TextField(blank=True)
cree_le = models.DateTimeField(auto_now_add=True)
@@ -178,7 +144,6 @@ class Facture(models.Model):
return f"{self.numero} — {self.client}"
def recompute_totaux(self, save=True):
- """Recalcule HT/TVA/TTC à partir des lignes."""
ht = Decimal("0")
tva = Decimal("0")
for ligne in self.lignes.all():
@@ -193,14 +158,11 @@ class Facture(models.Model):
class LigneFacture(models.Model):
- facture = models.ForeignKey(Facture, on_delete=models.CASCADE,
- related_name="lignes")
+ facture = models.ForeignKey(Facture, on_delete=models.CASCADE, related_name="lignes")
designation = models.CharField(max_length=255)
- quantite = models.DecimalField(max_digits=12, decimal_places=3,
- default=Decimal("1"))
+ quantite = models.DecimalField(max_digits=12, decimal_places=3, default=Decimal("1"))
prix_unitaire_ht = models.DecimalField(max_digits=12, decimal_places=2)
- taux_tva = models.DecimalField(max_digits=4, decimal_places=2,
- default=Decimal("0"))
+ taux_tva = models.DecimalField(max_digits=4, decimal_places=2, default=Decimal("0"))
class Meta:
verbose_name = "Ligne de facture"
@@ -215,15 +177,17 @@ class LigneFacture(models.Model):
class Paiement(models.Model):
- """Opération bancaire réelle, à éclater en une ou plusieurs dépenses
- (ex. un débit Amazon regroupant plusieurs vendeurs/factures)."""
+ """Opération bancaire réelle, à éclater en une ou plusieurs dépenses."""
SENS = [("debit", "Débit (achat)"), ("credit", "Crédit (encaissement)")]
- entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE,
- related_name="paiements")
+ entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="paiements")
date = models.DateField()
libelle = models.CharField("Libellé bancaire", max_length=255)
montant_total = models.DecimalField(max_digits=12, decimal_places=2)
sens = models.CharField(max_length=6, choices=SENS, default="debit")
+ categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL,
+ null=True, blank=True, related_name="paiements",
+ help_text="Surtout pour les mouvements neutres "
+ "(remboursement TVA, virement…).")
cree_le = models.DateTimeField(auto_now_add=True)
class Meta:
@@ -249,32 +213,27 @@ class Paiement(models.Model):
class Depense(models.Model):
- entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE,
- related_name="depenses")
- paiement = models.ForeignKey(Paiement, on_delete=models.SET_NULL,
- null=True, blank=True, related_name="depenses",
- help_text="Opération bancaire dont cette dépense "
- "fait partie (achat groupé éclaté).")
+ entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="depenses")
+ paiement = models.ForeignKey(Paiement, on_delete=models.SET_NULL, null=True, blank=True,
+ related_name="depenses",
+ help_text="Opération bancaire dont cette dépense fait partie.")
date = models.DateField()
activite = models.CharField(
max_length=20, choices=Activite.choices, blank=True,
help_text="Activité concernée (facultatif, pour la ventilation).")
- categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL,
- null=True, blank=True, related_name="depenses")
+ categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL, null=True, blank=True,
+ related_name="depenses")
libelle = models.CharField(max_length=255)
montant_ttc = models.DecimalField(
"Montant payé (€)", max_digits=12, decimal_places=2,
help_text="Pour un achat UE/import, c'est le montant HT (sans TVA facturée).")
montant_tva = models.DecimalField(
- "TVA déductible facturée (€)", max_digits=12, decimal_places=2,
- default=Decimal("0"),
+ "TVA déductible facturée (€)", max_digits=12, decimal_places=2, default=Decimal("0"),
help_text="TVA payée au fournisseur (achats nationaux uniquement).")
regime_tva = models.CharField(
- "Régime de TVA", max_length=12, choices=RegimeTva.choices,
- default=RegimeTva.NATIONALE)
+ "Régime de TVA", max_length=12, choices=RegimeTva.choices, default=RegimeTva.NATIONALE)
taux_tva = models.DecimalField(
- "Taux TVA autoliquidation (%)", max_digits=4, decimal_places=2,
- default=Decimal("0"),
+ "Taux TVA autoliquidation (%)", max_digits=4, decimal_places=2, default=Decimal("0"),
help_text="Taux français à auto-liquider pour les achats UE/import (ex. 20).")
cree_le = models.DateTimeField(auto_now_add=True)
@@ -288,33 +247,26 @@ class Depense(models.Model):
@property
def neutre(self):
- """Mouvement neutre : exclu des calculs fiscaux."""
return bool(self.categorie and self.categorie.exclure_calculs)
@property
def autoliquidee(self):
- """True si la TVA est auto-liquidée (achat UE ou import)."""
return self.regime_tva in (RegimeTva.INTRACOM, RegimeTva.IMPORT)
@property
def montant_ht(self):
- # En autoliquidation, le fournisseur ne facture pas de TVA : HT = montant payé.
if self.autoliquidee:
return self.montant_ttc.quantize(Decimal("0.01"))
return (self.montant_ttc - self.montant_tva).quantize(Decimal("0.01"))
@property
def tva_autoliquidee(self):
- """TVA française auto-liquidée (comptée en collectée ET en déductible)."""
if self.autoliquidee and not self.neutre:
- return (self.montant_ttc * self.taux_tva / Decimal("100")).quantize(
- Decimal("0.01"))
+ return (self.montant_ttc * self.taux_tva / Decimal("100")).quantize(Decimal("0.01"))
return Decimal("0.00")
@property
def tva_deductible_value(self):
- """TVA récupérable : facturée (national) ou auto-liquidée (UE/import).
- Nulle pour un mouvement neutre."""
if self.neutre:
return Decimal("0.00")
if self.autoliquidee:
diff --git a/compta/services.py b/compta/services.py
index d5bfff7..ebbb7e1 100644
--- a/compta/services.py
+++ b/compta/services.py
@@ -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)
diff --git a/compta/templates/compta/base.html b/compta/templates/compta/base.html
new file mode 100644
index 0000000..9bb265d
--- /dev/null
+++ b/compta/templates/compta/base.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+JB Compta — {% block titre %}Tableau de bord{% endblock %}
+
+
+
+
+{% if not entreprise %}
+ Aucune entreprise
+ Crée une entreprise dans l'administration ,
+ ou saisis une opération .
+{% else %}
+
+
+ {{ entreprise.nom }}
+ {{ entreprise.get_activite_display }} (principale) ·
+ {% if entreprise.franchise_tva %}Franchise de TVA{% else %}Assujetti TVA{% endif %}
+ {% if entreprise.versement_liberatoire %}· Versement libératoire{% endif %}
+ · Exercice {{ annee }}
+
+
+
+ {% block content %}{% endblock %}
+{% endif %}
+
+{% block scripts %}{% endblock %}
+
+
diff --git a/compta/templates/compta/dashboard.html b/compta/templates/compta/dashboard.html
index 6694271..f076149 100644
--- a/compta/templates/compta/dashboard.html
+++ b/compta/templates/compta/dashboard.html
@@ -1,86 +1,21 @@
-
-
-
-
-
-JB Compta — Tableau de bord
-
-
-
-
-{% if not entreprise %}
- Aucune entreprise
- Crée une entreprise et des factures dans
- l'administration , puis reviens ici.
-{% else %}
- {{ entreprise.nom }}
- {{ entreprise.get_activite_display }} (principale) ·
- {% if entreprise.franchise_tva %}Franchise de TVA{% else %}Assujetti TVA{% endif %}
- {% if entreprise.versement_liberatoire %}· Versement libératoire{% endif %}
- · Exercice {{ annee }}
-
-
+{% extends "compta/base.html" %}
+{% block titre %}Global{% endblock %}
+{% block content %}
{% if bareme_manquant %}
- Une activité avec du CA n'a pas de barème {{ annee }}. Ajoute-le dans
-
Barèmes .
+ Une activité avec du CA n'a pas de barème {{ annee }}.
+ Ajoute-le dans
Barèmes .
{% endif %}
-
CA HT encaissé (total)
{{ ca_ht }} €
-
Cotisations URSSAF (total)
+
Cotisations URSSAF
{{ cotisations_urssaf|default:"—" }} €
@@ -91,93 +26,55 @@
TVA à reverser
{{ tva_a_reverser }} €
{{ tva_collectee }} collectée − {{ tva_deductible }} déductible
- {% if tva_autoliquidee %}
dont {{ tva_autoliquidee }} € auto-liquidée (UE/import)
{% endif %}
+ {% if tva_autoliquidee %}
dont {{ tva_autoliquidee }} € auto-liquidée
{% endif %}
-
Ventilation par activité
-
-
- Activité CA HT % plafond
- URSSAF Impôt (VL) Revenu imposable
-
-
+ Activité CA HT % plafond URSSAF Impôt (VL) Revenu imposable
{% for l in lignes_activite %}
{{ l.label }}
{{ l.ca_ht }} €
-
- {% if l.pct_seuil_ca is not None %}{{ l.pct_seuil_ca }} %{% else %}—{% endif %}
-
+ {% if l.pct_seuil_ca is not None %}{{ l.pct_seuil_ca }} %{% else %}—{% endif %}
{% if l.cotisations is not None %}{{ l.cotisations }} €{% else %}—{% endif %}
{% if l.versement_liberatoire is not None %}{{ l.versement_liberatoire }} €{% else %}—{% endif %}
{% if l.revenu_imposable is not None %}{{ l.revenu_imposable }} €{% else %}—{% endif %}
{% endfor %}
-
- Total
- {{ ca_ht }} €
-
- {{ cotisations_urssaf|default:"—" }} €
- {{ versement_liberatoire|default:"—" }} €
- {{ revenu_imposable|default:"—" }} €
-
+ Total {{ ca_ht }} € {{ cotisations_urssaf|default:"—" }} € {{ versement_liberatoire|default:"—" }} € {{ revenu_imposable|default:"—" }} €
-
- TVA
-
-
- TVA collectée sur ventes {{ tva_sur_ventes }} €
- TVA auto-liquidée (UE / import) {{ tva_autoliquidee }} €
- TVA collectée totale (due) {{ tva_collectee }} €
- TVA déductible {{ tva_deductible }} €
- TVA à reverser {{ tva_a_reverser }} €
-
-
-
-
CA HT mensuel (toutes activités)
+
CA HT mensuel
-
À provisionner (année)
-
- Poste Montant
- URSSAF {{ cotisations_urssaf|default:"—" }} €
- Impôt (VL) {{ versement_liberatoire|default:"—" }} €
- TVA {{ tva_a_reverser }} €
-
-
-
Exports CSV
+
Exports CSV
+
+{% endblock %}
+{% block scripts %}
-{% endif %}
-
-
+{% endblock %}
diff --git a/compta/templates/compta/decl_tva.html b/compta/templates/compta/decl_tva.html
new file mode 100644
index 0000000..96189d2
--- /dev/null
+++ b/compta/templates/compta/decl_tva.html
@@ -0,0 +1,67 @@
+{% extends "compta/base.html" %}
+{% block titre %}Déclaration TVA{% endblock %}
+
+{% block extrafilters %}
+
+ {% for num, nom in mois_liste %}
+ {{ nom }}
+ {% endfor %}
+
+{% endblock %}
+
+{% block content %}
+
Déclaration TVA — {{ decl.mois_nom }} {{ annee }}
+
+ Mode :
+
Résumé ·
+
Champs CA3
+ (réglage par défaut de l'entreprise : {{ entreprise.get_vue_tva_display }})
+
+
+ {% if mode == 'ca3' %}
+
+
+ Ligne CA3 Libellé Base HT TVA
+
+ 01 Ventes / prestations taxables {{ decl.ca3.l01_base_taxable }} €
+ 08 Taux normal 20 % {{ decl.ca3.l08_base }} € {{ decl.ca3.l08_tva }} €
+ 9B Taux réduit 10 % {{ decl.ca3.l9B_base }} € {{ decl.ca3.l9B_tva }} €
+ 09 Taux réduit 5,5 % {{ decl.ca3.l09_base }} € {{ decl.ca3.l09_tva }} €
+ 03 Acquisitions intracommunautaires {{ decl.ca3.l03_intra_base }} €
+ 17 dont TVA sur acquisitions intracom {{ decl.ca3.l17_intra_tva }} €
+ 16 TVA brute due {{ decl.ca3.l16_tva_brute }} €
+ 20 TVA déductible (biens et services) {{ decl.ca3.l20_tva_deductible }} €
+ 28 Total TVA déductible {{ decl.ca3.l28_total_deductible }} €
+ 32 TVA à payer {{ decl.ca3.l32_a_payer }} €
+ {% if decl.ca3.l25_credit %}25 Crédit de TVA {{ decl.ca3.l25_credit }} € {% endif %}
+
+
+
+
Numéros de lignes du formulaire 3310-CA3 (régime réel normal). À recouper
+ avec ta déclaration officielle — vérifie notamment la ventilation par taux.
+ {% else %}
+
+
TVA collectée sur ventes
{{ decl.tva_sur_ventes }} €
+
TVA auto-liquidée (UE/import)
{{ decl.tva_autoliquidee }} €
+
TVA collectée totale
{{ decl.tva_collectee }} €
+
TVA déductible
{{ decl.tva_deductible }} €
+
+
{% if decl.credit_tva %}Crédit de TVA{% else %}TVA à payer{% endif %}
+
{% if decl.credit_tva %}{{ decl.credit_tva }}{% else %}{{ decl.tva_a_payer }}{% endif %} €
+
+
+ {% if decl.par_taux %}
+
Détail par taux (ventes)
+
+
+ Taux Base HT TVA
+
+ {% for t in decl.par_taux %}{{ t.taux }} % {{ t.base }} € {{ t.tva }} € {% endfor %}
+
+
+
+ {% endif %}
+ {% endif %}
+{% endblock %}
diff --git a/compta/templates/compta/decl_urssaf.html b/compta/templates/compta/decl_urssaf.html
new file mode 100644
index 0000000..ef53586
--- /dev/null
+++ b/compta/templates/compta/decl_urssaf.html
@@ -0,0 +1,43 @@
+{% extends "compta/base.html" %}
+{% block titre %}Déclaration URSSAF{% endblock %}
+
+{% block extrafilters %}
+
+ {% for num, nom in mois_liste %}
+ {{ nom }}
+ {% endfor %}
+
+{% endblock %}
+
+{% block content %}
+
Déclaration URSSAF — {{ decl.mois_nom }} {{ annee }}
+
À déclarer sur autoentrepreneur.urssaf.fr : le CA HT encaissé de la période,
+ par type d'activité. Les cotisations (et l'impôt si versement libératoire) en découlent.
+
+
+
+
+ Activité CA HT à déclarer Taux URSSAF Cotisations
+ {% if decl.versement_liberatoire is not None %}Taux VL Impôt (VL) {% endif %}
+
+
+ {% for l in decl.lignes %}
+
+ {{ l.label }}
+ {{ l.ca_ht }} €
+ {% if l.taux_urssaf is not None %}{{ l.taux_urssaf }} %{% else %}—{% endif %}
+ {% if l.cotisations is not None %}{{ l.cotisations }} €{% else %}—{% endif %}
+ {% if decl.versement_liberatoire is not None %}
+ {% if l.taux_vl is not None %}{{ l.taux_vl }} %{% else %}—{% endif %}
+ {% if l.versement_liberatoire is not None %}{{ l.versement_liberatoire }} €{% else %}—{% endif %}
+ {% endif %}
+
+ {% endfor %}
+
+ Total {{ decl.ca_ht }} € {{ decl.cotisations|default:"—" }} €
+ {% if decl.versement_liberatoire is not None %}{{ decl.versement_liberatoire }} € {% endif %}
+
+
+
+
+{% endblock %}
diff --git a/compta/templates/compta/mensuel.html b/compta/templates/compta/mensuel.html
new file mode 100644
index 0000000..62df8c4
--- /dev/null
+++ b/compta/templates/compta/mensuel.html
@@ -0,0 +1,29 @@
+{% extends "compta/base.html" %}
+{% block titre %}Mensuel{% endblock %}
+
+{% block content %}
+
Vue mensuelle {{ annee }}
+
+
+
+
+ Mois CA HT Cotisations URSSAF
+ Impôt (VL) TVA collectée TVA déductible TVA à reverser
+
+
+
+ {% for r in rows %}
+
+ {{ r.nom }}
+ {{ r.ca_ht }} €
+ {{ r.cotisations }} €
+ {% if r.versement_liberatoire is not None %}{{ r.versement_liberatoire }} €{% else %}—{% endif %}
+ {{ r.tva_collectee }} €
+ {{ r.tva_deductible }} €
+ {{ r.tva_a_reverser }} €
+
+ {% endfor %}
+
+
+
+{% endblock %}
diff --git a/compta/templates/compta/saisie_paiement.html b/compta/templates/compta/saisie_paiement.html
index de3ba61..47659db 100644
--- a/compta/templates/compta/saisie_paiement.html
+++ b/compta/templates/compta/saisie_paiement.html
@@ -3,7 +3,7 @@
-
JB Compta — Saisie d'un paiement
+
JB Compta — Saisie
-
Saisie d'un paiement
-
+
Saisie d'une opération
+
- {% if messages %}
- {% for m in messages %}
-
{{ m }}
- {% endfor %}
- {% endif %}
+ {% if messages %}{% for m in messages %}
{{ m }}
{% endfor %}{% endif %}
Libellé bancaire
-
+
- Montant total payé (€)
+ Montant signé (€)
+ placeholder="-150 ou 168.40" oninput="toggle()">
-
+
+
+
Dépense — éclate en une ou plusieurs lignes.
Libellé / vendeur
Catégorie
- Activité
- Régime TVA
+ Activité
+ Régime TVA
Taux %
Montant €
- TVA déduct. €
+ TVA déduct.
-
-
- + Ajouter une ligne
-
-
+
+ Ajouter une ligne
Réparti : 0.00 €
Reste : 0.00 €
+
+
+
Vente — crée une facture encaissée (ou un mouvement neutre).
+
+
+ Client existant
+
+ —
+ {% for c in clients %}{{ c.nom }} ({{ c.entreprise.nom }}) {% endfor %}
+
+
+
+ …ou nouveau client
+
+
+
+ Activité
+
+ {% for val,lab in activites %}{{ lab }} {% endfor %}
+
+
+
+ Taux TVA (%)
+
+
+
+ Désignation
+
+
+
+ Ou catégorie neutre (ex. remboursement TVA)
+
+ — (vente normale)
+ {% for c in categories_vente %}{{ c.nom }}{% if c.exclure_calculs %} (neutre){% endif %} {% endfor %}
+
+
+
+
Le montant TTC encaissé = le montant signé saisi plus haut. HT et TVA
+ sont calculés depuis le taux. Si tu choisis une catégorie neutre , aucune facture
+ n'est créée (simple mouvement de trésorerie).
+
+
- Enregistrer le paiement
+ Enregistrer
@@ -114,7 +155,7 @@
—
- {% for c in categories %}{{ c.nom }} {% endfor %}
+ {% for c in categories_depense %}{{ c.nom }} {% endfor %}
@@ -137,20 +178,32 @@