Catégories globales + flag neutre + seed_reference

This commit is contained in:
2026-06-30 00:43:18 +02:00
parent 0398e45f2f
commit 54f9fc56f4
29 changed files with 210 additions and 56 deletions
+46 -3
View File
@@ -31,6 +31,36 @@ class RegimeTva(models.TextChoices):
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"),
]
nom = models.CharField(max_length=100, unique=True)
usage = models.CharField(max_length=10, choices=USAGE, default="depense")
exclure_calculs = models.BooleanField(
"Neutre (exclue des calculs)", default=False,
help_text="Cochez pour les mouvements non fiscaux : TVA payée, "
"cotisations URSSAF, remboursement de TVA, virements…")
actif = models.BooleanField(default=True)
ordre = models.IntegerField(default=0)
class Meta:
verbose_name = "Catégorie"
verbose_name_plural = "Catégories"
ordering = ["usage", "ordre", "nom"]
def __str__(self):
return self.nom
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)
@@ -59,6 +89,7 @@ class Bareme(models.Model):
class Entreprise(models.Model):
VUE_TVA = [("simplifie", "Résumé simplifié"), ("ca3", "Champs CA3 officiels")]
nom = models.CharField(max_length=200)
siret = models.CharField(max_length=20, blank=True)
activite = models.CharField(
@@ -77,6 +108,9 @@ class Entreprise(models.Model):
"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")
email = models.EmailField(blank=True)
adresse = models.TextField(blank=True)
cree_le = models.DateTimeField(auto_now_add=True)
@@ -225,7 +259,8 @@ class Depense(models.Model):
activite = models.CharField(
max_length=20, choices=Activite.choices, blank=True,
help_text="Activité concernée (facultatif, pour la ventilation).")
categorie = models.CharField(max_length=100, blank=True)
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,
@@ -251,6 +286,11 @@ class Depense(models.Model):
def __str__(self):
return f"{self.date}{self.libelle}"
@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)."""
@@ -266,14 +306,17 @@ class Depense(models.Model):
@property
def tva_autoliquidee(self):
"""TVA française auto-liquidée (comptée en collectée ET en déductible)."""
if self.autoliquidee:
if self.autoliquidee and not self.neutre:
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)."""
"""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:
return self.tva_autoliquidee
return self.montant_tva