feat: wpm中增加到岗记录
This commit is contained in:
parent
cbce572350
commit
fddadfe30c
|
@ -57,6 +57,7 @@ class PmService:
|
||||||
"""
|
"""
|
||||||
从大任务自动排产出小任务
|
从大任务自动排产出小任务
|
||||||
"""
|
"""
|
||||||
|
from apps.wpm.services import make_sflogs
|
||||||
if utask.state != Utask.UTASK_CREATED:
|
if utask.state != Utask.UTASK_CREATED:
|
||||||
raise ParseError('任务状态异常')
|
raise ParseError('任务状态异常')
|
||||||
utask.state = Utask.UTASK_DECOMPOSE
|
utask.state = Utask.UTASK_DECOMPOSE
|
||||||
|
@ -82,6 +83,8 @@ class PmService:
|
||||||
'update_by': user,
|
'update_by': user,
|
||||||
'is_count_utask': True
|
'is_count_utask': True
|
||||||
})
|
})
|
||||||
|
make_sflogs(mgroup=utask.mgroup,
|
||||||
|
start_date=task_date, end_date=task_date, create_by=user)
|
||||||
else:
|
else:
|
||||||
# 获取产品的加工路线
|
# 获取产品的加工路线
|
||||||
rqs = Route.get_routes(product)
|
rqs = Route.get_routes(product)
|
||||||
|
@ -131,6 +134,8 @@ class PmService:
|
||||||
'update_by': user,
|
'update_by': user,
|
||||||
'is_count_utask': val.is_count_utask
|
'is_count_utask': val.is_count_utask
|
||||||
})
|
})
|
||||||
|
make_sflogs(mgroup=mgroup,
|
||||||
|
start_date=task_date, end_date=task_date, create_by=user)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_orderitems(cls, orderitems: QuerySet[OrderItem]):
|
def check_orderitems(cls, orderitems: QuerySet[OrderItem]):
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Generated by Django 3.2.12 on 2023-11-20 08:23
|
||||||
|
|
||||||
|
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),
|
||||||
|
('system', '0002_myschedule'),
|
||||||
|
('wpm', '0031_alter_mlog_mtask'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sflog',
|
||||||
|
name='work_date',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='值班日期'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AttLog',
|
||||||
|
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='删除标记')),
|
||||||
|
('state', models.CharField(choices=[('pending', '待定'), ('normal', '正常'), ('late', '迟到'), ('early_leave', '早退'), ('absent', '未到岗'), ('leave', '请假')], default='pending', help_text="[('pending', '待定'), ('normal', '正常'), ('late', '迟到'), ('early_leave', '早退'), ('absent', '未到岗'), ('leave', '请假')]", max_length=20, verbose_name='状态')),
|
||||||
|
('note', models.TextField(blank=True, default='', verbose_name='备注信息')),
|
||||||
|
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='attlog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||||
|
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='system.post', verbose_name='岗位')),
|
||||||
|
('sflog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.sflog', verbose_name='关联值班记录')),
|
||||||
|
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='attlog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='到岗人')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -32,6 +32,7 @@ class SfLog(CommonADModel):
|
||||||
Shift, verbose_name='当班班次', on_delete=models.CASCADE)
|
Shift, verbose_name='当班班次', on_delete=models.CASCADE)
|
||||||
leader = models.ForeignKey(
|
leader = models.ForeignKey(
|
||||||
'system.user', verbose_name='班长', on_delete=models.CASCADE, null=True, blank=True)
|
'system.user', verbose_name='班长', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
work_date = models.DateField('值班日期', null=True, blank=True)
|
||||||
start_time = models.DateTimeField('值班开始')
|
start_time = models.DateTimeField('值班开始')
|
||||||
end_time = models.DateTimeField('值班结束')
|
end_time = models.DateTimeField('值班结束')
|
||||||
note = models.TextField('其他备注', null=True, blank=True)
|
note = models.TextField('其他备注', null=True, blank=True)
|
||||||
|
@ -179,3 +180,27 @@ class Handover(CommonADModel):
|
||||||
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
||||||
submit_user = models.ForeignKey(
|
submit_user = models.ForeignKey(
|
||||||
User, verbose_name='提交人', on_delete=models.CASCADE, null=True, blank=True, related_name='handover_submit_user')
|
User, verbose_name='提交人', on_delete=models.CASCADE, null=True, blank=True, related_name='handover_submit_user')
|
||||||
|
|
||||||
|
|
||||||
|
class AttLog(CommonADModel):
|
||||||
|
"""
|
||||||
|
到岗记录
|
||||||
|
"""
|
||||||
|
ATT_STATE_CHOICES = [
|
||||||
|
('pending', '待定'),
|
||||||
|
('normal', '正常'),
|
||||||
|
('late', '迟到'),
|
||||||
|
('early_leave', '早退'),
|
||||||
|
('absent', '未到岗'),
|
||||||
|
('leave', '请假'),
|
||||||
|
# 可以根据需要添加更多状态
|
||||||
|
]
|
||||||
|
sflog = models.ForeignKey(
|
||||||
|
SfLog, verbose_name='关联值班记录', on_delete=models.CASCADE)
|
||||||
|
user = models.ForeignKey(
|
||||||
|
'system.user', verbose_name='到岗人', on_delete=models.CASCADE)
|
||||||
|
post = models.ForeignKey(
|
||||||
|
'system.post', verbose_name='岗位', on_delete=models.CASCADE)
|
||||||
|
state = models.CharField('状态', max_length=20,
|
||||||
|
choices=ATT_STATE_CHOICES, default='pending', help_text=str(ATT_STATE_CHOICES))
|
||||||
|
note = models.TextField('备注信息', default='', blank=True)
|
||||||
|
|
|
@ -3,13 +3,15 @@ from apps.utils.serializers import CustomModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from .models import SfLog, StLog, SfLogExp, WMaterial, Mlog, Handover, Mlogb
|
from .models import SfLog, StLog, SfLogExp, WMaterial, Mlog, Handover, Mlogb, AttLog
|
||||||
from apps.system.models import Dept, User
|
from apps.system.models import Dept, User
|
||||||
from apps.system.serializers import UserSimpleSerializer
|
from apps.system.serializers import UserSimpleSerializer
|
||||||
from apps.pm.models import Mtask
|
from apps.pm.models import Mtask
|
||||||
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.models import Mgroup, TeamMember, Shift
|
||||||
from apps.mtm.serializers import MaterialSimpleSerializer
|
from apps.mtm.serializers import MaterialSimpleSerializer
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
class StLogSerializer(CustomModelSerializer):
|
class StLogSerializer(CustomModelSerializer):
|
||||||
|
@ -37,16 +39,26 @@ class SfLogSerializer(CustomModelSerializer):
|
||||||
}
|
}
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
old_pcoal_heat = instance.pcoal_heat
|
with transaction.atomic():
|
||||||
old_team = instance.team
|
old_pcoal_heat = instance.pcoal_heat
|
||||||
instance = super().update(instance, validated_data)
|
old_team = instance.team
|
||||||
new_pcoal_heat = instance.pcoal_heat
|
instance: SfLog = super().update(instance, validated_data)
|
||||||
new_team = instance.team
|
new_pcoal_heat = instance.pcoal_heat
|
||||||
if new_pcoal_heat != old_pcoal_heat:
|
new_team = instance.team
|
||||||
cal_enstat_when_pcoal_heat_change.delay(instance.id)
|
mgroup: Mgroup = instance.mgroup
|
||||||
if new_team != old_team:
|
if new_pcoal_heat != old_pcoal_heat and mgroup.need_enm:
|
||||||
cal_enstat_when_team_change.delay(instance.id)
|
cal_enstat_when_pcoal_heat_change.delay(instance.id)
|
||||||
return instance
|
if new_team != old_team:
|
||||||
|
default_state = 'pending'
|
||||||
|
if timezone.now() > instance.end_time:
|
||||||
|
default_state = 'normal'
|
||||||
|
# 分配班组时创建人员到岗情况
|
||||||
|
for item in TeamMember.objects.filter(team=new_team, mgroup=instance.mgroup):
|
||||||
|
AttLog.objects.get_or_create(sflog=instance, user=item.user, defaults={
|
||||||
|
'sflog': instance, 'user': item.user, 'post': item.post, 'state': default_state, 'create_by': self.context['request'].user})
|
||||||
|
if mgroup.need_enm:
|
||||||
|
cal_enstat_when_team_change.delay(instance.id)
|
||||||
|
return instance
|
||||||
# def to_internal_value(self, data):
|
# def to_internal_value(self, data):
|
||||||
# if hasattr(self.Meta, 'update_fields') and self.context['request'].method in ['PUT', 'PATCH']:
|
# if hasattr(self.Meta, 'update_fields') and self.context['request'].method in ['PUT', 'PATCH']:
|
||||||
# u_fields = self.Meta.update_fields
|
# u_fields = self.Meta.update_fields
|
||||||
|
@ -111,6 +123,8 @@ class MlogSerializer(CustomModelSerializer):
|
||||||
source='equipment.name', read_only=True)
|
source='equipment.name', read_only=True)
|
||||||
equipment_2_name = serializers.CharField(
|
equipment_2_name = serializers.CharField(
|
||||||
source='equipment_2.name', read_only=True)
|
source='equipment_2.name', read_only=True)
|
||||||
|
shift = serializers.PrimaryKeyRelatedField(
|
||||||
|
label='班次ID', queryset=Shift.objects.all(), required=True)
|
||||||
shift_name = serializers.CharField(source='shift.name', read_only=True)
|
shift_name = serializers.CharField(source='shift.name', read_only=True)
|
||||||
mlogb = MlogbSerializer(
|
mlogb = MlogbSerializer(
|
||||||
label='多产出件信息', many=True, required=False)
|
label='多产出件信息', many=True, required=False)
|
||||||
|
@ -251,3 +265,16 @@ class MlogAnaSerializer(serializers.Serializer):
|
||||||
start_date = serializers.DateField(label='开始日期', required=True)
|
start_date = serializers.DateField(label='开始日期', required=True)
|
||||||
end_date = serializers.DateField(label='结束日期', required=True)
|
end_date = serializers.DateField(label='结束日期', required=True)
|
||||||
material_cate = serializers.CharField(label='物料系列', required=False)
|
material_cate = serializers.CharField(label='物料系列', required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AttLogSerializer(CustomModelSerializer):
|
||||||
|
mgroup_name = serializers.CharField(
|
||||||
|
source='sflog.mgroup.name', read_only=True)
|
||||||
|
user_name = serializers.CharField(source='user.name', read_only=True)
|
||||||
|
post_name = serializers.CharField(source='post.name', read_only=True)
|
||||||
|
belong_dept_name = serializers.CharField(
|
||||||
|
source='sflog.mgroup.belong_dept.name', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AttLog
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
@ -16,8 +16,10 @@ from apps.mtm.models import Mgroup, Shift, Material, Route
|
||||||
from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover
|
from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover
|
||||||
|
|
||||||
|
|
||||||
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, create_by=None):
|
||||||
for shift in Shift.objects.all():
|
shift_rule = mgroup.shift_rule
|
||||||
|
shifts = Shift.objects.filter(rule=shift_rule) # 根据排班规则制定排班记录
|
||||||
|
for shift in shifts:
|
||||||
start_time_o = shift.start_time_o
|
start_time_o = shift.start_time_o
|
||||||
end_time_o = shift.end_time_o
|
end_time_o = shift.end_time_o
|
||||||
current_date = start_date
|
current_date = start_date
|
||||||
|
@ -26,12 +28,16 @@ def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.da
|
||||||
end_time = datetime.datetime.combine(current_date, end_time_o)
|
end_time = datetime.datetime.combine(current_date, end_time_o)
|
||||||
if start_time > end_time:
|
if start_time > end_time:
|
||||||
start_time -= datetime.timedelta(days=1)
|
start_time -= datetime.timedelta(days=1)
|
||||||
|
duration = end_time - start_time
|
||||||
|
total_hours_now = duration.total_seconds() / 3600
|
||||||
SfLog.objects.get_or_create(mgroup=mgroup, shift=shift, start_time=start_time, defaults={
|
SfLog.objects.get_or_create(mgroup=mgroup, shift=shift, start_time=start_time, defaults={
|
||||||
"mgroup": mgroup,
|
"mgroup": mgroup,
|
||||||
"shift": shift,
|
"shift": shift,
|
||||||
|
"work_date": current_date,
|
||||||
"start_time": start_time,
|
"start_time": start_time,
|
||||||
"end_time": end_time,
|
"end_time": end_time,
|
||||||
"total_hour_now": 12
|
"total_hour_now": total_hours_now,
|
||||||
|
"create_by": create_by
|
||||||
})
|
})
|
||||||
current_date = current_date + datetime.timedelta(days=1)
|
current_date = current_date + datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ def get_total_hour_now(sflogId: str):
|
||||||
if sflogId:
|
if sflogId:
|
||||||
sflog = SfLog.objects.get(id=sflogId)
|
sflog = SfLog.objects.get(id=sflogId)
|
||||||
if sflog.end_time <= now:
|
if sflog.end_time <= now:
|
||||||
sflog.total_hour_now = 12 # 写死的数据不是很好
|
sflog.total_hour_now = (
|
||||||
|
sflog.end_time-sflog.start_time).total_seconds()/3600 # 写死的数据不是很好
|
||||||
else:
|
else:
|
||||||
total_hour_now = (now-sflog.start_time).total_seconds()/3600
|
total_hour_now = (now-sflog.start_time).total_seconds()/3600
|
||||||
sflog.total_hour_now = total_hour_now if total_hour_now > 0 else 0
|
sflog.total_hour_now = total_hour_now if total_hour_now > 0 else 0
|
||||||
|
|
|
@ -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, MlogViewSet, HandoverViewSet
|
from apps.wpm.views import SfLogViewSet, StLogViewSet, SfLogExpViewSet, WMaterialViewSet, MlogViewSet, HandoverViewSet, AttlogViewSet
|
||||||
|
|
||||||
|
|
||||||
API_BASE_URL = 'api/wpm/'
|
API_BASE_URL = 'api/wpm/'
|
||||||
|
@ -14,6 +14,7 @@ router.register('sflogexp', SfLogExpViewSet, basename='sflogexp')
|
||||||
router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
|
router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
|
||||||
router.register('mlog', MlogViewSet, basename='mlog')
|
router.register('mlog', MlogViewSet, basename='mlog')
|
||||||
router.register('handover', HandoverViewSet, basename='handover')
|
router.register('handover', HandoverViewSet, basename='handover')
|
||||||
|
router.register('attlog', AttlogViewSet, basename='attlog')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(API_BASE_URL, include(router.urls)),
|
path(API_BASE_URL, include(router.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,10 +14,10 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
|
||||||
from apps.utils.mixins import BulkCreateModelMixin
|
from apps.utils.mixins import BulkCreateModelMixin
|
||||||
|
|
||||||
from .filters import SfLogExpFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter
|
from .filters import SfLogExpFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter
|
||||||
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb
|
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog
|
||||||
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer,
|
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer,
|
||||||
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
|
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
|
||||||
GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer)
|
GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer, AttLogSerializer)
|
||||||
from .services import mlog_submit, update_mtask, handover_submit
|
from .services import mlog_submit, update_mtask, handover_submit
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
@ -304,3 +304,12 @@ class HandoverViewSet(CustomModelViewSet):
|
||||||
create_by=user
|
create_by=user
|
||||||
)
|
)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
|
class AttlogViewSet(CustomModelViewSet):
|
||||||
|
queryset = AttLog.objects.all()
|
||||||
|
serializer_class = AttLogSerializer
|
||||||
|
select_related_fields = ['user', 'post', 'sflog']
|
||||||
|
filterset_fields = ['sflog__mgroup',
|
||||||
|
'sflog__mgroup__belong_dept__name', 'sflog__work_date', 'sflog__mgroup__cate', 'sflog__mgroup__need_enm']
|
||||||
|
ordering = ['-sflog__work_date', 'create_time']
|
||||||
|
|
Loading…
Reference in New Issue