feat: 增加生产日志表及创建逻辑

This commit is contained in:
caoqianming 2023-10-08 11:27:41 +08:00
parent 8a3c6150a0
commit b7ba095de7
10 changed files with 188 additions and 16 deletions

View File

@ -2,32 +2,43 @@ from apps.utils.serializers import CustomModelSerializer
from apps.mtm.models import Shift, Material, Mgroup, Team, Goal, Process, Route 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 rest_framework.exceptions import ValidationError
from apps.system.models import Dept from apps.system.models import Dept
class ShiftSerializer(CustomModelSerializer): class ShiftSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Shift model = Shift
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
class MaterialSerializer(CustomModelSerializer): class MaterialSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Material model = Material
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
class MgroupSerializer(CustomModelSerializer): class MgroupSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True) belong_dept = serializers.PrimaryKeyRelatedField(
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True) label="所属部门", queryset=Dept.objects.all(), required=True)
belong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
class Meta: class Meta:
model = Mgroup model = Mgroup
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
class TeamSerializer(CustomModelSerializer): class TeamSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True) belong_dept = serializers.PrimaryKeyRelatedField(
label="所属部门", queryset=Dept.objects.all(), required=True)
leader_name = serializers.CharField(source='leader.name', read_only=True) leader_name = serializers.CharField(source='leader.name', read_only=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True) belong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
class Meta: class Meta:
model = Team model = Team
fields = '__all__' fields = '__all__'
@ -35,36 +46,48 @@ class TeamSerializer(CustomModelSerializer):
class GoalSerializer(CustomModelSerializer): class GoalSerializer(CustomModelSerializer):
goal_cate_name = serializers.CharField(source='goal_cate.name', read_only=True) goal_cate_name = serializers.CharField(
source='goal_cate.name', read_only=True)
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True) mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
class Meta: class Meta:
model = Goal model = Goal
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
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): class ProcessSerializer(CustomModelSerializer):
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True) belong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
class Meta: class Meta:
model = Process model = Process
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
class RouteSerializer(CustomModelSerializer): class RouteSerializer(CustomModelSerializer):
material_ = MaterialSerializer(source='material', read_only=True) material_ = MaterialSerializer(source='material', read_only=True)
process_name = serializers.CharField(source='process.name', read_only=True) process_name = serializers.CharField(source='process.name', read_only=True)
process_cate = serializers.CharField(source='process.cate', read_only=True) process_cate = serializers.CharField(source='process.cate', read_only=True)
class Meta: class Meta:
model = Route model = Route
fields = '__all__' fields = '__all__'
read_only_fields = EXCLUDE_FIELDS read_only_fields = EXCLUDE_FIELDS
def validate(self, attrs):
material = attrs['material']
if material.type != Material.MA_TYPE_GOOD:
raise ValidationError('请选择最终产品')
return super().validate(attrs)
def update(self, instance, validated_data): def update(self, instance, validated_data):
validated_data.pop('material', None) validated_data.pop('material', None)
validated_data.pop('process', None) validated_data.pop('process', None)
return super().update(instance, validated_data) return super().update(instance, validated_data)

View File

@ -0,0 +1,30 @@
# Generated by Django 3.2.12 on 2023-10-08 03:26
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0014_alter_process_options'),
('pm', '0003_auto_20230927_0936'),
]
operations = [
migrations.AddField(
model_name='mtask',
name='material_before',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mtask_material_before', to='mtm.material', verbose_name='领用物'),
),
migrations.AlterField(
model_name='mtask',
name='material',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mtask_material', to='mtm.material', verbose_name='产物'),
),
migrations.AlterField(
model_name='mtask',
name='state',
field=models.PositiveIntegerField(choices=[(10, '创建中'), (20, '已下达')], default=10, help_text="((10, '创建中'), (20, '已下达'))", verbose_name='状态'),
),
]

View File

@ -16,16 +16,18 @@ class Mtask(CommonADModel):
MTASK_STATES = ( MTASK_STATES = (
(MTASK_CREATED, '创建中'), (MTASK_CREATED, '创建中'),
(MTASK_ASSGINED, '已下达'), (MTASK_ASSGINED, '已下达'),
(MTASK_WORKING, '生产中'), # (MTASK_WORKING, '生产中'),
(MTASK_DONE, '已完成') # (MTASK_DONE, '已结束')
) )
state = models.PositiveIntegerField( state = models.PositiveIntegerField(
'状态', choices=MTASK_STATES, default=MTASK_CREATED, help_text=str(MTASK_STATES)) '状态', choices=MTASK_STATES, default=MTASK_CREATED, help_text=str(MTASK_STATES))
number = models.CharField('编号', max_length=50, unique=True) number = models.CharField('编号', max_length=50, unique=True)
mgroup = models.ForeignKey( mgroup = models.ForeignKey(
Mgroup, verbose_name='工段', on_delete=models.CASCADE) Mgroup, verbose_name='工段', on_delete=models.CASCADE)
material_before = models.ForeignKey(
Material, verbose_name='领用物', on_delete=models.CASCADE, related_name='mtask_material_before', null=True, blank=True)
material = models.ForeignKey( material = models.ForeignKey(
Material, verbose_name='产品', on_delete=models.CASCADE) Material, verbose_name='', on_delete=models.CASCADE, related_name='mtask_material')
count = models.PositiveIntegerField('任务数', default=1) count = models.PositiveIntegerField('任务数', default=1)
count_real = models.PositiveIntegerField('实际生产数', default=0) count_real = models.PositiveIntegerField('实际生产数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0) count_ok = models.PositiveIntegerField('合格数', default=0)

View File

@ -51,6 +51,9 @@ class PmService:
last_route = rqs.last() # 最后一步是产生产品 last_route = rqs.last() # 最后一步是产生产品
# 创建父任务 # 创建父任务
for ind, val in enumerate(rqs): for ind, val in enumerate(rqs):
material_before = None
if ind > 0:
material_before = rqs[ind-1].material
if val.is_autotask: if val.is_autotask:
# 找到工段 # 找到工段
mgroups = val.mgroups mgroups = val.mgroups
@ -82,6 +85,7 @@ class PmService:
fmtask, _ = Mtask.objects.get_or_create(mgroup=mgroup, material=halfgood, defaults={ fmtask, _ = Mtask.objects.get_or_create(mgroup=mgroup, material=halfgood, defaults={
'number': f'{product.number}_r{ind+1}_{start_date_str}', 'number': f'{product.number}_r{ind+1}_{start_date_str}',
'material': halfgood, 'material': halfgood,
'material_before': material_before,
'mgroup': mgroup, 'mgroup': mgroup,
'count': task_count, 'count': task_count,
'start_date': start_date, 'start_date': start_date,
@ -96,6 +100,7 @@ class PmService:
Mtask.objects.get_or_create(mgroup=mgroup, material=halfgood, parent=fmtask, defaults={ Mtask.objects.get_or_create(mgroup=mgroup, material=halfgood, parent=fmtask, defaults={
'number': f'{fmtask.number}_{i+1}', 'number': f'{fmtask.number}_{i+1}',
'material': halfgood, 'material': halfgood,
'material_before': material_before,
'mgroup': mgroup, 'mgroup': mgroup,
'count': task_count_day, 'count': task_count_day,
'start_date': task_date, 'start_date': task_date,

View File

@ -0,0 +1,38 @@
# Generated by Django 3.2.12 on 2023-10-08 01:17
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 = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('pm', '0003_auto_20230927_0936'),
('wpm', '0011_wmaterial'),
]
operations = [
migrations.CreateModel(
name='Mlog',
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='删除标记')),
('batch', models.CharField(max_length=50, verbose_name='批次号')),
('count_use', models.PositiveIntegerField(default=0, verbose_name='领用数')),
('count_real', models.PositiveIntegerField(default=0, verbose_name='实际生产数')),
('count_ok', models.PositiveIntegerField(default=0, verbose_name='合格数')),
('count_notok', models.PositiveIntegerField(default=0, verbose_name='不合格数')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mlog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('mtask', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.mtask', verbose_name='关联生产')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mlog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from apps.utils.models import CommonADModel, CommonBDModel from apps.utils.models import CommonADModel, CommonBDModel
from apps.mtm.models import Mgroup, Team, Shift, Material from apps.mtm.models import Mgroup, Team, Shift, Material
from apps.pm.models import Mtask
from django.utils.timezone import localtime from django.utils.timezone import localtime
# Create your models here. # Create your models here.
@ -73,3 +74,16 @@ class WMaterial(CommonBDModel):
Material, verbose_name='物料', on_delete=models.CASCADE) Material, verbose_name='物料', on_delete=models.CASCADE)
batch = models.CharField('批次号', max_length=50) batch = models.CharField('批次号', max_length=50)
count = models.PositiveIntegerField('当前数量', default=0) count = models.PositiveIntegerField('当前数量', default=0)
class Mlog(CommonADModel):
"""
生产日志
"""
mtask = models.ForeignKey(
Mtask, verbose_name='关联生产', on_delete=models.CASCADE)
batch = models.CharField('批次号', max_length=50)
count_use = models.PositiveIntegerField('领用数', default=0)
count_real = models.PositiveIntegerField('实际生产数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0)
count_notok = models.PositiveIntegerField('不合格数', default=0)

View File

@ -2,7 +2,7 @@ from apps.utils.constants import EXCLUDE_FIELDS
from apps.utils.serializers import CustomModelSerializer from apps.utils.serializers import CustomModelSerializer
from rest_framework import serializers from rest_framework import serializers
from .models import SfLog, StLog, SfLogExp, WMaterial from .models import SfLog, StLog, SfLogExp, WMaterial, Mlog
from apps.system.models import Dictionary from apps.system.models import Dictionary
from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change
from apps.mtm.serializers import MaterialSerializer from apps.mtm.serializers import MaterialSerializer
@ -73,3 +73,15 @@ class WMaterialSerializer(CustomModelSerializer):
class Meta: class Meta:
model = WMaterial model = WMaterial
fields = '__all__' fields = '__all__'
class MlogSerializer(CustomModelSerializer):
create_by_name = serializers.CharField(
source='create_by.name', read_only=True)
update_by_name = serializers.CharField(
source='update_by.name', read_only=True)
class Meta:
model = Mlog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS

View File

@ -8,9 +8,9 @@ from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from apps.inm.models import MIO, MIOItem from apps.inm.models import MIO, MIOItem
from apps.mtm.models import Mgroup, Shift from apps.mtm.models import Mgroup, Shift, Material
from .models import SfLog, SfLogExp, WMaterial from .models import SfLog, SfLogExp, WMaterial, Mlog
def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.date): def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.date):
@ -101,3 +101,30 @@ def do_in(mio: MIO):
wm.delete() wm.delete()
else: else:
raise ParseError('车间物料不足') raise ParseError('车间物料不足')
def mlog_create(mlog: Mlog):
"""
生产日志创建后需要执行的操作
"""
mtask = mlog.mtask
material = mtask.material
material_before = mtask.material_before
if material_before and material_before.is_hidden is False: # 需要进行车间库存管理
# 需要判断领用数是否合理
material_has_qs = WMaterial.objects.filter(
batch=mlog.batch, material=material_before)
count_x = material_has_qs.count()
if count_x == 1:
material_has = material_has_qs.first()
elif count_x == 0:
raise ParseError(f'{material_before.name}-批次库存不存在!')
else:
raise ParseError(f'{material_before.name}-存在多个相同批次!')
if mlog.count_use > material_has.count:
raise ParseError(f'{material_before.name}-该批次车间库存不足!')
else:
material_has.count = material_has.count - mlog.count_use
material_has.save()
# if material.is_hidden is False: # 需要入库
# wmaterial = WMaterial.objects.get_or_create(batch=mlog.batch, material=material, defaults={})

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.wpm.views import SfLogViewSet, StLogViewSet, SfLogExpViewSet, WMaterialViewSet from apps.wpm.views import SfLogViewSet, StLogViewSet, SfLogExpViewSet, WMaterialViewSet, MlogViewSet
API_BASE_URL = 'api/wpm/' API_BASE_URL = 'api/wpm/'
@ -12,6 +12,7 @@ router.register('sflog', SfLogViewSet, basename='sflog')
router.register('stlog', StLogViewSet, basename='stlog') router.register('stlog', StLogViewSet, basename='stlog')
router.register('sflogexp', SfLogExpViewSet, basename='sflogexp') router.register('sflogexp', SfLogExpViewSet, basename='sflogexp')
router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
router.register('mlog', MlogViewSet, basename='mlog')
urlpatterns = [ urlpatterns = [
path(API_BASE_URL, include(router.urls)), path(API_BASE_URL, include(router.urls)),
] ]

View File

@ -7,10 +7,12 @@ from rest_framework.response import Response
from apps.mtm.models import Material from apps.mtm.models import Material
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin
from .filters import SfLogExpFilter, SfLogFilter from .filters import SfLogExpFilter, SfLogFilter
from .models import SfLog, SfLogExp, StLog, WMaterial from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog
from .serializers import SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer from .serializers import SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogSerializer
from .services import mlog_create
# Create your views here. # Create your views here.
@ -93,3 +95,21 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
search_fields = ['material__name', search_fields = ['material__name',
'material__number', 'material__specification'] 'material__number', 'material__specification']
filterset_fields = ['material', 'belong_dept'] filterset_fields = ['material', 'belong_dept']
class MlogViewSet(ListModelMixin, BulkCreateModelMixin, DestroyModelMixin, CustomGenericViewSet):
"""
list: 生产日志
生产日志
"""
perms_map = {'get': '', 'post': 'mlog.create', 'delete': 'mlog.delete'}
queryset = Mlog.objects.all()
serializer_class = MlogSerializer
select_related_fields = ['create_by', 'update_by', 'mtask']
filterset_fields = ['mtask']
def perform_create(self, serializer):
with transaction.atomic():
ins = serializer.save()
mlog_create(ins)