All checks were successful
Deploy MES Core / deploy (push) Successful in 11s
98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
from django.db import models
|
|
|
|
|
|
class RouteStub(models.Model):
|
|
"""Маршрут (пока заглушка под техпроцессы)."""
|
|
|
|
name = models.CharField("Маршрут", max_length=200, unique=True)
|
|
|
|
class Meta:
|
|
verbose_name = "Маршрут"
|
|
verbose_name_plural = "Маршруты"
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class ProductEntity(models.Model):
|
|
"""Паспорт детали/сборки/изделия (КД).
|
|
|
|
planned_material:
|
|
- материал, заложенный в КД (для расчёта потребности и контроля замен при раскрое).
|
|
|
|
Нормы расхода (для BOM Explosion и MaterialRequirement):
|
|
- для листовой детали: blank_area_m2 (м² на 1 шт)
|
|
- для линейной (профиль/труба/круг): blank_length_mm (мм на 1 шт)
|
|
|
|
Примечание:
|
|
- категорию типа (лист/профиль) определяем по planned_material.category.
|
|
"""
|
|
|
|
ENTITY_TYPE = [
|
|
('product', 'Готовое изделие'),
|
|
('assembly', 'Сборочная единица'),
|
|
('part', 'Деталь'),
|
|
]
|
|
|
|
name = models.CharField("Наименование", max_length=255)
|
|
drawing_number = models.CharField("Обозначение/Чертёж", max_length=100, blank=True, default="")
|
|
entity_type = models.CharField("Тип", max_length=15, choices=ENTITY_TYPE, default='part')
|
|
|
|
planned_material = models.ForeignKey(
|
|
'warehouse.Material',
|
|
on_delete=models.PROTECT,
|
|
null=True,
|
|
blank=True,
|
|
verbose_name="Заложенный материал",
|
|
)
|
|
route = models.ForeignKey(
|
|
RouteStub,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
verbose_name="Маршрут",
|
|
)
|
|
|
|
blank_area_m2 = models.FloatField("Норма: площадь заготовки (м²/шт)", null=True, blank=True)
|
|
blank_length_mm = models.FloatField("Норма: длина заготовки (мм/шт)", null=True, blank=True)
|
|
|
|
dxf_file = models.FileField("Исходник (DXF/IGES/STEP)", upload_to="drawings/%Y/%m/", blank=True, null=True)
|
|
pdf_main = models.FileField("Чертёж (PDF)", upload_to="drawings_pdf/%Y/%m/", blank=True, null=True)
|
|
preview = models.ImageField("Превью", upload_to="previews/%Y/%m/", blank=True, null=True)
|
|
|
|
class Meta:
|
|
verbose_name = "КД (изделие/деталь)"
|
|
verbose_name_plural = "КД (изделия/детали)"
|
|
|
|
def __str__(self):
|
|
base = f"{self.drawing_number} {self.name}".strip()
|
|
return base if base else self.name
|
|
|
|
|
|
class BOM(models.Model):
|
|
"""Спецификация (BOM): parent состоит из child в количестве quantity."""
|
|
|
|
parent = models.ForeignKey(
|
|
ProductEntity,
|
|
related_name='components',
|
|
on_delete=models.CASCADE,
|
|
verbose_name="Куда входит (сборка)",
|
|
)
|
|
child = models.ForeignKey(
|
|
ProductEntity,
|
|
related_name='used_in',
|
|
on_delete=models.CASCADE,
|
|
verbose_name="Что входит (деталь)",
|
|
)
|
|
quantity = models.PositiveIntegerField("Кол-во в сборке", default=1)
|
|
|
|
class Meta:
|
|
unique_together = ('parent', 'child')
|
|
verbose_name = "Спецификация (BOM)"
|
|
verbose_name_plural = "Спецификации (BOM)"
|
|
|
|
def __str__(self):
|
|
return f"{self.parent} -> {self.child} x{self.quantity}"
|
|
|
|
# Create your models here.
|