107 lines
4.0 KiB
Python
107 lines
4.0 KiB
Python
from decimal import Decimal
|
|
|
|
from django.conf import settings
|
|
from django.core.validators import MinValueValidator, RegexValidator
|
|
from django.db import models
|
|
|
|
|
|
class ReportingEntry(models.Model):
|
|
"""Supplementary, documented facts absent from the operational sources.
|
|
|
|
Purchases and cash movements are not automatically expensed again in profit:
|
|
sales already carry their cost of goods. Corrections retain the audit trail.
|
|
"""
|
|
|
|
class Kind(models.TextChoices):
|
|
PRODUCTION = "production", "Выпуск продукции"
|
|
PURCHASE = "purchase", "Закупка для производства"
|
|
TAX = "tax", "Уплаченные налоги сотрудника"
|
|
OVERHEAD = "overhead", "Общие расходы"
|
|
CASH_IN = "cash_in", "Поступление денег"
|
|
CASH_OUT = "cash_out", "Выплата денег"
|
|
|
|
kind = models.CharField(max_length=16, choices=Kind.choices)
|
|
date = models.DateField("Дата факта", db_index=True)
|
|
reference = models.CharField("Документ / уникальный номер", max_length=200)
|
|
description = models.CharField("Описание", max_length=500, blank=True)
|
|
external_project = models.ForeignKey(
|
|
"commerce.ExternalProject", null=True, blank=True, on_delete=models.PROTECT
|
|
)
|
|
project = models.ForeignKey(
|
|
"projects.Project", null=True, blank=True, on_delete=models.PROTECT
|
|
)
|
|
product = models.ForeignKey(
|
|
"commerce.Product", null=True, blank=True, on_delete=models.PROTECT
|
|
)
|
|
employee = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT
|
|
)
|
|
quantity = models.DecimalField(
|
|
max_digits=18,
|
|
decimal_places=4,
|
|
default=0,
|
|
validators=[MinValueValidator(Decimal("0"))],
|
|
)
|
|
amount = models.DecimalField(
|
|
max_digits=18,
|
|
decimal_places=2,
|
|
default=0,
|
|
validators=[MinValueValidator(Decimal("0"))],
|
|
)
|
|
currency = models.CharField(
|
|
max_length=3, default="RUB", validators=[RegexValidator(r"^[A-Z]{3}$")]
|
|
)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.PROTECT,
|
|
related_name="reporting_entries_created",
|
|
)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
voided_at = models.DateTimeField(null=True, blank=True)
|
|
voided_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.PROTECT,
|
|
related_name="reporting_entries_voided",
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ["-date", "-pk"]
|
|
constraints = [
|
|
models.UniqueConstraint(
|
|
fields=["kind", "reference"], name="reporting_entry_reference_unique"
|
|
),
|
|
models.CheckConstraint(
|
|
condition=models.Q(amount__gte=0, quantity__gte=0),
|
|
name="reporting_entry_nonnegative",
|
|
),
|
|
models.CheckConstraint(
|
|
condition=(
|
|
models.Q(
|
|
kind="production",
|
|
product__isnull=False,
|
|
employee__isnull=True,
|
|
quantity__gt=0,
|
|
amount=0,
|
|
)
|
|
| models.Q(kind="purchase", employee__isnull=True, amount__gt=0)
|
|
| models.Q(
|
|
kind="tax",
|
|
employee__isnull=False,
|
|
product__isnull=True,
|
|
amount__gt=0,
|
|
quantity=0,
|
|
)
|
|
| models.Q(
|
|
kind__in=["overhead", "cash_in", "cash_out"],
|
|
product__isnull=True,
|
|
employee__isnull=True,
|
|
amount__gt=0,
|
|
quantity=0,
|
|
)
|
|
),
|
|
name="reporting_entry_kind_fields",
|
|
),
|
|
]
|