from django.db import models from django.contrib.auth.models import User class MaterialCategory(models.Model): """Категория сырья (Лист, Труба, Круг).""" name = models.CharField("Название категории", max_length=100, unique=True) gost_standard = models.CharField("ГОСТ", max_length=255, blank=True) class Meta: verbose_name = "Категория материала" verbose_name_plural = "Категории материалов" def __str__(self): return self.name class SteelGrade(models.Model): """Марка стали.""" name = models.CharField("Марка стали", max_length=100, unique=True) gost_standard = models.CharField("ГОСТ/ТУ", max_length=255, blank=True) class Meta: verbose_name = "Марка стали"; verbose_name_plural = "Марки стали" def __str__(self): return self.name class Material(models.Model): """ Справочник закупаемого сырья (Номенклатура). Логика: Это только "идея" материала, а не физический объект на полке. Для листа заполняем thickness, для трубы width (сечение), для всех length (стандартная длина). """ category = models.ForeignKey(MaterialCategory, on_delete=models.PROTECT, verbose_name="Категория") steel_grade = models.ForeignKey(SteelGrade, on_delete=models.PROTECT, null=True, blank=True) name = models.CharField("Наименование", max_length=255) thickness = models.FloatField("Толщина (S), мм", null=True, blank=True) width = models.FloatField("Ширина/Сечение (B), мм", null=True, blank=True) length = models.FloatField("Длина (L), мм", null=True, blank=True) class Meta: verbose_name = "Номенклатура (Сырье)"; verbose_name_plural = "Номенклатура (Сырье)" def __str__(self): return f"{self.category.name} {self.name} {self.steel_grade.name if self.steel_grade else ''}" class Location(models.Model): """Склады и участки (Центральный, Лазер, Сварка, СГП).""" name = models.CharField("Место хранения", max_length=100, unique=True) is_production_area = models.BooleanField("Это производственный участок", default=False) class Meta: verbose_name = "Склад/Участок"; verbose_name_plural = "Склады и участки" def __str__(self): return self.name class StockItem(models.Model): """ Универсальная физическая единица на складе. Логика Вьюх: 1. Если это сырье: заполнен material, пусто entity. 2. Если это готовая деталь: заполнен entity, пусто material. 3. Если is_remnant=True, то current_length/width показывают реальный размер куска. При списании в CuttingSession количество здесь уменьшается. Если 0 - можно удалять или скрывать. """ material = models.ForeignKey(Material, on_delete=models.PROTECT, null=True, blank=True, verbose_name="Сырье") entity = models.ForeignKey('manufacturing.ProductEntity', on_delete=models.PROTECT, null=True, blank=True, verbose_name="Произведенная сущность") location = models.ForeignKey(Location, on_delete=models.PROTECT, verbose_name="Где находится") quantity = models.FloatField("Количество (шт/м/кг)") # Для деловых остатков is_remnant = models.BooleanField("Деловой остаток", default=False) current_length = models.FloatField("Текущая длина, мм", null=True, blank=True) current_width = models.FloatField("Текущая ширина, мм", null=True, blank=True) unique_id = models.CharField("ID/Маркировка (для ДО)", max_length=50, unique=True, null=True, blank=True) class Meta: verbose_name = "Единица на складе"; verbose_name_plural = "Остатки на складах" def __str__(self): obj = self.entity if self.entity else self.material return f"{obj} | {self.quantity} ед. | {self.location}" class TransferRecord(models.Model): """ Документ перемещения (Вариант Б: строгий учет). Логика Вьюх: Создается "Отправителем" (статус sent). "Получатель" видит его в своем интерфейсе и жмет "Принять" (статус received). В этот момент у связанных StockItem меняется location на to_location. """ STATUS_CHOICES = [('sent', 'В пути'), ('received', 'Принято'), ('discrepancy', 'Расхождение')] items = models.ManyToManyField(StockItem, verbose_name="Перемещаемые объекты") from_location = models.ForeignKey(Location, related_name='outgoing', on_delete=models.PROTECT) to_location = models.ForeignKey(Location, related_name='incoming', on_delete=models.PROTECT) sender = models.ForeignKey(User, related_name='sent_transfers', on_delete=models.PROTECT) receiver = models.ForeignKey(User, related_name='received_transfers', on_delete=models.PROTECT, null=True, blank=True) status = models.CharField("Статус", max_length=20, choices=STATUS_CHOICES, default='sent') created_at = models.DateTimeField(auto_now_add=True) received_at = models.DateTimeField(null=True, blank=True) class Meta: verbose_name = "Перемещение"; verbose_name_plural = "Перемещения"