diff --git a/apps/pm/services.py b/apps/pm/services.py index 5c284c33..318f5fb5 100644 --- a/apps/pm/services.py +++ b/apps/pm/services.py @@ -57,6 +57,7 @@ class PmService: """ 从大任务自动排产出小任务 """ + from apps.wpm.services import make_sflogs if utask.state != Utask.UTASK_CREATED: raise ParseError('任务状态异常') utask.state = Utask.UTASK_DECOMPOSE @@ -82,6 +83,8 @@ class PmService: 'update_by': user, 'is_count_utask': True }) + make_sflogs(mgroup=utask.mgroup, + start_date=task_date, end_date=task_date, create_by=user) else: # 获取产品的加工路线 rqs = Route.get_routes(product) @@ -131,6 +134,8 @@ class PmService: 'update_by': user, 'is_count_utask': val.is_count_utask }) + make_sflogs(mgroup=mgroup, + start_date=task_date, end_date=task_date, create_by=user) @classmethod def check_orderitems(cls, orderitems: QuerySet[OrderItem]): diff --git a/apps/wpm/migrations/0032_auto_20231120_1623.py b/apps/wpm/migrations/0032_auto_20231120_1623.py new file mode 100644 index 00000000..d93575fd --- /dev/null +++ b/apps/wpm/migrations/0032_auto_20231120_1623.py @@ -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, + }, + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 1c45d8c0..9d8e2fae 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -32,6 +32,7 @@ class SfLog(CommonADModel): Shift, verbose_name='当班班次', on_delete=models.CASCADE) leader = models.ForeignKey( 'system.user', verbose_name='班长', on_delete=models.CASCADE, null=True, blank=True) + work_date = models.DateField('值班日期', null=True, blank=True) start_time = models.DateTimeField('值班开始') end_time = models.DateTimeField('值班结束') note = models.TextField('其他备注', null=True, blank=True) @@ -179,3 +180,27 @@ class Handover(CommonADModel): submit_time = models.DateTimeField('提交时间', null=True, blank=True) submit_user = models.ForeignKey( 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) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 73240789..435d817e 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -3,13 +3,15 @@ from apps.utils.serializers import CustomModelSerializer from rest_framework import serializers 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.serializers import UserSimpleSerializer from apps.pm.models import Mtask 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 django.db import transaction +from django.utils import timezone class StLogSerializer(CustomModelSerializer): @@ -37,16 +39,26 @@ class SfLogSerializer(CustomModelSerializer): } def update(self, instance, validated_data): - old_pcoal_heat = instance.pcoal_heat - old_team = instance.team - instance = super().update(instance, validated_data) - new_pcoal_heat = instance.pcoal_heat - new_team = instance.team - if new_pcoal_heat != old_pcoal_heat: - cal_enstat_when_pcoal_heat_change.delay(instance.id) - if new_team != old_team: - cal_enstat_when_team_change.delay(instance.id) - return instance + with transaction.atomic(): + old_pcoal_heat = instance.pcoal_heat + old_team = instance.team + instance: SfLog = super().update(instance, validated_data) + new_pcoal_heat = instance.pcoal_heat + new_team = instance.team + mgroup: Mgroup = instance.mgroup + if new_pcoal_heat != old_pcoal_heat and mgroup.need_enm: + cal_enstat_when_pcoal_heat_change.delay(instance.id) + 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): # if hasattr(self.Meta, 'update_fields') and self.context['request'].method in ['PUT', 'PATCH']: # u_fields = self.Meta.update_fields @@ -111,6 +123,8 @@ class MlogSerializer(CustomModelSerializer): source='equipment.name', read_only=True) equipment_2_name = serializers.CharField( 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) mlogb = MlogbSerializer( label='多产出件信息', many=True, required=False) @@ -251,3 +265,16 @@ class MlogAnaSerializer(serializers.Serializer): start_date = serializers.DateField(label='开始日期', required=True) end_date = serializers.DateField(label='结束日期', required=True) 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__' diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 924a40a6..f4f8777b 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -16,8 +16,10 @@ from apps.mtm.models import Mgroup, Shift, Material, Route from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover -def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.date): - for shift in Shift.objects.all(): +def make_sflogs(mgroup: Mgroup, start_date: datetime.date, end_date: datetime.date, create_by=None): + shift_rule = mgroup.shift_rule + shifts = Shift.objects.filter(rule=shift_rule) # 根据排班规则制定排班记录 + for shift in shifts: start_time_o = shift.start_time_o end_time_o = shift.end_time_o 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) if start_time > end_time: 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={ "mgroup": mgroup, "shift": shift, + "work_date": current_date, "start_time": start_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) diff --git a/apps/wpm/tasks.py b/apps/wpm/tasks.py index 3cf4c407..4dd41757 100644 --- a/apps/wpm/tasks.py +++ b/apps/wpm/tasks.py @@ -41,7 +41,8 @@ def get_total_hour_now(sflogId: str): if sflogId: sflog = SfLog.objects.get(id=sflogId) if sflog.end_time <= now: - sflog.total_hour_now = 12 # 写死的数据不是很好 + sflog.total_hour_now = ( + sflog.end_time-sflog.start_time).total_seconds()/3600 # 写死的数据不是很好 else: total_hour_now = (now-sflog.start_time).total_seconds()/3600 sflog.total_hour_now = total_hour_now if total_hour_now > 0 else 0 diff --git a/apps/wpm/urls.py b/apps/wpm/urls.py index 58c2d75b..89539afe 100644 --- a/apps/wpm/urls.py +++ b/apps/wpm/urls.py @@ -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, MlogViewSet, HandoverViewSet +from apps.wpm.views import SfLogViewSet, StLogViewSet, SfLogExpViewSet, WMaterialViewSet, MlogViewSet, HandoverViewSet, AttlogViewSet API_BASE_URL = 'api/wpm/' @@ -14,6 +14,7 @@ router.register('sflogexp', SfLogExpViewSet, basename='sflogexp') router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('mlog', MlogViewSet, basename='mlog') router.register('handover', HandoverViewSet, basename='handover') +router.register('attlog', AttlogViewSet, basename='attlog') urlpatterns = [ path(API_BASE_URL, include(router.urls)), ] diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 4e706685..04b4dded 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -14,10 +14,10 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.utils.mixins import BulkCreateModelMixin 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, MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, - GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer) + GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer, AttLogSerializer) from .services import mlog_submit, update_mtask, handover_submit # Create your views here. @@ -304,3 +304,12 @@ class HandoverViewSet(CustomModelViewSet): create_by=user ) 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']