Files
watersurf-erp/app/documents/models.py

149 lines
9.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Документы 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, OrderStatus
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="Клиент")
status = models.ForeignKey(
OrderStatus,
on_delete=models.PROTECT,
verbose_name="Статус заказа",
null=True,
blank=True,
related_name="customer_orders",
)
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}"