136 lines
6.7 KiB
Python
136 lines
6.7 KiB
Python
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)
|
||
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 Material(models.Model):
|
||
"""
|
||
Справочник ТМЦ (Трубы, листы, профили).
|
||
Необходим для точного списания остатков и синхронизации с 1С.
|
||
"""
|
||
name = models.CharField("Наименование", max_length=255, unique=True)
|
||
|
||
def __str__(self): return self.name
|
||
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(Material, 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', 'Недодел'),
|
||
]
|
||
|
||
# --- Ссылка на основу (временно 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):
|
||
return f"{self.task.drawing_name} - {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 = 'Профили сотрудников' |