1
0
forked from erp-dev/erp

feat: mes module

This commit is contained in:
2026-05-11 21:37:41 +08:00
parent d7b388e935
commit 9359013b4c
20 changed files with 3232 additions and 133 deletions

View File

@@ -15,6 +15,7 @@ from api_v2.views.printing import PrintingJobByCustomerView
from django.contrib.contenttypes.models import ContentType
from stateflow import models as stateflow_models
from django.test.utils import override_settings
from mes import models as mes_models
class QuickCreateEmployeeUserAPITest(TestCase):
@@ -2292,3 +2293,222 @@ class AgentTransportVehicleAPITest(TestCase):
url = f'/api/v2/ai/transport-vehicles/{self.vehicle_a.license_plate}/'
response = self.client.get(url, {'merchant_id': self.merchant.id})
self.assertEqual(response.status_code, 401)
@override_settings(AGENT_ACCESS_KEY='agent-test-key')
class AgentMesAPITest(TestCase):
def setUp(self):
self.client = APIClient()
self.device_list_url = '/api/v2/ai/mes/devices/'
self.assignment_list_url = '/api/v2/ai/mes/production-assignments/'
self.merchant = basic_models.Merchant.objects.create(
name='MES Agent 商户',
type=basic_models.MerchantTypeEnum.FACTORY,
)
self.other_merchant = basic_models.Merchant.objects.create(
name='其他 MES 商户',
type=basic_models.MerchantTypeEnum.FACTORY,
)
self.user = get_user_model().objects.create_user(username='agent-mes-user', password='pass12345')
self.other_user = get_user_model().objects.create_user(username='agent-mes-other-user', password='pass12345')
self.employee = basic_models.Employee.objects.create(merchant=self.merchant, sys_user=self.user, name='MES员工')
self.other_employee = basic_models.Employee.objects.create(merchant=self.other_merchant, sys_user=self.other_user, name='其他MES员工')
self.category = mes_models.DeviceCategory.objects.create(
merchant=self.merchant,
name='打印机',
created_by=self.user,
operator=self.employee,
)
self.device_a = mes_models.Device.objects.create(
merchant=self.merchant,
category=self.category,
name='设备A',
peak_capacity=120,
capacity_unit=mes_models.CapacityUnitEnum.METER,
extra={'capacity': {'per_hour': 120}},
created_by=self.user,
operator=self.employee,
)
self.device_b = mes_models.Device.objects.create(
merchant=self.merchant,
category=self.category,
name='设备B',
peak_capacity=90,
capacity_unit=mes_models.CapacityUnitEnum.YARD,
created_by=self.user,
operator=self.employee,
)
self.other_category = mes_models.DeviceCategory.objects.create(
merchant=self.other_merchant,
name='他商户分类',
created_by=self.other_user,
operator=self.other_employee,
)
self.other_device = mes_models.Device.objects.create(
merchant=self.other_merchant,
category=self.other_category,
name='他商户设备',
peak_capacity=60,
created_by=self.other_user,
operator=self.other_employee,
)
self.customer = basic_models.Customer.objects.create(
merchant=self.merchant,
name='测试客户',
mobile='13800138011',
)
self.other_customer = basic_models.Customer.objects.create(
merchant=self.other_merchant,
name='他商户客户',
mobile='13800138012',
)
self.printing_order = printing_models.PrintingOrder.objects.create(
merchant=self.merchant,
customer=self.customer,
fabric='测试面料',
width='150cm',
created_by=self.user,
)
self.other_printing_order = printing_models.PrintingOrder.objects.create(
merchant=self.other_merchant,
customer=self.other_customer,
fabric='其他面料',
width='160cm',
created_by=self.other_user,
)
self.content_type = ContentType.objects.get_for_model(printing_models.PrintingOrder)
self.assignment_published = mes_models.ProductionAssignment.objects.create(
merchant=self.merchant,
device=self.device_a,
content_type=self.content_type,
object_id=self.printing_order.id,
assigner=self.employee,
assignee=self.employee,
production_quantity=300,
status=mes_models.ProductionAssignmentStatusEnum.PUBLISHED,
created_by=self.user,
operator=self.employee,
)
self.assignment_draft = mes_models.ProductionAssignment.objects.create(
merchant=self.merchant,
device=self.device_b,
content_type=self.content_type,
object_id=self.printing_order.id,
assigner=self.employee,
production_quantity=100,
status=mes_models.ProductionAssignmentStatusEnum.DRAFT,
created_by=self.user,
operator=self.employee,
)
self.other_assignment = mes_models.ProductionAssignment.objects.create(
merchant=self.other_merchant,
device=self.other_device,
content_type=self.content_type,
object_id=self.other_printing_order.id,
assigner=self.other_employee,
production_quantity=50,
status=mes_models.ProductionAssignmentStatusEnum.PUBLISHED,
created_by=self.other_user,
operator=self.other_employee,
)
mes_models.ProductionAssignment.objects.filter(id=self.assignment_published.id).update(
created_at=timezone.make_aware(datetime.datetime(2026, 5, 1, 10, 0, 0))
)
mes_models.ProductionAssignment.objects.filter(id=self.assignment_draft.id).update(
created_at=timezone.make_aware(datetime.datetime(2026, 5, 3, 12, 0, 0))
)
mes_models.ProductionAssignment.objects.filter(id=self.other_assignment.id).update(
created_at=timezone.make_aware(datetime.datetime(2026, 5, 2, 9, 0, 0))
)
def _headers(self, key='agent-test-key'):
return {'HTTP_AUTHORIZATION': key}
def test_list_mes_devices_success(self):
response = self.client.get(
self.device_list_url,
{'merchant_id': self.merchant.id},
**self._headers(),
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 2)
self.assertEqual(response.data['results'][0]['category']['name'], '打印机')
def test_list_mes_devices_isolates_by_merchant(self):
response = self.client.get(
self.device_list_url,
{'merchant_id': self.other_merchant.id},
**self._headers(),
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 1)
self.assertEqual(response.data['results'][0]['id'], self.other_device.id)
def test_list_mes_devices_requires_access_key(self):
response = self.client.get(self.device_list_url, {'merchant_id': self.merchant.id})
self.assertEqual(response.status_code, 401)
def test_list_mes_production_assignments_filters_by_date_range(self):
response = self.client.get(
self.assignment_list_url,
{
'merchant_id': self.merchant.id,
'start_date': '2026-05-01',
'end_date': '2026-05-02',
},
**self._headers(),
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 1)
self.assertEqual(response.data['results'][0]['id'], self.assignment_published.id)
self.assertEqual(response.data['results'][0]['device']['id'], self.device_a.id)
def test_list_mes_production_assignments_supports_status_and_device_filter(self):
response = self.client.get(
self.assignment_list_url,
{
'merchant_id': self.merchant.id,
'start_date': '2026-05-01',
'end_date': '2026-05-31',
'device_id': self.device_b.id,
'status': mes_models.ProductionAssignmentStatusEnum.DRAFT,
},
**self._headers(),
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 1)
self.assertEqual(response.data['results'][0]['id'], self.assignment_draft.id)
def test_list_mes_production_assignments_rejects_invalid_date_range(self):
response = self.client.get(
self.assignment_list_url,
{
'merchant_id': self.merchant.id,
'start_date': '2026-05-31',
'end_date': '2026-05-01',
},
**self._headers(),
)
self.assertEqual(response.status_code, 400)
self.assertIn('end_date', response.data)
def test_list_mes_production_assignments_requires_access_key(self):
response = self.client.get(
self.assignment_list_url,
{
'merchant_id': self.merchant.id,
'start_date': '2026-05-01',
'end_date': '2026-05-31',
},
)
self.assertEqual(response.status_code, 401)