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.utils.constants import EXCLUDE_FIELDS
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from apps.system.models import Dept
class ShiftSerializer(CustomModelSerializer):
class Meta:
model = Shift
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class MaterialSerializer(CustomModelSerializer):
class Meta:
model = Material
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class MgroupSerializer(CustomModelSerializer):
belong_dept = serializers.PrimaryKeyRelatedField(label="所属部门", queryset=Dept.objects.all(), required=True)
belong_dept_name = serializers.CharField(source='belong_dept.name', read_only=True)
belong_dept = serializers.PrimaryKeyRelatedField(
label="所属部门", queryset=Dept.objects.all(), required=True)
belong_dept_name = serializers.CharField(
source='belong_dept.name', read_only=True)
class Meta:
model = Mgroup
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
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)
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:
model = Team
fields = '__all__'
@ -35,36 +46,48 @@ class TeamSerializer(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)
class Meta:
model = Goal
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
class MgroupGoalYearSerializer(serializers.Serializer):
mgroup = serializers.CharField(label='ID或名称')
year = serializers.IntegerField(label='')
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:
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 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):
validated_data.pop('material', None)
validated_data.pop('process', None)
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_CREATED, '创建中'),
(MTASK_ASSGINED, '已下达'),
(MTASK_WORKING, '生产中'),
(MTASK_DONE, '已完成')
# (MTASK_WORKING, '生产中'),
# (MTASK_DONE, '已结束')
)
state = models.PositiveIntegerField(
'状态', choices=MTASK_STATES, default=MTASK_CREATED, help_text=str(MTASK_STATES))
number = models.CharField('编号', max_length=50, unique=True)
mgroup = models.ForeignKey(
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, verbose_name='产品', on_delete=models.CASCADE)
Material, verbose_name='', on_delete=models.CASCADE, related_name='mtask_material')
count = models.PositiveIntegerField('任务数', default=1)
count_real = models.PositiveIntegerField('实际生产数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0)

View File

@ -51,6 +51,9 @@ class PmService:
last_route = rqs.last() # 最后一步是产生产品
# 创建父任务
for ind, val in enumerate(rqs):
material_before = None
if ind > 0:
material_before = rqs[ind-1].material
if val.is_autotask:
# 找到工段
mgroups = val.mgroups
@ -82,6 +85,7 @@ class PmService:
fmtask, _ = Mtask.objects.get_or_create(mgroup=mgroup, material=halfgood, defaults={
'number': f'{product.number}_r{ind+1}_{start_date_str}',
'material': halfgood,
'material_before': material_before,
'mgroup': mgroup,
'count': task_count,
'start_date': start_date,
@ -96,6 +100,7 @@ class PmService:
Mtask.objects.get_or_create(mgroup=mgroup, material=halfgood, parent=fmtask, defaults={
'number': f'{fmtask.number}_{i+1}',
'material': halfgood,
'material_before': material_before,
'mgroup': mgroup,
'count': task_count_day,
'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 apps.utils.models import CommonADModel, CommonBDModel
from apps.mtm.models import Mgroup, Team, Shift, Material
from apps.pm.models import Mtask
from django.utils.timezone import localtime
# Create your models here.
@ -73,3 +74,16 @@ class WMaterial(CommonBDModel):
Material, verbose_name='物料', on_delete=models.CASCADE)
batch = models.CharField('批次号', max_length=50)
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 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.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change
from apps.mtm.serializers import MaterialSerializer
@ -73,3 +73,15 @@ class WMaterialSerializer(CustomModelSerializer):
class Meta:
model = WMaterial
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 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):
@ -101,3 +101,30 @@ def do_in(mio: MIO):
wm.delete()
else:
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 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/'
@ -12,6 +12,7 @@ router.register('sflog', SfLogViewSet, basename='sflog')
router.register('stlog', StLogViewSet, basename='stlog')
router.register('sflogexp', SfLogExpViewSet, basename='sflogexp')
router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
router.register('mlog', MlogViewSet, basename='mlog')
urlpatterns = [
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.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin
from .filters import SfLogExpFilter, SfLogFilter
from .models import SfLog, SfLogExp, StLog, WMaterial
from .serializers import SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog
from .serializers import SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogSerializer
from .services import mlog_create
# Create your views here.
@ -93,3 +95,21 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
search_fields = ['material__name',
'material__number', 'material__specification']
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)