Feature: экономика заказа клиента — связи с поставщиком и расходами, отчёт по марже и рентабельности

Made-with: Cursor
This commit is contained in:
2026-02-26 14:52:08 +00:00
parent 8145db86e3
commit 8fcecb558d
9 changed files with 240 additions and 3 deletions

View File

@@ -2,6 +2,7 @@
"""Документы ERP WaterSurf."""
from decimal import Decimal
from django.db import models
from django.db.models import Sum
from references.models import Currency, OrderKind, Client, Organization, Supplier, Employee, CashAccount, Product
class CustomerOrder(models.Model):
@@ -18,6 +19,25 @@ class CustomerOrder(models.Model):
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="Товар")
@@ -38,6 +58,7 @@ class SupplierOrder(models.Model):
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)
@@ -97,6 +118,7 @@ class CashExpense(models.Model):
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: