baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
"""客户与供应商(partner)模型。
|
||||
|
||||
设计:
|
||||
- 客户 (Customer) 与供应商 (Supplier) 共享 PriceLevel 价格等级字典
|
||||
- 联系人 (Contact) 可挂在任一客户/供应商上
|
||||
- 客户/供应商都有期初应收/应付余额、累计金额、信用额度等
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from apps.core.base_models import TenantScopedModel
|
||||
|
||||
|
||||
class PriceLevel(TenantScopedModel):
|
||||
"""价格等级(VIP1、VIP2、批发、零售 等)。"""
|
||||
|
||||
code = models.CharField(max_length=32)
|
||||
name = models.CharField(max_length=64)
|
||||
discount_rate = models.DecimalField(
|
||||
max_digits=6, decimal_places=4, default=0, help_text="默认折扣率"
|
||||
)
|
||||
sort_order = models.IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
db_table = "partner_price_level"
|
||||
unique_together = [("tenant", "code")]
|
||||
ordering = ["sort_order", "code"]
|
||||
verbose_name_plural = "价格等级"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} {self.name}"
|
||||
|
||||
|
||||
class Contact(TenantScopedModel):
|
||||
"""联系人(可挂在 customer 或 supplier 上)。"""
|
||||
|
||||
name = models.CharField(max_length=64)
|
||||
phone = models.CharField(max_length=32, blank=True, default="")
|
||||
email = models.EmailField(blank=True, default="")
|
||||
title = models.CharField(max_length=64, blank=True, default="", help_text="职位/称呼")
|
||||
customer = models.ForeignKey(
|
||||
"Customer",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="contacts",
|
||||
)
|
||||
supplier = models.ForeignKey(
|
||||
"Supplier",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="contacts",
|
||||
)
|
||||
is_primary = models.BooleanField(default=False, help_text="是否主联系人")
|
||||
|
||||
class Meta:
|
||||
db_table = "partner_contact"
|
||||
verbose_name_plural = "联系人"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.phone})"
|
||||
|
||||
|
||||
class Customer(TenantScopedModel):
|
||||
"""客户。"""
|
||||
|
||||
KIND_CHOICES = [
|
||||
("individual", "个人"),
|
||||
("company", "企业"),
|
||||
]
|
||||
|
||||
code = models.CharField(max_length=64, help_text="客户编码(租户内唯一)")
|
||||
name = models.CharField(max_length=128)
|
||||
kind = models.CharField(max_length=16, choices=KIND_CHOICES, default="company")
|
||||
contact_name = models.CharField(max_length=64, blank=True, default="")
|
||||
phone = models.CharField(max_length=32, blank=True, default="")
|
||||
email = models.EmailField(blank=True, default="")
|
||||
address = models.CharField(max_length=255, blank=True, default="")
|
||||
tax_number = models.CharField(max_length=64, blank=True, default="", help_text="税号")
|
||||
bank_account = models.CharField(max_length=64, blank=True, default="")
|
||||
|
||||
price_level = models.ForeignKey(
|
||||
PriceLevel,
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="customers",
|
||||
)
|
||||
credit_limit = models.DecimalField(
|
||||
max_digits=18, decimal_places=4, default=0, help_text="信用额度"
|
||||
)
|
||||
initial_receivable = models.DecimalField(
|
||||
max_digits=18, decimal_places=4, default=0, help_text="期初应收"
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(default=True)
|
||||
remark = models.TextField(blank=True, default="")
|
||||
|
||||
class Meta:
|
||||
db_table = "partner_customer"
|
||||
unique_together = [("tenant", "code")]
|
||||
verbose_name_plural = "客户"
|
||||
indexes = [
|
||||
models.Index(fields=["tenant", "code"]),
|
||||
models.Index(fields=["tenant", "name"]),
|
||||
models.Index(fields=["tenant", "phone"]),
|
||||
]
|
||||
ordering = ["code"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} {self.name}"
|
||||
|
||||
|
||||
class Supplier(TenantScopedModel):
|
||||
"""供应商。"""
|
||||
|
||||
code = models.CharField(max_length=64)
|
||||
name = models.CharField(max_length=128)
|
||||
contact_name = models.CharField(max_length=64, blank=True, default="")
|
||||
phone = models.CharField(max_length=32, blank=True, default="")
|
||||
email = models.EmailField(blank=True, default="")
|
||||
address = models.CharField(max_length=255, blank=True, default="")
|
||||
tax_number = models.CharField(max_length=64, blank=True, default="")
|
||||
bank_account = models.CharField(max_length=64, blank=True, default="")
|
||||
|
||||
initial_payable = models.DecimalField(
|
||||
max_digits=18, decimal_places=4, default=0, help_text="期初应付"
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(default=True)
|
||||
remark = models.TextField(blank=True, default="")
|
||||
|
||||
class Meta:
|
||||
db_table = "partner_supplier"
|
||||
unique_together = [("tenant", "code")]
|
||||
verbose_name_plural = "供应商"
|
||||
indexes = [
|
||||
models.Index(fields=["tenant", "code"]),
|
||||
models.Index(fields=["tenant", "name"]),
|
||||
]
|
||||
ordering = ["code"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} {self.name}"
|
||||
|
||||
|
||||
class CustomerProductPrice(TenantScopedModel):
|
||||
"""客户专属价格本(客户 × 商品 → 成交价,取价优先级最高)。"""
|
||||
|
||||
customer = models.ForeignKey(
|
||||
Customer, on_delete=models.CASCADE, related_name="product_prices"
|
||||
)
|
||||
product = models.ForeignKey(
|
||||
"catalog.Product", on_delete=models.CASCADE, related_name="customer_prices"
|
||||
)
|
||||
price = models.DecimalField(max_digits=18, decimal_places=4)
|
||||
|
||||
class Meta:
|
||||
db_table = "partner_customer_product_price"
|
||||
unique_together = [("tenant", "customer", "product")]
|
||||
verbose_name_plural = "客户专属价"
|
||||
ordering = ["customer", "product"]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.customer.code}/{self.product.code} = {self.price}"
|
||||
Reference in New Issue
Block a user