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 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 GoalFilter(filters.FilterSet):
class Meta: 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 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. # Create your models here.
class Material(CommonAModel): class Material(CommonAModel):
@ -10,6 +10,7 @@ class Material(CommonAModel):
MA_TYPE_HELPSO = 40 MA_TYPE_HELPSO = 40
MA_TYPE_TOOL = 50 MA_TYPE_TOOL = 50
MA_TYPE_HELPTOOL = 60 MA_TYPE_HELPTOOL = 60
MA_TYPE_OFFICE = 70
type_choices=( type_choices=(
(MA_TYPE_BASE, '电/水/气'), (MA_TYPE_BASE, '电/水/气'),
@ -18,13 +19,19 @@ class Material(CommonAModel):
(MA_TYPE_MAINSO, '主要原料'), (MA_TYPE_MAINSO, '主要原料'),
(MA_TYPE_HELPSO, '辅助材料'), (MA_TYPE_HELPSO, '辅助材料'),
(MA_TYPE_TOOL, '加工工具'), (MA_TYPE_TOOL, '加工工具'),
(MA_TYPE_HELPTOOL, '辅助工装') (MA_TYPE_HELPTOOL, '辅助工装'),
(MA_TYPE_OFFICE, '办公用品')
) )
name = models.CharField('名称', max_length=50) 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) code = models.CharField('标识', max_length=50, null=True, blank=True)
type = models.PositiveSmallIntegerField('物料类型', choices= type_choices, default=1, help_text=str(type_choices)) type = models.PositiveSmallIntegerField('物料类型', choices= type_choices, default=1, help_text=str(type_choices))
testitems = models.JSONField('检测项目', default=list, blank=True) testitems = models.JSONField('检测项目', default=list, blank=True)
sort = models.PositiveSmallIntegerField('排序', default=1) 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: class Meta:
verbose_name = '物料表' verbose_name = '物料表'
@ -91,9 +98,23 @@ class Goal(CommonADModel):
class Process(CommonAModel): class Process(CommonBModel):
""" """
工序 工序 belong_dept为所在车间
""" """
name = models.CharField('工序名称', max_length=100) 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.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 apps.utils.constants import EXCLUDE_FIELDS
from rest_framework import serializers from rest_framework import serializers
from apps.system.models import Dept from apps.system.models import Dept
@ -44,4 +44,27 @@ class GoalSerializer(CustomModelSerializer):
class MgroupGoalYearSerializer(serializers.Serializer): class MgroupGoalYearSerializer(serializers.Serializer):
mgroup = serializers.CharField(label='ID或名称') 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 django.urls import path, include
from rest_framework.routers import DefaultRouter 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/' API_BASE_URL = 'api/mtm/'
HTML_BASE_URL = 'mtm/' HTML_BASE_URL = 'mtm/'
@ -12,7 +12,8 @@ router.register('team', TeamViewSet, basename='team')
router.register('material', MaterialViewSet, basename='material') router.register('material', MaterialViewSet, basename='material')
router.register('shift', ShiftViewSet, basename='shift') router.register('shift', ShiftViewSet, basename='shift')
router.register('goal', GoalViewSet, basename='goal') router.register('goal', GoalViewSet, basename='goal')
router.register('process', ProcessViewSet, basename='process')
router.register('route', RouteViewSet, basename='route')
urlpatterns = [ urlpatterns = [
path(API_BASE_URL, include(router.urls)), path(API_BASE_URL, include(router.urls)),
] ]

View File

@ -1,17 +1,17 @@
from django.shortcuts import render 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.decorators import action
from rest_framework.mixins import ListModelMixin
from rest_framework.response import Response from rest_framework.response import Response
from drf_yasg import openapi from rest_framework.exceptions import ParseError
from drf_yasg.utils import swagger_auto_schema
from django.db.models import Q 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.services import get_mgroup_goals
from apps.mtm.serializers import MgroupGoalYearSerializer from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
# Create your views here. # Create your views here.
class MaterialViewSet(CustomModelViewSet): class MaterialViewSet(CustomModelViewSet):
@ -22,9 +22,9 @@ class MaterialViewSet(CustomModelViewSet):
""" """
queryset = Material.objects.all() queryset = Material.objects.all()
serializer_class = MaterialSerializer serializer_class = MaterialSerializer
filterset_fields = ['code'] filterset_class = MaterialFilter
search_fields = ['name', 'code'] search_fields = ['name', 'code', 'number', 'specification']
ordering = ['id'] ordering = ['sort', 'id']
class ShiftViewSet(ListModelMixin, CustomGenericViewSet): class ShiftViewSet(ListModelMixin, CustomGenericViewSet):
@ -90,4 +90,24 @@ class GoalViewSet(CustomModelViewSet):
sr.is_valid(raise_exception=True) sr.is_valid(raise_exception=True)
vdata = sr.validated_data vdata = sr.validated_data
res = get_mgroup_goals(vdata['mgroup'], vdata['year'], True) 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']