feat: 添加process和route表
This commit is contained in:
parent
c89d787690
commit
095eaabbb9
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)),
|
||||
]
|
|
@ -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']
|
Loading…
Reference in New Issue