153 lines
5.0 KiB
Python
153 lines
5.0 KiB
Python
"""销售模型:SalesOrder(销售订单)+ SalesBill(销售单/出库单)。"""
|
|
|
|
from decimal import Decimal
|
|
|
|
from django.db import models
|
|
|
|
from apps.core.base_models import TenantScopedModel
|
|
|
|
|
|
class SalesOrder(TenantScopedModel):
|
|
"""销售订单(可选,预先订货流程)。"""
|
|
|
|
STATE_CHOICES = [
|
|
("draft", "草稿"),
|
|
("confirmed", "已确认"),
|
|
("delivered", "已发货"),
|
|
("cancelled", "已取消"),
|
|
]
|
|
|
|
bill_no = models.CharField(max_length=64)
|
|
customer = models.ForeignKey(
|
|
"partner.Customer",
|
|
on_delete=models.PROTECT,
|
|
related_name="sales_orders",
|
|
)
|
|
warehouse = models.ForeignKey(
|
|
"inventory.Warehouse",
|
|
on_delete=models.PROTECT,
|
|
related_name="sales_orders",
|
|
)
|
|
bill_date = models.DateField()
|
|
expected_date = models.DateField(null=True, blank=True)
|
|
total_amount = models.DecimalField(max_digits=18, decimal_places=4, default=0)
|
|
state = models.CharField(max_length=16, choices=STATE_CHOICES, default="draft")
|
|
remark = models.TextField(blank=True, default="")
|
|
|
|
class Meta:
|
|
db_table = "sales_order"
|
|
unique_together = [("tenant", "bill_no")]
|
|
ordering = ["-bill_date", "-id"]
|
|
verbose_name_plural = "销售订单"
|
|
|
|
|
|
class SalesOrderLine(TenantScopedModel):
|
|
order = models.ForeignKey(
|
|
SalesOrder, on_delete=models.CASCADE, related_name="lines"
|
|
)
|
|
product = models.ForeignKey(
|
|
"catalog.Product", on_delete=models.PROTECT, related_name="so_lines"
|
|
)
|
|
source_unit = models.ForeignKey(
|
|
"catalog.Unit",
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name="%(class)s_lines",
|
|
help_text="录入单位(空=按基本单位)",
|
|
)
|
|
source_quantity = models.DecimalField(
|
|
max_digits=18, decimal_places=4, null=True, blank=True,
|
|
help_text="录入数量(按录入单位;quantity 恒为基本单位数量)",
|
|
)
|
|
|
|
quantity = models.DecimalField(max_digits=18, decimal_places=4)
|
|
unit_price = models.DecimalField(max_digits=18, decimal_places=4)
|
|
amount = models.DecimalField(max_digits=18, decimal_places=4, default=0)
|
|
delivered_qty = models.DecimalField(max_digits=18, decimal_places=4, default=0)
|
|
|
|
class Meta:
|
|
db_table = "sales_order_line"
|
|
ordering = ["id"]
|
|
|
|
|
|
class SalesBill(TenantScopedModel):
|
|
"""销售单(实际出库时生成)。"""
|
|
|
|
STATE_CHOICES = [
|
|
("draft", "草稿"),
|
|
("confirmed", "已过账"),
|
|
("cancelled", "已取消"),
|
|
]
|
|
|
|
bill_no = models.CharField(max_length=64)
|
|
customer = models.ForeignKey(
|
|
"partner.Customer",
|
|
on_delete=models.PROTECT,
|
|
related_name="sales_bills",
|
|
)
|
|
warehouse = models.ForeignKey(
|
|
"inventory.Warehouse",
|
|
on_delete=models.PROTECT,
|
|
related_name="sales_bills",
|
|
)
|
|
order = models.ForeignKey(
|
|
SalesOrder,
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name="bills",
|
|
)
|
|
bill_date = models.DateField()
|
|
total_amount = models.DecimalField(max_digits=18, decimal_places=4, default=0)
|
|
round_off = models.DecimalField(
|
|
max_digits=18, decimal_places=4, default=0, help_text="抹零金额(行合计-净额)"
|
|
)
|
|
state = models.CharField(max_length=16, choices=STATE_CHOICES, default="draft")
|
|
remark = models.TextField(blank=True, default="")
|
|
|
|
class Meta:
|
|
db_table = "sales_bill"
|
|
unique_together = [("tenant", "bill_no")]
|
|
ordering = ["-bill_date", "-id"]
|
|
verbose_name_plural = "销售单"
|
|
|
|
|
|
class SalesBillLine(TenantScopedModel):
|
|
bill = models.ForeignKey(
|
|
SalesBill, on_delete=models.CASCADE, related_name="lines"
|
|
)
|
|
product = models.ForeignKey(
|
|
"catalog.Product", on_delete=models.PROTECT, related_name="sb_lines"
|
|
)
|
|
source_unit = models.ForeignKey(
|
|
"catalog.Unit",
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL,
|
|
related_name="%(class)s_lines",
|
|
help_text="录入单位(空=按基本单位)",
|
|
)
|
|
source_quantity = models.DecimalField(
|
|
max_digits=18, decimal_places=4, null=True, blank=True,
|
|
help_text="录入数量(按录入单位;quantity 恒为基本单位数量)",
|
|
)
|
|
|
|
quantity = models.DecimalField(max_digits=18, decimal_places=4)
|
|
unit_price = models.DecimalField(max_digits=18, decimal_places=4)
|
|
amount = models.DecimalField(max_digits=18, decimal_places=4, default=0)
|
|
|
|
# 税率(D4):行级快照,避免事后改商品税率影响历史单据
|
|
tax_rate = models.DecimalField(
|
|
max_digits=6, decimal_places=4, default=0,
|
|
help_text="行税率(0.13 = 13%);价内口径:amount 为含税额",
|
|
)
|
|
tax_amount = models.DecimalField(
|
|
max_digits=18, decimal_places=4, default=0,
|
|
help_text="行税额(从含税额中拆出)",
|
|
)
|
|
|
|
class Meta:
|
|
db_table = "sales_bill_line"
|
|
ordering = ["id"]
|