forked from erp-dev/erp
feat: shipment change && version modelize
This commit is contained in:
@@ -7,6 +7,7 @@ from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api_v1.models import UploadedFile
|
||||
from api_v1.external_datetime import parse_external_china_datetime
|
||||
from api_v1.utils.media import build_public_media_url
|
||||
from api_v1.views.shipment.serializers import SalesItemSerializer
|
||||
from printing import models
|
||||
@@ -54,7 +55,11 @@ def resolve_printing_order_kd_riqi(obj):
|
||||
value = getattr(obj, 'kd_riqi', None)
|
||||
if value is not None:
|
||||
return serializers.DateTimeField().to_representation(value)
|
||||
return _resolve_printing_order_external_first_record_field(obj, 'KdRiQi')
|
||||
raw_value = _resolve_printing_order_external_first_record_field(obj, 'KdRiQi')
|
||||
parsed = parse_external_china_datetime(raw_value)
|
||||
if parsed is None:
|
||||
return raw_value
|
||||
return serializers.DateTimeField().to_representation(parsed)
|
||||
|
||||
|
||||
def _serialize_plate_images(raw_value, request):
|
||||
@@ -354,13 +359,24 @@ class PrintingOrderCreateUpdateSerializer(serializers.ModelSerializer):
|
||||
],
|
||||
help_text="出货日期时间(可仅传 YYYY-MM-DD,将自动视为 00:00:00)",
|
||||
)
|
||||
kd_riqi = serializers.DateTimeField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
input_formats=[
|
||||
"iso-8601",
|
||||
"%Y-%m-%d",
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
"%Y-%m-%d %H:%M",
|
||||
],
|
||||
help_text="外部开单日期时间(可仅传 YYYY-MM-DD,将自动视为 00:00:00)",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.PrintingOrder
|
||||
fields = [
|
||||
'id', 'customer', 'fabric', 'width', 'is_urgent', 'area', 'address',
|
||||
'fabric_source', 'is_fabric_received', 'craft', 'description',
|
||||
'outgoing_date', 'curve', 'new_curve', 'position',
|
||||
'outgoing_date', 'kd_riqi', 'curve', 'new_curve', 'position',
|
||||
'printing_warn', 'rolling_warn', 'production_warn', 'is_invalid',
|
||||
'process', 'external_order_id',
|
||||
]
|
||||
|
||||
@@ -94,6 +94,7 @@ class PrintingOrderAPITestCase(TestCase):
|
||||
'craft': '活性印花',
|
||||
'description': '测试订单描述',
|
||||
'outgoing_date': '2025-11-20',
|
||||
'kd_riqi': '2025-11-19 13:14:15',
|
||||
'printing_warn': '注意颜色',
|
||||
'rolling_warn': '注意温度',
|
||||
'production_warn': '质量检查'
|
||||
@@ -120,6 +121,9 @@ class PrintingOrderAPITestCase(TestCase):
|
||||
self.assertEqual(str(local_dt.date()), '2025-11-20')
|
||||
self.assertEqual(local_dt.hour, 0)
|
||||
self.assertEqual(local_dt.minute, 0)
|
||||
self.assertIsNotNone(order.kd_riqi)
|
||||
local_kd_riqi = timezone.localtime(order.kd_riqi)
|
||||
self.assertEqual(local_kd_riqi.isoformat(), '2025-11-19T13:14:15+08:00')
|
||||
|
||||
def test_filter_outgoing_date_to_includes_whole_day(self):
|
||||
"""
|
||||
@@ -284,14 +288,14 @@ class PrintingOrderAPITestCase(TestCase):
|
||||
self.assertEqual(item['external_customer_name'], '李泽柔')
|
||||
self.assertEqual(item['external_employee_name'], '丽容')
|
||||
self.assertEqual(item['bianhao_kd'], '7.07')
|
||||
self.assertEqual(item['kd_riqi'], '2026-07-06T21:48:58Z')
|
||||
self.assertEqual(item['kd_riqi'], '2026-07-06T21:48:58+08:00')
|
||||
|
||||
detail = self.client.get(f'/api/v1/printing-orders/{order1.id}/')
|
||||
self.assertEqual(detail.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(detail.data['external_order_id'], 'KD20410611')
|
||||
self.assertEqual(detail.data['external_customer_id'], 'KH00991')
|
||||
self.assertEqual(detail.data['bianhao_kd'], '7.07')
|
||||
self.assertEqual(detail.data['kd_riqi'], '2026-07-06T21:48:58Z')
|
||||
self.assertEqual(detail.data['kd_riqi'], '2026-07-06T21:48:58+08:00')
|
||||
|
||||
def test_bianhao_kd_returns_null_without_external_order_id(self):
|
||||
order = printing_models.PrintingOrder.objects.create(
|
||||
@@ -342,6 +346,39 @@ class PrintingOrderAPITestCase(TestCase):
|
||||
result_ids = [item['id'] for item in response.data['results']]
|
||||
self.assertEqual(result_ids, [older.id, newer.id])
|
||||
self.assertEqual(response.data['results'][0]['kd_riqi'], '2026-07-01T18:00:00+08:00')
|
||||
|
||||
def test_order_by_kd_riqi_puts_nulls_last(self):
|
||||
null_order = printing_models.PrintingOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
fabric='空开单时间',
|
||||
width='150cm',
|
||||
external_order_id='KD-NULL',
|
||||
)
|
||||
older = printing_models.PrintingOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
fabric='旧开单时间',
|
||||
width='150cm',
|
||||
external_order_id='KD-OLDER-NULLS-LAST',
|
||||
kd_riqi=datetime(2026, 7, 1, 10, 0, tzinfo=dt_timezone.utc),
|
||||
)
|
||||
newer = printing_models.PrintingOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
fabric='新开单时间',
|
||||
width='150cm',
|
||||
external_order_id='KD-NEWER-NULLS-LAST',
|
||||
kd_riqi=datetime(2026, 7, 2, 10, 0, tzinfo=dt_timezone.utc),
|
||||
)
|
||||
|
||||
asc_response = self.client.get('/api/v1/printing-orders/?ordering=kd_riqi&limit=3')
|
||||
desc_response = self.client.get('/api/v1/printing-orders/?ordering=-kd_riqi&limit=3')
|
||||
|
||||
self.assertEqual(asc_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(desc_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual([item['id'] for item in asc_response.data['results']], [older.id, newer.id, null_order.id])
|
||||
self.assertEqual([item['id'] for item in desc_response.data['results']], [newer.id, older.id, null_order.id])
|
||||
|
||||
def test_retrieve_printing_order(self):
|
||||
"""测试获取订单详情"""
|
||||
@@ -418,7 +455,8 @@ class PrintingOrderAPITestCase(TestCase):
|
||||
|
||||
patch_data = {
|
||||
'is_urgent': True,
|
||||
'craft': '新工艺'
|
||||
'craft': '新工艺',
|
||||
'kd_riqi': '2025-11-21 09:30:00',
|
||||
}
|
||||
|
||||
response = self.client.patch(
|
||||
@@ -431,6 +469,7 @@ class PrintingOrderAPITestCase(TestCase):
|
||||
order.refresh_from_db()
|
||||
self.assertEqual(order.is_urgent, True)
|
||||
self.assertEqual(order.craft, '新工艺')
|
||||
self.assertEqual(timezone.localtime(order.kd_riqi).isoformat(), '2025-11-21T09:30:00+08:00')
|
||||
self.assertEqual(order.fabric, '原布料') # 未修改字段保持不变
|
||||
|
||||
def test_delete_printing_order_forbidden(self):
|
||||
|
||||
@@ -10,7 +10,7 @@ from rest_framework.permissions import BasePermission
|
||||
from rest_framework.permissions import DjangoModelPermissions
|
||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import CharField, Exists, OuterRef, Prefetch, Subquery
|
||||
from django.db.models import CharField, Exists, F, OuterRef, Prefetch, Subquery
|
||||
from django.db.models.functions import Cast, Coalesce
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.decorators.http import condition
|
||||
@@ -38,6 +38,23 @@ from .serializers import (
|
||||
from .mixins import CustomerVisibilityFilterMixin
|
||||
|
||||
|
||||
class PrintingOrderOrderingFilter(filters.OrderingFilter):
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
ordering = self.get_ordering(request, queryset, view)
|
||||
if not ordering:
|
||||
return queryset
|
||||
|
||||
expressions = []
|
||||
for field in ordering:
|
||||
if field == "kd_riqi":
|
||||
expressions.append(F("kd_riqi").asc(nulls_last=True))
|
||||
elif field == "-kd_riqi":
|
||||
expressions.append(F("kd_riqi").desc(nulls_last=True))
|
||||
else:
|
||||
expressions.append(field)
|
||||
return queryset.order_by(*expressions)
|
||||
|
||||
|
||||
class IsPrintingFactory(BasePermission):
|
||||
"""自定义权限类,允许印染工厂用户访问"""
|
||||
|
||||
@@ -276,7 +293,7 @@ class PrintingOrderViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
filters.OrderingFilter,
|
||||
PrintingOrderOrderingFilter,
|
||||
]
|
||||
filterset_class = PrintingOrderFilterSet
|
||||
search_fields = ["customer__name", "fabric", "area", "craft", "description"]
|
||||
|
||||
Reference in New Issue
Block a user