from django.db import models from django.utils import timezone from django.contrib.auth.models import User from warehouse.models import Material as WarehouseMaterial class Company(models.Model): """ Справочник контрагентов/заказчиков. Позволяет группировать сделки по компаниям и избегать дублей в названиях. """ name = models.CharField("Название компании", max_length=255, unique=True) description = models.TextField("Краткое описание / Примечание", blank=True) def __str__(self): return self.name class Meta: verbose_name = "Компания"; verbose_name_plural = "Компании" class Machine(models.Model): """ Список производственных участков (станков). Используется для фильтрации сменных заданий для конкретных операторов. """ name = models.CharField("Название станка", max_length=100) def __str__(self): return self.name class Meta: verbose_name = "Станок"; verbose_name_plural = "Станки" class Deal(models.Model): """ Заказ или проект. Номер парсится из пути к файлам. Служит контейнером для группы деталей (позиций). """ number = models.CharField("№ Сделки", max_length=100, unique=True) company = models.ForeignKey(Company, on_delete=models.PROTECT, verbose_name="Заказчик", null=True, blank=True) description = models.TextField("Описание сделки", blank=True, help_text="Общая информация по заказу") def __str__(self): return f"Сделка №{self.number} ({self.company})" class Meta: verbose_name = "Сделка"; verbose_name_plural = "Сделки" class ProductionTask(models.Model): """ Основание для производства. Определяет ЧТО делать. Создается технологом или мастером на основе заказа. """ deal = models.ForeignKey(Deal, on_delete=models.CASCADE, verbose_name="Сделка") drawing_name = models.CharField("Название детали", max_length=255, blank=True, default="Б/ч") size_value = models.FloatField("Размер детали", help_text="Длина (мм) или Толщина (мм)") drawing_file = models.FileField("Исходник (DXF/IGES)", upload_to="drawings/%Y/%m/", blank=True, null=True) extra_drawing = models.FileField("Доп. чертеж (PDF)", upload_to="extra_drawings/%Y/%m/", blank=True, null=True) material = models.ForeignKey(WarehouseMaterial, on_delete=models.PROTECT, verbose_name="Материал") quantity_ordered = models.PositiveIntegerField("Заказано всего, шт") is_bend = models.BooleanField("Гибка", default=False) created_at = models.DateTimeField("Дата создания", auto_now_add=True) class Meta: verbose_name = "Задание на деталь"; verbose_name_plural = "Задания на детали" ordering = ['-created_at'] def __str__(self): return f"{self.drawing_name} (Заказ {self.deal.number})" class Item(models.Model): """ Единица сменного задания. Определяет КТО, КОГДА и СКОЛЬКО сделал. """ STATUS_CHOICES = [ ('work', 'В работе'), ('done', 'Выполнено'), ('partial', 'Частично'), ('leftover', 'Недодел'), ('imported', 'Импортировано'), ] # --- Ссылка на основу (временно null=True для миграции старых данных) --- task = models.ForeignKey(ProductionTask, on_delete=models.CASCADE, related_name='items', verbose_name="Задание", null=True, blank=True) # --- Смена (заполняет мастер) --- date = models.DateField("Дата смены", default=timezone.now) machine = models.ForeignKey(Machine, on_delete=models.PROTECT, verbose_name="Станок") quantity_plan = models.PositiveIntegerField("План на смену, шт") # --- Исполнение (заполняет оператор) --- quantity_fact = models.PositiveIntegerField("Факт, шт", default=0) material_taken = models.TextField("Взятый материал", blank=True, help_text="Напр: 3 трубы по 12м") usable_waste = models.TextField("Деловой отход", blank=True, help_text="Напр: кусок 1500мм") scrap_weight = models.FloatField("Лом (кг)", default=0.0) # --- Статусы и учет --- status = models.CharField("Статус", max_length=10, choices=STATUS_CHOICES, default='work') is_synced_1c = models.BooleanField("Учтено в 1С", default=False) class Meta: verbose_name = "Пункт сменки"; verbose_name_plural = "Реестр сменных заданий" ordering = ['-date', 'task__deal'] def __str__(self): if self.task: return f"{self.task.drawing_name} - {self.date}" return f"Без задания - {self.date}" class EmployeeProfile(models.Model): ROLE_CHOICES = [ ('admin', 'Администратор'), ('technologist', 'Технолог'), ('master', 'Мастер'), ('operator', 'Оператор'), ('clerk', 'Учетчик'), ] # Связь 1 к 1 со стандартным юзером Django user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile', verbose_name='Пользователь') role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='operator', verbose_name='Должность') # Привязка станков (можно выбрать несколько для одного оператора) machines = models.ManyToManyField('Machine', blank=True, verbose_name='Закрепленные станки') def __str__(self): return f"{self.user.username} - {self.get_role_display()}" class Meta: verbose_name = 'Профиль сотрудника' verbose_name_plural = 'Профили сотрудников'