from django.db import models from django.utils import timezone from django.contrib.auth.models import User # --- СПРАВОЧНИКИ --- class Company(models.Model): name = models.CharField("Название компании", max_length=255, unique=True) def __str__(self): return self.name class Machine(models.Model): name = models.CharField("Название станка", max_length=100) def __str__(self): return self.name class EmployeeProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') role = models.CharField("Должность", max_length=20, default='operator') machines = models.ManyToManyField(Machine, blank=True) def __str__(self): return self.user.username # --- КОНТУР ЗАКАЗОВ (СДЕЛКИ И ПОТРЕБНОСТИ) --- class Deal(models.Model): """Сделка. Контейнер заказа клиента.""" number = models.CharField("№ Сделки", max_length=100, unique=True) company = models.ForeignKey(Company, on_delete=models.PROTECT, null=True, blank=True) status = models.CharField("Статус", max_length=20, default='lead') def __str__(self): return f"Сделка №{self.number}" class DealItem(models.Model): """ Что заказал клиент (точка входа MRP). Логика Вьюх: Менеджер вносит сюда 5 шт "Лавок". На основе этого генерируются MaterialRequirement и ProductionTask. """ deal = models.ForeignKey(Deal, related_name='items', on_delete=models.CASCADE) entity = models.ForeignKey('manufacturing.ProductEntity', on_delete=models.PROTECT, verbose_name="Изделие") quantity = models.PositiveIntegerField("Заказано, шт") class MaterialRequirement(models.Model): """ Потребность в закупке сырья для Сделки. Логика Вьюх: Генерируется автоматически после "взрыва" спецификации (BOM). Снабженец видит это в своем АРМ и организует приход. """ STATUS = [('needed', 'К закупке'), ('ordered', 'В пути'), ('fulfilled', 'Обеспечено')] deal = models.ForeignKey(Deal, on_delete=models.CASCADE) material = models.ForeignKey('warehouse.Material', on_delete=models.PROTECT) required_qty = models.FloatField("Нужно докупить (шт/м/кг)") status = models.CharField(max_length=20, choices=STATUS, default='needed') # --- ПРОИЗВОДСТВЕННЫЙ КОНТУР --- class ProductionTask(models.Model): """ Сменное задание (План на производство детали). Логика: Связывает Сделку и КД (ProductEntity). """ deal = models.ForeignKey(Deal, on_delete=models.CASCADE, verbose_name="Сделка") entity = models.ForeignKey('manufacturing.ProductEntity', on_delete=models.CASCADE, verbose_name="Что делать") quantity_ordered = models.PositiveIntegerField("План, шт") created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.entity.name} (Заказ {self.deal.number})" class CuttingSession(models.Model): """ Сессия переработки (Основа для списания/начисления). Логика Вьюх: Оператор создает сессию. Указывает, какой StockItem (Лист/Хлыст) он взял со своего участка. В рамках сессии он закрывает пункты (ShiftItem). При закрытии сессии Вьюха: 1) списывает used_stock_item, 2) начисляет новые StockItem с готовыми деталями, 3) начисляет ДО. """ operator = models.ForeignKey(User, on_delete=models.PROTECT) machine = models.ForeignKey(Machine, on_delete=models.PROTECT) used_stock_item = models.ForeignKey('warehouse.StockItem', on_delete=models.PROTECT, verbose_name="Взятый со склада материал") date = models.DateField("Дата", default=timezone.localdate) created_at = models.DateTimeField(auto_now_add=True) is_closed = models.BooleanField("Сессия закрыта", default=False) class ShiftItem(models.Model): """ Конкретный пункт отчета в рамках сессии. (Замена старой модели Item). """ session = models.ForeignKey(CuttingSession, related_name='tasks', on_delete=models.CASCADE) task = models.ForeignKey(ProductionTask, on_delete=models.PROTECT, verbose_name="Плановое задание") quantity_fact = models.PositiveIntegerField("Изготовлено (Факт), шт", default=0) # Флаг для контроля отклонений (если взяли Ст3 вместо Ст10) material_substitution = models.BooleanField("Замена материала по факту", default=False)