"""Документы ERP WaterSurf.""" from decimal import Decimal from django.db import models from django.db.models import Sum from django.core.validators import MinValueValidator, MaxValueValidator from references.models import Currency, OrderKind, Client, Organization, Supplier, Employee, CashAccount, Product class CustomerOrder(models.Model): date = models.DateField("Дата") number = models.CharField("Номер", max_length=50) order_kind = models.ForeignKey(OrderKind, on_delete=models.PROTECT, verbose_name="Вид заказа") organization = models.ForeignKey(Organization, on_delete=models.PROTECT, verbose_name="Организация") client = models.ForeignKey(Client, on_delete=models.PROTECT, verbose_name="Клиент") total_amount = models.DecimalField("Стоимость заказа", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False) author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="customer_orders") class Meta: verbose_name = "Заказ клиента" verbose_name_plural = "Заказы клиентов" def __str__(self): return f"{self.number} от {self.date}" def recalc_total(self): self.total_amount = sum((i.price * i.quantity for i in self.items.all()), Decimal("0")) def get_economics(self): """Сводка по поступлениям и расходам по заказу: поступления, расходы (через заказы поставщику + прямые), маржа, маржинальность, рентабельность.""" total_inflows = self.cash_inflows.aggregate(s=Sum("amount"))["s"] or Decimal("0") expenses_via_supplier = CashExpense.objects.filter(supplier_order__customer_order=self).aggregate(s=Sum("amount"))["s"] or Decimal("0") expenses_direct = self.cash_expenses.aggregate(s=Sum("amount"))["s"] or Decimal("0") total_expenses = expenses_via_supplier + expenses_direct margin = total_inflows - total_expenses margin_pct = (margin / total_inflows * 100) if total_inflows else None profitability_pct = (margin / total_expenses * 100) if total_expenses else None return { "total_inflows": total_inflows, "expenses_via_supplier": expenses_via_supplier, "expenses_direct": expenses_direct, "total_expenses": total_expenses, "margin": margin, "margin_pct": margin_pct, "profitability_pct": profitability_pct, } class CustomerOrderItem(models.Model): document = models.ForeignKey(CustomerOrder, on_delete=models.CASCADE, related_name="items", verbose_name="Заказ") product = models.ForeignKey(Product, on_delete=models.PROTECT, verbose_name="Товар") price = models.DecimalField("Цена", max_digits=18, decimal_places=2, default=Decimal("0")) currency = models.ForeignKey(Currency, on_delete=models.PROTECT, verbose_name="Валюта", null=True, blank=True) quantity = models.DecimalField( "Количество", max_digits=18, decimal_places=0, default=Decimal("1"), validators=[MinValueValidator(0), MaxValueValidator(99)], ) amount = models.DecimalField("Стоимость", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False) class Meta: verbose_name = "Строка заказа клиента" verbose_name_plural = "Строки заказа клиента" def save(self, *args, **kwargs): self.amount = self.price * self.quantity super().save(*args, **kwargs) class SupplierOrder(models.Model): date = models.DateField("Дата") number = models.CharField("Номер", max_length=50) organization = models.ForeignKey(Organization, on_delete=models.PROTECT, verbose_name="Организация") supplier = models.ForeignKey(Supplier, on_delete=models.PROTECT, verbose_name="Поставщик") customer_order = models.ForeignKey(CustomerOrder, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Заказ клиента", related_name="supplier_orders") currency = models.ForeignKey(Currency, on_delete=models.PROTECT, verbose_name="Валюта") rate = models.DecimalField("Курс валюты", max_digits=18, decimal_places=6, default=Decimal("1")) total_in_currency = models.DecimalField("Стоимость в валюте", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False) total_amount = models.DecimalField("Стоимость заказа", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False) author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="supplier_orders") class Meta: verbose_name = "Заказ поставщику" verbose_name_plural = "Заказы поставщику" def __str__(self): return f"{self.number} от {self.date}" def recalc_totals(self): self.total_in_currency = sum((i.price * i.quantity for i in self.items.all()), Decimal("0")) self.total_amount = self.total_in_currency * self.rate class SupplierOrderItem(models.Model): document = models.ForeignKey(SupplierOrder, on_delete=models.CASCADE, related_name="items", verbose_name="Заказ поставщику") product = models.ForeignKey(Product, on_delete=models.PROTECT, verbose_name="Товар") price = models.DecimalField("Цена", max_digits=18, decimal_places=2, default=Decimal("0")) currency = models.ForeignKey(Currency, on_delete=models.PROTECT, verbose_name="Валюта", null=True, blank=True) quantity = models.DecimalField( "Количество", max_digits=18, decimal_places=0, default=Decimal("1"), validators=[MinValueValidator(0), MaxValueValidator(99)], ) amount = models.DecimalField("Стоимость", max_digits=18, decimal_places=2, default=Decimal("0"), editable=False) class Meta: verbose_name = "Строка заказа поставщику" verbose_name_plural = "Строки заказа поставщику" def save(self, *args, **kwargs): self.amount = self.price * self.quantity super().save(*args, **kwargs) class CashInflow(models.Model): date = models.DateField("Дата") number = models.CharField("Номер", max_length=50) recipient = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Получатель", related_name="inflows") amount = models.DecimalField("Сумма", max_digits=18, decimal_places=2, default=Decimal("0")) customer_order = models.ForeignKey(CustomerOrder, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Заказ клиента", related_name="cash_inflows") comment = models.CharField("Комментарий", max_length=500, blank=True) author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="cash_inflows") class Meta: verbose_name = "Поступление денежных средств" verbose_name_plural = "Поступления денежных средств" def __str__(self): return f"{self.number} от {self.date}" class CashTransfer(models.Model): date = models.DateField("Дата") number = models.CharField("Номер", max_length=50) sender = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Отправитель", related_name="transfers_sent") recipient = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Получатель", related_name="transfers_received") amount = models.DecimalField("Сумма", max_digits=18, decimal_places=2, default=Decimal("0")) comment = models.CharField("Комментарий", max_length=500, blank=True) author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="cash_transfers") class Meta: verbose_name = "Перемещение денежных средств" verbose_name_plural = "Перемещения денежных средств" def __str__(self): return f"{self.number} от {self.date}" class CashExpense(models.Model): date = models.DateField("Дата") number = models.CharField("Номер", max_length=50) sender = models.ForeignKey(CashAccount, on_delete=models.PROTECT, verbose_name="Отправитель", related_name="expenses") amount = models.DecimalField("Сумма", max_digits=18, decimal_places=2, default=Decimal("0")) supplier_order = models.ForeignKey(SupplierOrder, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Заказ поставщику", related_name="cash_expenses") customer_order = models.ForeignKey(CustomerOrder, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Заказ клиента", related_name="cash_expenses") comment = models.CharField("Комментарий", max_length=500, blank=True) author = models.ForeignKey(Employee, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Автор", related_name="cash_expenses") class Meta: verbose_name = "Расход денежных средств" verbose_name_plural = "Расходы денежных средств" def __str__(self): return f"{self.number} от {self.date}"