1
0
forked from erp-dev/erp
Files
erpnew/basic_info/models.py

452 lines
16 KiB
Python
Raw 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.
from decimal import Decimal
from django.db import models
from flower.common import ModelBase
from django.contrib.auth.models import User
# ==================== 常量定义 ====================
METER_TO_YARD_RATE = 0.9144
YARD_TO_METER_RATE = 1.09361
# ==================== 枚举类定义 ====================
class WareHouseModeEnum(models.IntegerChoices):
"""仓库出入库模式枚举"""
RESTRICT_IN = 1, '严进宽出'
RESTRICT_IN_OUT = 2, '严进严出'
UNRESTRICTED = 3, '宽进宽出'
class WarehouseTypeEnum(models.IntegerChoices):
"""仓库类别枚举"""
WHOLE = 1, '整仓'
SCATTERED = 2, '散仓'
class MerchantTypeEnum(models.IntegerChoices):
"""商户类型枚举"""
STORE = 1, '布行'
FACTORY = 2, '印花厂'
class ProductUnitEnum(models.IntegerChoices):
"""产品单位枚举"""
METER = 1, ''
YARD = 2, ''
KG = 3, '公斤'
class EmployeeTypeEnum(models.TextChoices):
"""员工职位类型枚举"""
PRINTER = '打纸', '打纸'
ROLLING = '滚筒', '滚筒'
WARE = '仓库', '仓库'
DELIVERY = '送货', '送货'
class EmployeeStatusEnum(models.TextChoices):
"""员工状态枚举"""
ACTIVE = '在职', '在职'
INACTIVE = '离职', '离职'
class DeviceStatusEnum(models.TextChoices):
"""设备状态枚举"""
ACTIVE = '使用中', '使用中'
INACTIVE = '停用', '停用'
MAINTENANCE = '维修中', '维修中'
class DeviceTypeEnum(models.TextChoices):
"""设备类型枚举"""
ROLLING = '滚筒', '滚筒'
PRINTER = '打印', '打印'
# ==================== 模型定义 ====================
class EmployeeType(ModelBase):
"""员工职位类型"""
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='employee_types', verbose_name='所属商户')
title = models.CharField(max_length=50, verbose_name='职位名称')
description = models.TextField(blank=True, null=True, verbose_name='职位描述')
reverse = models.CharField(max_length=100, blank=True, null=True, verbose_name='预留字段')
def __str__(self):
return self.title
class Meta:
verbose_name = '员工职位类型'
verbose_name_plural = '员工职位类型'
unique_together = ('merchant', 'title')
class QuickInput(ModelBase):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=100, verbose_name='')
value = models.CharField(max_length=200, verbose_name='')
group = models.CharField(verbose_name='')
def __str__(self):
return f'{self.group}-{self.name}'
class Meta:
verbose_name = '快捷输入'
verbose_name_plural = '快捷输入'
unique_together = ('name', 'group')
class Merchant(ModelBase):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=100, verbose_name='商户名称')
type = models.IntegerField(choices=MerchantTypeEnum.choices, verbose_name='商户类型')
area = models.CharField(max_length=100, blank=True, null=True, verbose_name='地区')
email = models.EmailField(blank=True, null=True, verbose_name='电子邮箱')
mobile = models.CharField(max_length=20, blank=True, null=True, verbose_name='手机')
contact = models.TextField(blank=True, null=True, verbose_name='联系人')
auto_complete_stock_change = models.BooleanField(
default=False,
verbose_name='自动确认库存变动',
)
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return self.name
class Meta:
verbose_name = '商户资料'
verbose_name_plural = '商户资料'
class VehicleType(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='vehicle_types', verbose_name='所属商户')
name = models.CharField(max_length=50, verbose_name='车辆类型名称')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return self.name
class Meta:
verbose_name = '车辆类型'
verbose_name_plural = '车辆类型'
class WareHouse(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='warehouses', verbose_name='所属商户')
name = models.CharField(max_length=100, verbose_name='仓库名称')
location = models.CharField(max_length=200, blank=True, null=True, verbose_name='仓库地址')
type = models.IntegerField(
choices=WarehouseTypeEnum.choices,
default=WarehouseTypeEnum.WHOLE,
verbose_name='仓库类别',
)
mode = models.IntegerField(
choices=WareHouseModeEnum.choices,
default=WareHouseModeEnum.RESTRICT_IN,
verbose_name='出入库模式',
)
contact = models.CharField(max_length=100, blank=True, null=True, verbose_name='联系人')
mobile = models.CharField(max_length=20, blank=True, null=True, verbose_name='手机')
area = models.CharField(max_length=100, blank=True, null=True, verbose_name='地区')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return self.name
class Meta:
verbose_name = '仓库资料'
verbose_name_plural = '仓库资料'
class Supplier(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='suppliers', verbose_name='所属商户')
name = models.CharField(max_length=100, verbose_name='供应商名称')
area = models.CharField(max_length=100, blank=True, null=True, verbose_name='地区')
email = models.EmailField(blank=True, null=True, verbose_name='电子邮箱')
mobile = models.CharField(max_length=20, blank=True, null=True, verbose_name='手机')
contact = models.TextField(blank=True, null=True, verbose_name='联系人')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return self.name
class Meta:
verbose_name = '供应商'
verbose_name_plural = '供应商'
class BankAccount(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='bank_accounts', verbose_name='所属商户')
name = models.CharField(max_length=100, verbose_name='名称')
auto_number = models.CharField(max_length=50, verbose_name='自动编号')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
attachment = models.FileField(upload_to='bank_accounts/', blank=True, null=True, verbose_name='附件')
def __str__(self):
return f'{self.name} - {self.auto_number}'
class Meta:
verbose_name = '银行账户'
verbose_name_plural = '银行账户'
class ProductCategory(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='product_categories', verbose_name='所属商户')
name = models.CharField(max_length=100, verbose_name='类别名称')
description = models.TextField(blank=True, null=True, verbose_name='类别描述')
product_prefix = models.CharField(max_length=5, verbose_name='产品编号前缀')
def __str__(self):
return self.name
class Meta:
verbose_name = '产品类别'
verbose_name_plural = '产品类别'
class Product(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, verbose_name='所属商户')
category = models.ForeignKey(
ProductCategory,
on_delete=models.PROTECT,
related_name='products',
verbose_name='产品类别',
)
name = models.CharField(max_length=100, verbose_name='产品名')
human_id = models.CharField(max_length=100, verbose_name='产品编号', blank=True)
color = models.CharField(max_length=50, blank=True, null=True, verbose_name='颜色')
width_size = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name='宽幅')
single_price_in = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name='参考进价',
blank=True,
null=True,
)
single_price_out = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name='参考售价',
blank=True,
null=True,
)
unit = models.IntegerField(
choices=ProductUnitEnum.choices,
default=ProductUnitEnum.METER,
verbose_name='单位',
)
spec = models.CharField(max_length=100, blank=True, null=True, verbose_name='规格')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
image = models.ImageField(
upload_to='product_images/',
blank=True,
null=True,
verbose_name='产品图片',
)
minimum_quantity = models.IntegerField(null=True, blank=True, verbose_name='最低库存量')
transform_rate = models.DecimalField(
max_digits=10,
decimal_places=4,
blank=True,
null=True,
verbose_name='公斤转换率',
)
def __str__(self):
return self.name
def save(
self,
*args,
force_insert=False,
force_update=False,
using=None,
update_fields=None,
):
super().save(
*args,
force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields,
)
# Generate human_id if not set after saving to get the ID
if not self.human_id and self.category:
self.human_id = f'{self.category.product_prefix}{self.id:05d}'
super().save(
*args,
force_insert=False,
force_update=False,
using=using,
update_fields=['human_id'],
)
class Meta:
verbose_name = '产品资料'
verbose_name_plural = '产品资料'
class Customer(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='customers', verbose_name='所属商户')
name = models.CharField(max_length=100, verbose_name='客户名称')
visible_employees = models.ManyToManyField(
'Employee',
blank=True,
related_name='visible_customers',
verbose_name='可见该客户的员工',
)
created_by = models.ForeignKey(
'Employee',
on_delete=models.PROTECT,
related_name='created_customers',
null=True,
verbose_name='创建者',
)
mobile = models.CharField(max_length=20, verbose_name='手机')
area = models.CharField(max_length=100, blank=True, null=True, verbose_name='地区')
email = models.EmailField(blank=True, null=True, verbose_name='电子邮箱')
contact = models.CharField(blank=True, null=True, verbose_name='联系人')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return self.name
class Meta:
verbose_name = '客户资料'
verbose_name_plural = '客户资料'
class Employee(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='employees', verbose_name='所属商户')
sys_user = models.OneToOneField(
User,
on_delete=models.PROTECT,
related_name='employee',
null=True,
blank=True,
verbose_name='系统用户',
)
name = models.CharField(max_length=100, verbose_name='员工姓名')
position = models.ForeignKey(
'EmployeeType',
on_delete=models.PROTECT,
related_name='employees',
null=True,
blank=True,
verbose_name='职位',
)
mobile = models.CharField(max_length=20, blank=True, null=True, verbose_name='手机')
area = models.CharField(max_length=100, blank=True, null=True, verbose_name='地区')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
status = models.CharField(
max_length=20,
choices=EmployeeStatusEnum.choices,
default=EmployeeStatusEnum.ACTIVE,
verbose_name='员工状态',
)
@property
def job_type(self) -> str:
"""
返回职位名称(向后兼容)
如果有关联的职位类型,返回其 title否则返回空字符串
"""
if self.position:
return self.position.title
return ''
def __str__(self):
return self.name
class Meta:
verbose_name = '员工资料'
verbose_name_plural = '员工资料'
class UserProfile(ModelBase):
id = models.BigAutoField(primary_key=True)
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
related_name='profile',
verbose_name='关联用户',
)
merchant = models.ForeignKey(
'Merchant',
on_delete=models.PROTECT,
related_name='user_profiles',
verbose_name='所属商户',
)
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return f"{self.user.username}'s Profile"
class Meta:
verbose_name = '用户资料扩展'
verbose_name_plural = '用户资料扩展'
class DeviceInfo(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='devices', verbose_name='所属商户')
name = models.CharField(max_length=100, verbose_name='设备名称')
type = models.CharField(
max_length=20,
choices=DeviceTypeEnum.choices,
default=DeviceTypeEnum.ROLLING,
verbose_name='设备类型',
)
status = models.CharField(
max_length=20,
choices=DeviceStatusEnum.choices,
default=DeviceStatusEnum.ACTIVE,
verbose_name='设备状态',
)
start_working_at = models.DateField(blank=True, null=True, verbose_name='开始服役日期')
stop_working_at = models.DateField(blank=True, null=True, verbose_name='停止服役日期')
is_occupied = models.BooleanField(default=False, verbose_name='是否被占用')
description = models.TextField(blank=True, null=True, verbose_name='备注描述')
def __str__(self):
return self.name
class Meta:
verbose_name = '设备资料'
verbose_name_plural = '设备资料'
class VehicleTransportRecord(ModelBase):
id = models.BigAutoField(primary_key=True)
merchant = models.ForeignKey('Merchant', on_delete=models.PROTECT, related_name='vehicle_transport_records', verbose_name='所属商户')
vehicle_type = models.ForeignKey(
VehicleType,
on_delete=models.PROTECT,
related_name='vehicles',
verbose_name='车次',
)
driver_name = models.CharField(max_length=100, verbose_name='司机姓名')
contact_number = models.CharField(max_length=20, blank=True, null=True, verbose_name='联系电话')
license_plate = models.CharField(max_length=20, verbose_name='车牌号码', null=True, blank=True)
delivery_date = models.DateField(verbose_name='送货日期')
def __str__(self):
return f'{self.vehicle_type}-{self.driver_name}-{self.delivery_date}'
class Meta:
verbose_name = '司机车次'
verbose_name_plural = '司机车次'