feat: 添加process和route表

This commit is contained in:
caoqianming 2023-09-21 16:26:58 +08:00
parent c89d787690
commit 095eaabbb9
6 changed files with 195 additions and 23 deletions

View File

@ -1,5 +1,20 @@
from django_filters import rest_framework as filters
from apps.mtm.models import Goal
from apps.mtm.models import Goal, Material
from django.db.models.expressions import F
class MaterialFilter(filters.FilterSet):
tag = filters.CharFilter(method='filter_tag')
class Meta:
model = Material
fields = {
"type": ["exact", "in"],
}
def filter_tag(self, queryset, name, value):
if value == 'low_inm':
queryset = queryset.exclude(count_safe=None).filter(count__lte = F('count_safe'))
return queryset
class GoalFilter(filters.FilterSet):
class Meta:

View File

@ -0,0 +1,92 @@
# Generated by Django 3.2.12 on 2023-09-21 08:26
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('system', '0002_myschedule'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('mtm', '0009_process'),
]
operations = [
migrations.AddField(
model_name='material',
name='count',
field=models.PositiveIntegerField(default=0, verbose_name='物料库存总数'),
),
migrations.AddField(
model_name='material',
name='count_safe',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='安全库存总数'),
),
migrations.AddField(
model_name='material',
name='number',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='编号'),
),
migrations.AddField(
model_name='material',
name='specification',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='型号'),
),
migrations.AddField(
model_name='material',
name='unit',
field=models.CharField(default='', max_length=10, verbose_name='基准计量单位'),
),
migrations.AddField(
model_name='process',
name='belong_dept',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='process_belong_dept', to='system.dept', verbose_name='所属部门'),
),
migrations.AddField(
model_name='process',
name='cate',
field=models.CharField(default='', max_length=10, verbose_name='大类'),
),
migrations.AddField(
model_name='process',
name='instruction',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.file', verbose_name='指导书'),
),
migrations.AddField(
model_name='process',
name='instruction_content',
field=models.TextField(blank=True, null=True, verbose_name='指导书内容'),
),
migrations.AddField(
model_name='process',
name='sort',
field=models.PositiveSmallIntegerField(default=1, verbose_name='排序'),
),
migrations.AlterField(
model_name='material',
name='type',
field=models.PositiveSmallIntegerField(choices=[(0, '电/水/气'), (10, '成品'), (20, '半成品'), (30, '主要原料'), (40, '辅助材料'), (50, '加工工具'), (60, '辅助工装'), (70, '办公用品')], default=1, help_text="((0, '电/水/气'), (10, '成品'), (20, '半成品'), (30, '主要原料'), (40, '辅助材料'), (50, '加工工具'), (60, '辅助工装'), (70, '办公用品'))", verbose_name='物料类型'),
),
migrations.CreateModel(
name='Route',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('sort', models.PositiveSmallIntegerField(default=1, verbose_name='顺序')),
('is_autotask', models.BooleanField(default=False, verbose_name='是否自动排产')),
('out_rate', models.FloatField(default=100, verbose_name='出材率')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='route_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联产品')),
('process', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mtm.process', verbose_name='工序')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='route_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -1,5 +1,5 @@
from django.db import models
from apps.system.models import CommonAModel, Dictionary, CommonBModel, CommonADModel
from apps.system.models import CommonAModel, Dictionary, CommonBModel, CommonADModel, File, BaseModel
# Create your models here.
class Material(CommonAModel):
@ -10,6 +10,7 @@ class Material(CommonAModel):
MA_TYPE_HELPSO = 40
MA_TYPE_TOOL = 50
MA_TYPE_HELPTOOL = 60
MA_TYPE_OFFICE = 70
type_choices=(
(MA_TYPE_BASE, '电/水/气'),
@ -18,13 +19,19 @@ class Material(CommonAModel):
(MA_TYPE_MAINSO, '主要原料'),
(MA_TYPE_HELPSO, '辅助材料'),
(MA_TYPE_TOOL, '加工工具'),
(MA_TYPE_HELPTOOL, '辅助工装')
(MA_TYPE_HELPTOOL, '辅助工装'),
(MA_TYPE_OFFICE, '办公用品')
)
name = models.CharField('名称', max_length=50)
number = models.CharField('编号', max_length=100, null=True, blank=True)
specification = models.CharField('型号', max_length=100, null=True, blank=True)
code = models.CharField('标识', max_length=50, null=True, blank=True)
type = models.PositiveSmallIntegerField('物料类型', choices= type_choices, default=1, help_text=str(type_choices))
testitems = models.JSONField('检测项目', default=list, blank=True)
sort = models.PositiveSmallIntegerField('排序', default=1)
unit = models.CharField('基准计量单位', default='', max_length=10)
count = models.PositiveIntegerField('物料库存总数', default=0)
count_safe = models.PositiveIntegerField('安全库存总数', null=True, blank=True)
class Meta:
verbose_name = '物料表'
@ -91,9 +98,23 @@ class Goal(CommonADModel):
class Process(CommonAModel):
class Process(CommonBModel):
"""
工序
工序 belong_dept为所在车间
"""
name = models.CharField('工序名称', max_length=100)
cate = models.CharField('大类', max_length=10, default='')
sort = models.PositiveSmallIntegerField('排序', default=1)
instruction = models.ForeignKey(File, verbose_name='指导书', on_delete=models.SET_NULL, null=True, blank=True)
instruction_content = models.TextField('指导书内容', null=True, blank=True)
class Route(CommonAModel):
"""
工艺路线
"""
material = models.ForeignKey(Material, verbose_name='关联产品', on_delete=models.CASCADE, null=True, blank=True)
process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE, null=True, blank=True)
sort = models.PositiveSmallIntegerField('顺序', default=1)
is_autotask = models.BooleanField('是否自动排产', default=False)
out_rate = models.FloatField('出材率', default=100)

