forked from erp-dev/erp
fin
This commit is contained in:
@@ -4,6 +4,7 @@ from datetime import timedelta
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from basic_info.models import Employee, EmployeeStatusEnum, EmployeeType
|
||||
from mission.models import Mission, MissionCategory, MissionParticipant, MissionReply
|
||||
from mission.signals import (
|
||||
mission_cancelled,
|
||||
@@ -104,11 +105,30 @@ def _clear_unreplied_notification_state_if_active(*, mission: Mission) -> None:
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def set_mission_participants(*, mission: Mission, participant_ids: list[int]) -> Mission:
|
||||
def set_mission_participants(
|
||||
*,
|
||||
mission: Mission,
|
||||
participant_ids: list[int] | None = None,
|
||||
employee_type_ids: list[int] | None = None,
|
||||
) -> Mission:
|
||||
mission = Mission.objects.select_for_update().get(pk=mission.pk)
|
||||
participant_ids = list(dict.fromkeys(participant_ids or []))
|
||||
employee_type_ids = list(dict.fromkeys(employee_type_ids or []))
|
||||
|
||||
from basic_info.models import Employee
|
||||
if employee_type_ids:
|
||||
employee_type_count = EmployeeType.objects.filter(
|
||||
id__in=employee_type_ids,
|
||||
merchant=mission.merchant,
|
||||
).count()
|
||||
if employee_type_count != len(employee_type_ids):
|
||||
raise ValueError("员工职位不存在或不属于任务所属商户")
|
||||
|
||||
position_employee_ids = Employee.objects.filter(
|
||||
merchant=mission.merchant,
|
||||
position_id__in=employee_type_ids,
|
||||
status=EmployeeStatusEnum.ACTIVE,
|
||||
).values_list("id", flat=True)
|
||||
participant_ids = list(dict.fromkeys([*participant_ids, *position_employee_ids]))
|
||||
|
||||
employees = list(Employee.objects.filter(id__in=participant_ids, merchant=mission.merchant))
|
||||
if len(employees) != len(participant_ids):
|
||||
@@ -138,6 +158,7 @@ def create_mission(
|
||||
content_type=None,
|
||||
content_id: int | None = None,
|
||||
participant_ids: list[int] | None = None,
|
||||
employee_type_ids: list[int] | None = None,
|
||||
notify_if_unreplied: bool = False,
|
||||
unreplied_notify_interval_minutes: int | None = None,
|
||||
unreplied_notify_max_count: int = 5,
|
||||
@@ -166,8 +187,12 @@ def create_mission(
|
||||
unreplied_notify_interval_minutes=unreplied_notify_interval_minutes,
|
||||
unreplied_notify_max_count=unreplied_notify_max_count,
|
||||
)
|
||||
if participant_ids is not None:
|
||||
set_mission_participants(mission=mission, participant_ids=participant_ids)
|
||||
if participant_ids is not None or employee_type_ids is not None:
|
||||
set_mission_participants(
|
||||
mission=mission,
|
||||
participant_ids=participant_ids,
|
||||
employee_type_ids=employee_type_ids,
|
||||
)
|
||||
_send_signal_on_commit(
|
||||
mission_created,
|
||||
sender=Mission,
|
||||
@@ -188,6 +213,7 @@ def update_mission(
|
||||
content_id: int | None = None,
|
||||
update_content_object: bool = False,
|
||||
participant_ids: list[int] | None = None,
|
||||
employee_type_ids: list[int] | None = None,
|
||||
notify_if_unreplied=UNSET,
|
||||
unreplied_notify_interval_minutes=UNSET,
|
||||
unreplied_notify_max_count=UNSET,
|
||||
@@ -266,8 +292,12 @@ def update_mission(
|
||||
|
||||
if update_fields:
|
||||
mission.save(update_fields=[*dict.fromkeys(update_fields), "updated_at"])
|
||||
if participant_ids is not None:
|
||||
set_mission_participants(mission=mission, participant_ids=participant_ids)
|
||||
if participant_ids is not None or employee_type_ids is not None:
|
||||
set_mission_participants(
|
||||
mission=mission,
|
||||
participant_ids=participant_ids,
|
||||
employee_type_ids=employee_type_ids,
|
||||
)
|
||||
return mission
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
from django.utils import timezone
|
||||
|
||||
from basic_info.models import Employee, Merchant, MerchantTypeEnum
|
||||
from basic_info.models import Employee, EmployeeStatusEnum, EmployeeType, Merchant, MerchantTypeEnum
|
||||
from mission.models import Mission, MissionCategory, MissionParticipant, MissionReply
|
||||
from mission.services import (
|
||||
cancel_mission,
|
||||
@@ -25,6 +25,7 @@ from mission.signals import (
|
||||
mission_reply_rejected,
|
||||
)
|
||||
from mission.payload_processors import _split_structured_description
|
||||
from api_v2.views.mission import MissionWriteSerializer
|
||||
from notifier.models import NotificationEventKeyEnum, Notifier, NotifierChannelEnum, NotifierRoute
|
||||
from notifier.services import dispatch_notification_event
|
||||
|
||||
@@ -36,8 +37,22 @@ class MissionModelTestCase(TestCase):
|
||||
self.default_category = MissionCategory.objects.create(merchant=self.merchant, name="通用")
|
||||
self.custom_category = MissionCategory.objects.create(merchant=self.merchant, name="售后")
|
||||
self.other_category = MissionCategory.objects.create(merchant=self.other_merchant, name="通用")
|
||||
self.printer_type = EmployeeType.objects.create(merchant=self.merchant, title="打纸")
|
||||
self.empty_type = EmployeeType.objects.create(merchant=self.merchant, title="空职位")
|
||||
self.other_type = EmployeeType.objects.create(merchant=self.other_merchant, title="外部职位")
|
||||
self.creator = Employee.objects.create(merchant=self.merchant, name="创建者")
|
||||
self.responder = Employee.objects.create(merchant=self.merchant, name="回应者")
|
||||
self.position_employee = Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
name="职位参与者",
|
||||
position=self.printer_type,
|
||||
)
|
||||
self.inactive_position_employee = Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
name="离职职位参与者",
|
||||
position=self.printer_type,
|
||||
status=EmployeeStatusEnum.INACTIVE,
|
||||
)
|
||||
self.other_employee = Employee.objects.create(merchant=self.other_merchant, name="外部员工")
|
||||
self.mission = Mission.objects.create(
|
||||
merchant=self.merchant,
|
||||
@@ -165,6 +180,32 @@ class MissionModelTestCase(TestCase):
|
||||
self.assertFalse(mission.is_urgent)
|
||||
self.assertEqual(list(mission.participants.values_list("employee_id", flat=True)), [self.responder.id])
|
||||
|
||||
def test_create_mission_adds_active_employees_from_employee_types(self):
|
||||
mission = create_mission(
|
||||
creator=self.creator,
|
||||
description="按职位添加参与者",
|
||||
participant_ids=[self.responder.id, self.position_employee.id],
|
||||
employee_type_ids=[self.printer_type.id, self.empty_type.id],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
set(mission.participants.values_list("employee_id", flat=True)),
|
||||
{self.responder.id, self.position_employee.id},
|
||||
)
|
||||
|
||||
def test_mission_write_serializer_accepts_employee_type_ids(self):
|
||||
serializer = MissionWriteSerializer(
|
||||
data={
|
||||
"description": "接口创建任务",
|
||||
"employee_type_ids": [self.printer_type.id],
|
||||
},
|
||||
is_create=True,
|
||||
context={"employee": self.creator},
|
||||
)
|
||||
|
||||
self.assertTrue(serializer.is_valid(), serializer.errors)
|
||||
self.assertEqual(serializer.validated_data["employee_type_ids"], [self.printer_type.id])
|
||||
|
||||
def test_create_mission_supports_unreplied_notification_fields(self):
|
||||
mission = create_mission(
|
||||
creator=self.creator,
|
||||
@@ -249,6 +290,24 @@ class MissionModelTestCase(TestCase):
|
||||
self.assertEqual(self.mission.content_id, related.id)
|
||||
self.assertEqual(list(self.mission.participants.values_list("employee_id", flat=True)), [self.responder.id])
|
||||
|
||||
def test_update_mission_sets_participants_from_employee_types(self):
|
||||
MissionParticipant.objects.create(
|
||||
merchant=self.merchant,
|
||||
mission=self.mission,
|
||||
employee=self.responder,
|
||||
)
|
||||
|
||||
update_mission(
|
||||
mission=self.mission,
|
||||
updated_by=self.creator,
|
||||
employee_type_ids=[self.printer_type.id],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
list(self.mission.participants.values_list("employee_id", flat=True)),
|
||||
[self.position_employee.id],
|
||||
)
|
||||
|
||||
def test_update_mission_updates_unreplied_notification_fields(self):
|
||||
update_mission(
|
||||
mission=self.mission,
|
||||
@@ -286,6 +345,13 @@ class MissionModelTestCase(TestCase):
|
||||
participant_ids=[self.other_employee.id],
|
||||
)
|
||||
|
||||
def test_set_participants_rejects_cross_merchant_employee_type(self):
|
||||
with self.assertRaises(ValueError):
|
||||
set_mission_participants(
|
||||
mission=self.mission,
|
||||
employee_type_ids=[self.other_type.id],
|
||||
)
|
||||
|
||||
def test_update_mission_rejects_cross_merchant_category(self):
|
||||
with self.assertRaises(ValueError):
|
||||
update_mission(
|
||||
|
||||
Reference in New Issue
Block a user