forked from erp-dev/erp
214 lines
7.7 KiB
Python
214 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',
|
|
null=True,
|
|
blank=True,
|
|
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']),
|
|
]
|
|
|