All checks were successful
Deploy MES Core / deploy (push) Successful in 11s
90 lines
5.0 KiB
Python
90 lines
5.0 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)
|
||
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) |