Files

166 lines
5.5 KiB
Python

"""客户与供应商(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}"