feat: mtask 工序更改为关联mgroup
This commit is contained in:
parent
0ac87f5c09
commit
0f6a7289b8
|
|
@ -4,16 +4,20 @@ from django.utils import timezone
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
|
||||||
|
|
||||||
class MtaskFilter(filters.FilterSet):
|
class MtaskFilter(filters.FilterSet):
|
||||||
tag = filters.CharFilter(method='filter_tag')
|
tag = filters.CharFilter(method='filter_tag')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Mtask
|
model = Mtask
|
||||||
fields = {
|
fields = {
|
||||||
"state": ["exact", "in"],
|
"state": ["exact", "in"],
|
||||||
"start_date": ["exact", "gte", "lte"],
|
"start_date": ["exact", "gte", "lte"],
|
||||||
"end_date": ["exact", "gte", "lte"],
|
"end_date": ["exact", "gte", "lte"],
|
||||||
"process": ["exact"],
|
"mgroup": ["exact"],
|
||||||
"process__cate": ["exact"],
|
"mgroup__cate": ["exact"],
|
||||||
|
"mgroup__process": ["exact"],
|
||||||
|
"mgroup__process__cate": ["exact"],
|
||||||
"material": ["exact"],
|
"material": ["exact"],
|
||||||
"parent": ["exact", "isnull"]
|
"parent": ["exact", "isnull"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Generated by Django 3.2.12 on 2023-09-27 01:36
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mtm', '0013_mgroup_process'),
|
||||||
|
('pm', '0002_remove_mtask_order'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='mtask',
|
||||||
|
name='belong_dept',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='mtask',
|
||||||
|
name='process',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='mtask',
|
||||||
|
name='mgroup',
|
||||||
|
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='mtm.mgroup', verbose_name='工段'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from apps.utils.models import CommonBModel, CommonBDModel
|
from apps.utils.models import CommonADModel
|
||||||
from apps.mtm.models import Material, Process
|
from apps.mtm.models import Material, Mgroup
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
class Mtask(CommonBDModel):
|
|
||||||
|
class Mtask(CommonADModel):
|
||||||
"""
|
"""
|
||||||
生产任务
|
生产任务
|
||||||
"""
|
"""
|
||||||
|
|
@ -18,14 +19,18 @@ class Mtask(CommonBDModel):
|
||||||
(MTASK_WORKING, '生产中'),
|
(MTASK_WORKING, '生产中'),
|
||||||
(MTASK_DONE, '已完成')
|
(MTASK_DONE, '已完成')
|
||||||
)
|
)
|
||||||
state = models.PositiveIntegerField('状态', choices=MTASK_STATES, default=MTASK_CREATED, help_text=str(MTASK_STATES))
|
state = models.PositiveIntegerField(
|
||||||
|
'状态', 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)
|
||||||
process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE)
|
mgroup = models.ForeignKey(
|
||||||
material = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
|
Mgroup, verbose_name='工段', on_delete=models.CASCADE)
|
||||||
|
material = models.ForeignKey(
|
||||||
|
Material, verbose_name='产品', on_delete=models.CASCADE)
|
||||||
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)
|
||||||
count_notok = models.PositiveIntegerField('不合格数', default=0)
|
count_notok = models.PositiveIntegerField('不合格数', default=0)
|
||||||
start_date = models.DateField('计划开工日期')
|
start_date = models.DateField('计划开工日期')
|
||||||
end_date = models.DateField('计划完工日期')
|
end_date = models.DateField('计划完工日期')
|
||||||
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='父任务')
|
parent = models.ForeignKey(
|
||||||
|
'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='父任务')
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
from apps.utils.serializers import CustomModelSerializer
|
|
||||||
from apps.pm.models import Mtask
|
|
||||||
from apps.mtm.serializers import MaterialSerializer
|
|
||||||
from apps.sam.models import OrderItem
|
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
from apps.mtm.serializers import MaterialSerializer
|
||||||
|
from apps.pm.models import Mtask
|
||||||
|
from apps.sam.models import OrderItem
|
||||||
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
|
|
||||||
|
|
||||||
class MtaskSerializer(CustomModelSerializer):
|
class MtaskSerializer(CustomModelSerializer):
|
||||||
material_ = MaterialSerializer(source='material', read_only=True)
|
material_ = MaterialSerializer(source='material', read_only=True)
|
||||||
|
mgroup__name = serializers.CharField(source='mgroup.name', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Mtask
|
model = Mtask
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
@ -14,11 +18,14 @@ class MtaskSerializer(CustomModelSerializer):
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
if instance.state != Mtask.MTASK_CREATED:
|
if instance.state != Mtask.MTASK_CREATED:
|
||||||
raise ValidationError('任务非创建中不可编辑')
|
raise ValidationError('任务非创建中不可编辑')
|
||||||
new_data = {key: validated_data[key] for key in ['number', 'count', 'start_date', 'end_date']}
|
new_data = {key: validated_data[key] for key in [
|
||||||
return super().update(instance, validated_data)
|
'number', 'count', 'start_date', 'end_date']}
|
||||||
|
return super().update(instance, new_data)
|
||||||
|
|
||||||
|
|
||||||
class SchedueSerializer(serializers.Serializer):
|
class SchedueSerializer(serializers.Serializer):
|
||||||
orderitems = serializers.PrimaryKeyRelatedField(label='orderitem的ID列表', queryset=OrderItem.objects.all(), many=True)
|
orderitems = serializers.PrimaryKeyRelatedField(
|
||||||
|
label='orderitem的ID列表', queryset=OrderItem.objects.all(), many=True)
|
||||||
start_date = serializers.DateField(label='计划开工日期')
|
start_date = serializers.DateField(label='计划开工日期')
|
||||||
end_date = serializers.DateField(label='计划完工日期', allow_null=True, required=False)
|
end_date = serializers.DateField(
|
||||||
|
label='计划完工日期', allow_null=True, required=False)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from apps.sam.models import Order, OrderItem
|
from apps.sam.models import OrderItem
|
||||||
from apps.mtm.models import Route, Material
|
from apps.mtm.models import Route, Material, Mgroup
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from apps.pm.models import Mtask
|
from apps.pm.models import Mtask
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
|
@ -7,6 +7,7 @@ from datetime import date, timedelta
|
||||||
import math
|
import math
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
class PmService:
|
class PmService:
|
||||||
|
|
||||||
def check_orderitems(cls, orderitems: QuerySet[OrderItem]):
|
def check_orderitems(cls, orderitems: QuerySet[OrderItem]):
|
||||||
|
|
@ -19,7 +20,8 @@ class PmService:
|
||||||
raise ParseError('订单项已排任务!')
|
raise ParseError('订单项已排任务!')
|
||||||
productId = item.material.id
|
productId = item.material.id
|
||||||
if productId not in rdict:
|
if productId not in rdict:
|
||||||
rdict[productId] = [item.material, item.count, item.order.delivery_date, [item]]
|
rdict[productId] = [item.material, item.count,
|
||||||
|
item.order.delivery_date, [item]]
|
||||||
else:
|
else:
|
||||||
order_date = item.order.delivery_date
|
order_date = item.order.delivery_date
|
||||||
if rdict[productId][2] > order_date:
|
if rdict[productId][2] > order_date:
|
||||||
|
|
@ -28,9 +30,8 @@ class PmService:
|
||||||
rdict[productId][3].append(item)
|
rdict[productId][3].append(item)
|
||||||
return rdict
|
return rdict
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def schedue_from_orderitems(cls, orderitemIds: List[str], start_date: date, end_date: date=None):
|
def schedue_from_orderitems(cls, user, orderitemIds: List[str], start_date: date, end_date: date = None):
|
||||||
"""
|
"""
|
||||||
从多个订单明细自动排产
|
从多个订单明细自动排产
|
||||||
"""
|
"""
|
||||||
|
|
@ -46,16 +47,30 @@ class PmService:
|
||||||
# 计算相差天数
|
# 计算相差天数
|
||||||
rela_days = (end_date - start_date).days + 1
|
rela_days = (end_date - start_date).days + 1
|
||||||
# 获取工艺路线
|
# 获取工艺路线
|
||||||
rqs = Route.objects.filter(material=product).order_by('sort', 'create_time')
|
rqs = Route.get_routes(product)
|
||||||
last_route = rqs.last() # 最后一步是产生产品
|
last_route = rqs.last() # 最后一步是产生产品
|
||||||
# 创建父任务
|
# 创建父任务
|
||||||
for ind, val in enumerate(rqs):
|
for ind, val in enumerate(rqs):
|
||||||
if val.is_autotask:
|
if val.is_autotask:
|
||||||
|
# 找到工段
|
||||||
|
mgroups = Mgroup.objects.filter(process=val.process)
|
||||||
|
mgroups_count = mgroups.count()
|
||||||
|
if mgroups_count == 1:
|
||||||
|
mgroup = mgroups.first()
|
||||||
|
elif mgroups_count == 0:
|
||||||
|
raise ParseError(f'{val.name}-工段不存在!')
|
||||||
|
else:
|
||||||
|
raise ParseError(f'{val.name}-工段存在多个!')
|
||||||
# 找到存在的半成品
|
# 找到存在的半成品
|
||||||
halfgood, _ = Material.objects.get_or_create(type=Material.MA_TYPE_HALFGOOD, parent=product, process=val.process,
|
halfgood, _ = Material.objects.get_or_create(type=Material.MA_TYPE_HALFGOOD, parent=product, mgroup=mgroup,
|
||||||
defaults={'parent': product, 'process': val.process,
|
defaults={'parent': product, 'mgroup': mgroup,
|
||||||
'is_hidden': True, 'name': product.name,
|
'is_hidden': True, 'name': product.name,
|
||||||
'number': product.number, 'specification': product.specification, 'type': Material.MA_TYPE_HALFGOOD})
|
'number': product.number,
|
||||||
|
'specification': product.specification,
|
||||||
|
'type': Material.MA_TYPE_HALFGOOD,
|
||||||
|
'create_by': user,
|
||||||
|
'update_by': user,
|
||||||
|
})
|
||||||
if val == last_route:
|
if val == last_route:
|
||||||
halfgood = product
|
halfgood = product
|
||||||
task_count = count
|
task_count = count
|
||||||
|
|
@ -64,24 +79,28 @@ class PmService:
|
||||||
task_count = math.ceil(count / (val.out_rate/100))
|
task_count = math.ceil(count / (val.out_rate/100))
|
||||||
else:
|
else:
|
||||||
task_count = math.ceil(count / val.out_rate)
|
task_count = math.ceil(count / val.out_rate)
|
||||||
fmtask, _ = Mtask.objects.get_or_create(process=val.process, 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,
|
||||||
'process': val.process,
|
'mgroup': mgroup,
|
||||||
'count': task_count,
|
'count': task_count,
|
||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
'end_date': end_date
|
'end_date': end_date,
|
||||||
|
'create_by': user,
|
||||||
|
'update_by': user,
|
||||||
})
|
})
|
||||||
task_count_day = math.ceil(task_count/rela_days)
|
task_count_day = math.ceil(task_count/rela_days)
|
||||||
if fmtask.parent is None:
|
if fmtask.parent is None:
|
||||||
for i in range(rela_days):
|
for i in range(rela_days):
|
||||||
task_date = start_date + timedelta(days=i+1)
|
task_date = start_date + timedelta(days=i+1)
|
||||||
Mtask.objects.get_or_create(process=val.process, 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,
|
||||||
'process': val.process,
|
'mgroup': mgroup,
|
||||||
'count': task_count_day,
|
'count': task_count_day,
|
||||||
'start_date': task_date,
|
'start_date': task_date,
|
||||||
'end_date': task_date,
|
'end_date': task_date,
|
||||||
'parent': fmtask
|
'parent': fmtask,
|
||||||
|
'create_by': user,
|
||||||
|
'update_by': user
|
||||||
})
|
})
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
from django.shortcuts import render
|
|
||||||
from apps.utils.viewsets import CustomModelViewSet
|
|
||||||
from apps.pm.models import Mtask
|
|
||||||
from apps.pm.serializers import MtaskSerializer, SchedueSerializer
|
|
||||||
from apps.pm.filters import MtaskFilter
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from apps.pm.services import PmService
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from apps.utils.serializers import PkSerializer
|
||||||
|
from apps.utils.viewsets import CustomModelViewSet
|
||||||
|
|
||||||
|
from .filters import MtaskFilter
|
||||||
|
from .models import Mtask
|
||||||
|
from .serializers import MtaskSerializer, SchedueSerializer
|
||||||
|
from .services import PmService
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
|
||||||
class MtaskViewSet(CustomModelViewSet):
|
class MtaskViewSet(CustomModelViewSet):
|
||||||
"""
|
"""
|
||||||
list: 生产任务
|
list: 生产任务
|
||||||
|
|
@ -19,11 +23,11 @@ class MtaskViewSet(CustomModelViewSet):
|
||||||
queryset = Mtask.objects.all()
|
queryset = Mtask.objects.all()
|
||||||
serializer_class = MtaskSerializer
|
serializer_class = MtaskSerializer
|
||||||
filterset_class = MtaskFilter
|
filterset_class = MtaskFilter
|
||||||
select_related_fields = ['material', 'process', 'order']
|
select_related_fields = ['material', 'mgroup']
|
||||||
ordering_fields = ['start_date', 'process__sort']
|
ordering_fields = ['start_date', 'mgroup__process__sort']
|
||||||
ordering = ['process__sort', '-start_date', '-create_time']
|
ordering = ['mgroup__process__sort', '-start_date', '-create_time']
|
||||||
|
|
||||||
@action(methods=['post'], detail=False, perms_map={'post': 'mtasks.schedue'}, serializer_class=SchedueSerializer)
|
@action(methods=['post'], detail=False, perms_map={'post': 'mtask.schedue'}, serializer_class=SchedueSerializer)
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def schedue_from_orderitems(self, request, *args, **kwargs):
|
def schedue_from_orderitems(self, request, *args, **kwargs):
|
||||||
"""从多个订单明细自动排产
|
"""从多个订单明细自动排产
|
||||||
|
|
@ -33,7 +37,8 @@ class MtaskViewSet(CustomModelViewSet):
|
||||||
sr = SchedueSerializer(data=request.data)
|
sr = SchedueSerializer(data=request.data)
|
||||||
sr.is_valid(raise_exception=True)
|
sr.is_valid(raise_exception=True)
|
||||||
vdata = sr.validated_data
|
vdata = sr.validated_data
|
||||||
PmService.schedue_from_orderitems(vdata['orderitems'], vdata['start_date'], vdata.get('end_date', None))
|
PmService.schedue_from_orderitems(request.user,
|
||||||
|
vdata['orderitems'], vdata['start_date'], vdata.get('end_date', None))
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
def perform_destroy(self, instance):
|
def perform_destroy(self, instance):
|
||||||
|
|
@ -41,3 +46,15 @@ class MtaskViewSet(CustomModelViewSet):
|
||||||
raise ParseError('该任务非创建中不可删除')
|
raise ParseError('该任务非创建中不可删除')
|
||||||
return super().perform_destroy(instance)
|
return super().perform_destroy(instance)
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=False, perms_map={'post': 'mtask.asgin'}, serializer_class=PkSerializer)
|
||||||
|
@transaction.atomic
|
||||||
|
def assgin(self, request):
|
||||||
|
"""下达任务
|
||||||
|
|
||||||
|
下达任务
|
||||||
|
"""
|
||||||
|
ids = request.data.get('ids', [])
|
||||||
|
mtasks = Mtask.objects.filter(
|
||||||
|
id__in=ids, parent=None, state=Mtask.MTASK_CREATED)
|
||||||
|
mtasks.update(state=Mtask.MTASK_ASSGINED)
|
||||||
|
return Response()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue