from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.conf import settings from django.db import models from django.core.exceptions import ValidationError from flower.common import ModelBase class CapacityUnitEnum(models.IntegerChoices): METER = 1, '米' YARD = 2, '码' class ProductionAssignmentStatusEnum(models.IntegerChoices): DRAFT = 1, 'Draft' PUBLISHED = 2, '已发布' ACCEPTED = 3, '已接收' CANCELLED = 4, '已取消' COMPLETED = 5, '已完工' class DeviceCategory(ModelBase): id = models.BigAutoField(primary_key=True) merchant = models.ForeignKey( 'basic_info.Merchant', on_delete=models.PROTECT, related_name='mes_device_categories', verbose_name='所属商户', ) name = models.CharField(max_length=100, verbose_name='设备分类名称') created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='created_mes_device_categories', verbose_name='创建人', ) operator = models.ForeignKey( 'basic_info.Employee', on_delete=models.PROTECT, related_name='operated_mes_device_categories', verbose_name='操作人', ) def __str__(self): return self.name def clean(self): if self.operator_id and self.merchant_id and self.operator.merchant_id != self.merchant_id: raise ValidationError({'operator': '操作人不属于当前商户'}) class Meta: verbose_name = '设备分类' verbose_name_plural = '设备分类' ordering = ['id'] constraints = [ models.UniqueConstraint(fields=['merchant', 'name'], name='unique_mes_device_category_name_per_merchant'), ] class Device(ModelBase): id = models.BigAutoField(primary_key=True) merchant = models.ForeignKey( 'basic_info.Merchant', on_delete=models.PROTECT, related_name='mes_devices', verbose_name='所属商户', ) category = models.ForeignKey( DeviceCategory, on_delete=models.PROTECT, related_name='devices', verbose_name='设备分类', ) name = models.CharField(max_length=100, verbose_name='设备名称') peak_capacity = models.PositiveIntegerField(default=100, verbose_name='峰值产能') capacity_unit = models.IntegerField( choices=CapacityUnitEnum.choices, default=CapacityUnitEnum.METER, verbose_name='产能单位', ) extra = models.JSONField(null=True, blank=True, verbose_name='扩展参数') created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='created_mes_devices', verbose_name='创建人', ) operator = models.ForeignKey( 'basic_info.Employee', on_delete=models.PROTECT, related_name='operated_mes_devices', verbose_name='操作人', ) def __str__(self): return self.name def clean(self): errors = {} if self.operator_id and self.merchant_id and self.operator.merchant_id != self.merchant_id: errors['operator'] = '操作人不属于当前商户' if self.category_id and self.merchant_id and self.category.merchant_id != self.merchant_id: errors['category'] = '设备分类不属于当前商户' if self.peak_capacity <= 0: errors['peak_capacity'] = '峰值产能必须大于0' if errors: raise ValidationError(errors) class Meta: verbose_name = '设备' verbose_name_plural = '设备' ordering = ['id'] constraints = [ models.UniqueConstraint(fields=['merchant', 'name'], name='unique_mes_device_name_per_merchant'), ] class ProductionAssignment(ModelBase): id = models.BigAutoField(primary_key=True) merchant = models.ForeignKey( 'basic_info.Merchant', on_delete=models.PROTECT, related_name='mes_production_assignments', verbose_name='所属商户', ) device = models.ForeignKey( Device, on_delete=models.PROTECT, related_name='production_assignments', verbose_name='设备', ) content_type = models.ForeignKey( ContentType, on_delete=models.PROTECT, related_name='mes_production_assignments', verbose_name='关联对象类型', ) object_id = models.PositiveBigIntegerField(db_index=True, verbose_name='关联对象ID') content_object = GenericForeignKey('content_type', 'object_id') assigner = models.ForeignKey( 'basic_info.Employee', on_delete=models.PROTECT, related_name='assigned_mes_production_assignments', verbose_name='指派者', ) assignee = models.ForeignKey( 'basic_info.Employee', on_delete=models.PROTECT, related_name='received_mes_production_assignments', null=True, blank=True, verbose_name='被指派人', ) production_quantity = models.PositiveIntegerField(verbose_name='生产数量') status = models.IntegerField( choices=ProductionAssignmentStatusEnum.choices, default=ProductionAssignmentStatusEnum.DRAFT, verbose_name='状态', ) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='created_mes_production_assignments', verbose_name='创建人', ) operator = models.ForeignKey( 'basic_info.Employee', on_delete=models.PROTECT, related_name='operated_mes_production_assignments', verbose_name='操作人', ) extra = models.JSONField(null=True, blank=True, verbose_name='扩展参数') def __str__(self): return f'ProductionAssignment #{self.id}' def clean(self): errors = {} if self.device_id and self.merchant_id and self.device.merchant_id != self.merchant_id: errors['device'] = '设备不属于当前商户' if self.assigner_id and self.merchant_id and self.assigner.merchant_id != self.merchant_id: errors['assigner'] = '指派者不属于当前商户' if self.assignee_id and self.merchant_id and self.assignee.merchant_id != self.merchant_id: errors['assignee'] = '被指派人不属于当前商户' if self.operator_id and self.merchant_id and self.operator.merchant_id != self.merchant_id: errors['operator'] = '操作人不属于当前商户' if self.production_quantity is not None and self.production_quantity <= 0: errors['production_quantity'] = '生产数量必须大于0' if self.content_type_id and self.object_id: try: content_object = self.content_type.get_object_for_this_type(pk=self.object_id) except Exception as exc: errors['object_id'] = '关联对象不存在' else: content_object_merchant_id = getattr(content_object, 'merchant_id', None) if content_object_merchant_id is not None and self.merchant_id and content_object_merchant_id != self.merchant_id: errors['object_id'] = '关联对象不属于当前商户' if errors: raise ValidationError(errors) class Meta: verbose_name = '生产指派' verbose_name_plural = '生产指派' ordering = ['-created_at', '-id'] indexes = [ models.Index(fields=['merchant', 'content_type', 'object_id']), models.Index(fields=['merchant', 'device', 'created_at']), models.Index(fields=['merchant', 'status', 'created_at']), ]