View File

@ -1,5 +1,5 @@
from apps.utils.serializers import CustomModelSerializer
from apps.mtm.models import Shift, Material, Mgroup, Team, Goal
from apps.mtm.models import Shift, Material, Mgroup, Team, Goal, Process, Route
from apps.utils.constants import EXCLUDE_FIELDS
from rest_framework import serializers
from apps.system.models import Dept
@ -44,4 +44,27 @@ class GoalSerializer(CustomModelSerializer):
class MgroupGoalYearSerializer(serializers.Serializer):
mgroup = serializers.CharField(label='ID或名称')
year = serializers.IntegerField(label='')
year = serializers.IntegerField(label='')
class ProcessSerializer(CustomModelSerializer):
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
class Meta:
model = Process
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class RouteSerializer(CustomModelSerializer):
material_ = MaterialSerializer(source='material', read_only=True)
process_name = serializers.CharField(source='process.name', read_only=True)
process_cate = serializers.CharField(source='process.cate', read_only=True)
class Meta:
model = Route
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
def update(self, instance, validated_data):
validated_data.pop('material', None)
validated_data.pop('process', None)
return super().update(instance, validated_data)

View File

@ -1,7 +1,7 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from apps.mtm.views import MgroupViewSet, ShiftViewSet, TeamViewSet, MaterialViewSet, GoalViewSet
from apps.mtm.views import (MgroupViewSet, ShiftViewSet, TeamViewSet, MaterialViewSet, GoalViewSet, ProcessViewSet, RouteViewSet)
API_BASE_URL = 'api/mtm/'
HTML_BASE_URL = 'mtm/'
@ -12,7 +12,8 @@ router.register('team', TeamViewSet, basename='team')
router.register('material', MaterialViewSet, basename='material')
router.register('shift', ShiftViewSet, basename='shift')
router.register('goal', GoalViewSet, basename='goal')
router.register('process', ProcessViewSet, basename='process')
router.register('route', RouteViewSet, basename='route')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),
]

View File

@ -1,17 +1,17 @@
from django.shortcuts import render
from rest_framework.mixins import ListModelMixin
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.mtm.models import Material, Mgroup, Shift, Team, Goal
from apps.mtm.serializers import MaterialSerializer, MgroupSerializer, ShiftSerializer, TeamSerializer, GoalSerializer
from apps.mtm.filters import GoalFilter
from rest_framework.decorators import action
from rest_framework.mixins import ListModelMixin
from rest_framework.response import Response
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from django.db.models import Q
from rest_framework.exceptions import ParseError
from apps.mtm.filters import GoalFilter, MaterialFilter
from apps.mtm.models import Goal, Material, Mgroup, Shift, Team, Process, Route
from apps.mtm.serializers import (GoalSerializer, MaterialSerializer,
MgroupGoalYearSerializer, MgroupSerializer,
ShiftSerializer, TeamSerializer, ProcessSerializer, RouteSerializer)
from apps.mtm.services import get_mgroup_goals
from apps.mtm.serializers import MgroupGoalYearSerializer
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
# Create your views here.
class MaterialViewSet(CustomModelViewSet):
@ -22,9 +22,9 @@ class MaterialViewSet(CustomModelViewSet):
"""
queryset = Material.objects.all()
serializer_class = MaterialSerializer
filterset_fields = ['code']
search_fields = ['name', 'code']
ordering = ['id']
filterset_class = MaterialFilter
search_fields = ['name', 'code', 'number', 'specification']
ordering = ['sort', 'id']
class ShiftViewSet(ListModelMixin, CustomGenericViewSet):
@ -90,4 +90,24 @@ class GoalViewSet(CustomModelViewSet):
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
res = get_mgroup_goals(vdata['mgroup'], vdata['year'], True)
return Response(res)
return Response(res)
class ProcessViewSet(CustomModelViewSet):
queryset = Process.objects.all()
serializer_class = ProcessSerializer
select_related_fields = ['belong_dept']
search_fields = ['name', 'cate']
filterset_fields = ['cate']
ordering = ['sort', 'create_time']
def perform_destroy(self, instance):
if Route.objects.filter(process=instance).exists():
raise ParseError('存在使用的工艺路线!')
return super().perform_destroy(instance)
class RouteViewSet(CustomModelViewSet):
queryset = Route.objects.all()
serializer_class = RouteSerializer
filterset_fields = ['material', 'process', 'is_autotask']
ordering = ['sort', 'create_time']