1
0
forked from erp-dev/erp
Files
erpnew/mes/models.py
2026-05-11 21:37:41 +08:00

212 lines
7.7 KiB
Python

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']),
]