feat: 增加生产日志表及创建逻辑
This commit is contained in:
parent
8a3c6150a0
commit
b7ba095de7
|
@ -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)
|
||||
|
||||
|
|
|
@ -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='状态'),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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={})
|
||||
|
|
|
@ -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)),
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue