Конкретно пересмотрел логику работы. Легаси вынесена в архив
All checks were successful
Deploy MES Core / deploy (push) Successful in 13s
All checks were successful
Deploy MES Core / deploy (push) Successful in 13s
This commit is contained in:
@@ -1,12 +1,21 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import BOM, ProductEntity, RouteStub
|
||||
from .models import BOM, EntityOperation, Operation, ProductEntity
|
||||
|
||||
|
||||
@admin.register(RouteStub)
|
||||
class RouteStubAdmin(admin.ModelAdmin):
|
||||
list_display = ('name',)
|
||||
search_fields = ('name',)
|
||||
@admin.register(Operation)
|
||||
class OperationAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'code', 'workshop')
|
||||
search_fields = ('name', 'code')
|
||||
list_filter = ('workshop',)
|
||||
autocomplete_fields = ('workshop',)
|
||||
|
||||
|
||||
class EntityOperationInline(admin.TabularInline):
|
||||
model = EntityOperation
|
||||
fields = ('seq', 'operation')
|
||||
autocomplete_fields = ('operation',)
|
||||
extra = 5
|
||||
|
||||
|
||||
class BOMChildInline(admin.TabularInline):
|
||||
@@ -31,8 +40,8 @@ class ProductEntityAdmin(admin.ModelAdmin):
|
||||
)
|
||||
list_filter = ('entity_type', 'planned_material__category')
|
||||
search_fields = ('drawing_number', 'name', 'planned_material__name', 'planned_material__full_name')
|
||||
autocomplete_fields = ('planned_material', 'route')
|
||||
inlines = (BOMChildInline,)
|
||||
autocomplete_fields = ('planned_material',)
|
||||
inlines = (EntityOperationInline, BOMChildInline,)
|
||||
|
||||
|
||||
@admin.register(BOM)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 6.0.3 on 2026-04-07 18:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('manufacturing', '0004_castingpassport_outsourcedpassport_partpassport_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='assemblypassport',
|
||||
name='requires_painting',
|
||||
field=models.BooleanField(default=False, verbose_name='Требуется покраска'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='assemblypassport',
|
||||
name='requires_welding',
|
||||
field=models.BooleanField(default=False, verbose_name='Требуется сварка'),
|
||||
),
|
||||
]
|
||||
43
manufacturing/migrations/0006_operation_entityoperation.py
Normal file
43
manufacturing/migrations/0006_operation_entityoperation.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 6.0.3 on 2026-04-08 18:00
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('manufacturing', '0005_assemblypassport_requires_painting_and_more'),
|
||||
('shiftflow', '0022_employeeprofile_allowed_workshops_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Operation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, unique=True, verbose_name='Операция')),
|
||||
('code', models.SlugField(help_text='Стабильный идентификатор (например welding, painting, laser_cutting).', max_length=64, unique=True, verbose_name='Код')),
|
||||
('workshop', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='shiftflow.workshop', verbose_name='Цех по умолчанию')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Операция',
|
||||
'verbose_name_plural': 'Операции',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EntityOperation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('seq', models.PositiveSmallIntegerField(default=1, verbose_name='Порядок')),
|
||||
('entity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='operations', to='manufacturing.productentity', verbose_name='Сущность')),
|
||||
('operation', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='manufacturing.operation', verbose_name='Операция')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Операция сущности',
|
||||
'verbose_name_plural': 'Операции сущностей',
|
||||
'ordering': ('entity', 'seq', 'id'),
|
||||
'unique_together': {('entity', 'seq')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 6.0.3 on 2026-04-08 18:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('manufacturing', '0006_operation_entityoperation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='productentity',
|
||||
name='route',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='RouteStub',
|
||||
),
|
||||
]
|
||||
@@ -1,14 +1,30 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class RouteStub(models.Model):
|
||||
"""Маршрут (пока заглушка под техпроцессы)."""
|
||||
class Operation(models.Model):
|
||||
"""Операция техпроцесса.
|
||||
|
||||
name = models.CharField("Маршрут", max_length=200, unique=True)
|
||||
Комментарий: справочник расширяется без изменений кода.
|
||||
"""
|
||||
|
||||
name = models.CharField('Операция', max_length=200, unique=True)
|
||||
code = models.SlugField(
|
||||
'Код',
|
||||
max_length=64,
|
||||
unique=True,
|
||||
help_text='Стабильный идентификатор (например welding, painting, laser_cutting).',
|
||||
)
|
||||
workshop = models.ForeignKey(
|
||||
'shiftflow.Workshop',
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name='Цех по умолчанию',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Маршрут"
|
||||
verbose_name_plural = "Маршруты"
|
||||
verbose_name = 'Операция'
|
||||
verbose_name_plural = 'Операции'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -48,13 +64,6 @@ class ProductEntity(models.Model):
|
||||
blank=True,
|
||||
verbose_name="Заложенный материал",
|
||||
)
|
||||
route = models.ForeignKey(
|
||||
RouteStub,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Маршрут",
|
||||
)
|
||||
|
||||
blank_area_m2 = models.FloatField("Норма: площадь заготовки (м²/шт)", null=True, blank=True)
|
||||
blank_length_mm = models.FloatField("Норма: длина заготовки (мм/шт)", null=True, blank=True)
|
||||
@@ -74,6 +83,23 @@ class ProductEntity(models.Model):
|
||||
return base if base else self.name
|
||||
|
||||
|
||||
class EntityOperation(models.Model):
|
||||
"""Операции техпроцесса для конкретной сущности (деталь/сборка/изделие)."""
|
||||
|
||||
entity = models.ForeignKey(ProductEntity, on_delete=models.CASCADE, related_name='operations', verbose_name='Сущность')
|
||||
operation = models.ForeignKey(Operation, on_delete=models.PROTECT, verbose_name='Операция')
|
||||
seq = models.PositiveSmallIntegerField('Порядок', default=1)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Операция сущности'
|
||||
verbose_name_plural = 'Операции сущностей'
|
||||
ordering = ('entity', 'seq', 'id')
|
||||
unique_together = ('entity', 'seq')
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.entity}: {self.seq}. {self.operation}"
|
||||
|
||||
|
||||
class BOM(models.Model):
|
||||
"""Спецификация (BOM): parent состоит из child в количестве quantity."""
|
||||
|
||||
@@ -103,6 +129,9 @@ class BOM(models.Model):
|
||||
class AssemblyPassport(models.Model):
|
||||
entity = models.OneToOneField(ProductEntity, on_delete=models.CASCADE, related_name='assembly_passport')
|
||||
|
||||
requires_welding = models.BooleanField('Требуется сварка', default=False)
|
||||
requires_painting = models.BooleanField('Требуется покраска', default=False)
|
||||
|
||||
weight_kg = models.FloatField('Масса, кг', null=True, blank=True)
|
||||
coating = models.CharField('Покрытие', max_length=200, blank=True, default='')
|
||||
coating_color = models.CharField('Цвет', max_length=100, blank=True, default='')
|
||||
|
||||
Reference in New Issue
Block a user