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 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 = '司机车次'