forked from erp-dev/erp
fin
This commit is contained in:
@@ -368,6 +368,7 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
|
||||
|
||||
printing_order_id = serializers.CharField(source='printing_order.human_id', read_only=True)
|
||||
external_order_id = serializers.CharField(source='printing_order.external_order_id', read_only=True)
|
||||
customer_name = serializers.CharField(source='printing_order.customer.name', read_only=True, allow_null=True)
|
||||
product_name = serializers.CharField(source='product.name', read_only=True)
|
||||
product_image_url = serializers.SerializerMethodField()
|
||||
status = serializers.CharField(read_only=True)
|
||||
@@ -388,7 +389,8 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.PrintingJob
|
||||
fields = [
|
||||
'id', 'original_id', 'sub_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name',
|
||||
'id', 'original_id', 'sub_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id',
|
||||
'customer_name', 'product', 'product_name',
|
||||
'product_image_url', 'has_started',
|
||||
'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'work_state', 'work_state_display',
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"""
|
||||
PrintingJob API 测试
|
||||
"""
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from django.contrib.auth import get_user_model
|
||||
@@ -187,10 +189,74 @@ class PrintingJobAPITestCase(TestCase):
|
||||
|
||||
# 新增字段:批量推进记录(稳定输出 key)
|
||||
for item in response.data['results']:
|
||||
self.assertIn('customer_name', item)
|
||||
self.assertEqual(item['customer_name'], self.customer.name)
|
||||
self.assertIn('batch_advance_records', item)
|
||||
self.assertIsInstance(item['batch_advance_records'], list)
|
||||
self.assertEqual(len(item['batch_advance_records']), 0)
|
||||
|
||||
def test_filter_printing_jobs_by_customer_name(self):
|
||||
"""测试按客户名称筛选款式明细列表"""
|
||||
other_customer = basic_models.Customer.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='其他客户',
|
||||
mobile='13900139001',
|
||||
area='测试地区'
|
||||
)
|
||||
other_order = printing_models.PrintingOrder.objects.create(
|
||||
customer=other_customer,
|
||||
fabric='其他布料',
|
||||
width='150cm',
|
||||
process=self.process,
|
||||
)
|
||||
matched_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=100,
|
||||
unit='米',
|
||||
)
|
||||
other_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=other_order,
|
||||
product=self.product,
|
||||
quantity=200,
|
||||
unit='米',
|
||||
)
|
||||
|
||||
response = self.client.get('/api/v1/printing-jobs/?customer_name=测试客')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
ids = {item['id'] for item in response.data['results']}
|
||||
self.assertIn(matched_job.id, ids)
|
||||
self.assertNotIn(other_job.id, ids)
|
||||
|
||||
def test_filter_printing_jobs_by_created_at_date_range_includes_whole_to_day(self):
|
||||
"""测试 created_at_to 传日期时包含整天"""
|
||||
tz = timezone.get_current_timezone()
|
||||
in_range = timezone.make_aware(datetime(2026, 7, 3, 23, 30, 0), tz)
|
||||
out_range = timezone.make_aware(datetime(2026, 7, 4, 0, 0, 0), tz)
|
||||
|
||||
matched_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=100,
|
||||
unit='米',
|
||||
)
|
||||
out_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=200,
|
||||
unit='米',
|
||||
)
|
||||
printing_models.PrintingJob.objects.filter(id=matched_job.id).update(created_at=in_range)
|
||||
printing_models.PrintingJob.objects.filter(id=out_job.id).update(created_at=out_range)
|
||||
|
||||
response = self.client.get(
|
||||
'/api/v1/printing-jobs/?created_at_from=2026-07-03&created_at_to=2026-07-03'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
ids = {item['id'] for item in response.data['results']}
|
||||
self.assertIn(matched_job.id, ids)
|
||||
self.assertNotIn(out_job.id, ids)
|
||||
|
||||
def test_list_printing_jobs_includes_is_sales_order_bound(self):
|
||||
"""测试列表返回是否已绑定销售单字段,并支持关闭该字段"""
|
||||
unbound_job = printing_models.PrintingJob.objects.create(
|
||||
|
||||
@@ -444,6 +444,9 @@ class PrintingJobFilterSet(django_filters.FilterSet):
|
||||
external_order_id = django_filters.CharFilter(
|
||||
field_name="printing_order__external_order_id", lookup_expr="icontains"
|
||||
)
|
||||
customer_name = django_filters.CharFilter(
|
||||
field_name="printing_order__customer__name", lookup_expr="icontains"
|
||||
)
|
||||
product = django_filters.NumberFilter()
|
||||
product_name = django_filters.CharFilter(
|
||||
field_name="product__name", lookup_expr="icontains"
|
||||
@@ -453,12 +456,30 @@ class PrintingJobFilterSet(django_filters.FilterSet):
|
||||
quantity_max = django_filters.NumberFilter(field_name="quantity", lookup_expr="lte")
|
||||
pieces_min = django_filters.NumberFilter(field_name="pieces", lookup_expr="gte")
|
||||
pieces_max = django_filters.NumberFilter(field_name="pieces", lookup_expr="lte")
|
||||
created_at_from = django_filters.CharFilter(method="filter_created_at_from")
|
||||
created_at_to = django_filters.CharFilter(method="filter_created_at_to")
|
||||
is_production_completed = django_filters.BooleanFilter()
|
||||
|
||||
class Meta:
|
||||
model = models.PrintingJob
|
||||
fields = ["printing_order", "product", "external_order_id", "is_production_completed"]
|
||||
|
||||
def filter_created_at_from(self, queryset, name, value):
|
||||
dt, _has_time = PrintingOrderFilterSet._parse_date_or_datetime(value)
|
||||
if dt is None:
|
||||
raise ValidationError("created_at_from 必须是日期或日期时间")
|
||||
return queryset.filter(created_at__gte=dt)
|
||||
|
||||
def filter_created_at_to(self, queryset, name, value):
|
||||
dt, has_time = PrintingOrderFilterSet._parse_date_or_datetime(value)
|
||||
if dt is None:
|
||||
raise ValidationError("created_at_to 必须是日期或日期时间")
|
||||
|
||||
if not has_time:
|
||||
return queryset.filter(created_at__lt=dt + timedelta(days=1))
|
||||
|
||||
return queryset.filter(created_at__lte=dt)
|
||||
|
||||
|
||||
class PrintingJobViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
"""
|
||||
@@ -484,6 +505,7 @@ class PrintingJobViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
查询参数:
|
||||
- printing_order: 印染订单ID
|
||||
- external_order_id: 外部订单编号(按所属订单模糊查询)
|
||||
- customer_name: 客户名称(按所属订单客户名称模糊查询)
|
||||
- product: 产品ID
|
||||
- product_name: 产品名称(模糊查询)
|
||||
- unit: 单位(模糊查询)
|
||||
@@ -491,6 +513,8 @@ class PrintingJobViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
- quantity_max: 最大数量
|
||||
- pieces_min: 最小件数
|
||||
- pieces_max: 最大件数
|
||||
- created_at_from: 创建时间开始(支持 YYYY-MM-DD 或日期时间)
|
||||
- created_at_to: 创建时间结束(YYYY-MM-DD 会包含整天)
|
||||
- is_production_completed: 是否已标记完成生产(true/false)
|
||||
- search: 全文搜索(产品名称、单位、尺寸、备注)
|
||||
- ordering: 排序字段
|
||||
@@ -529,7 +553,7 @@ class PrintingJobViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
if self.action in ["list", "retrieve"]:
|
||||
from business.models import SalesOrderItem, SalesOrderStatusEnum
|
||||
|
||||
queryset = queryset.select_related("printing_order", "product")
|
||||
queryset = queryset.select_related("printing_order", "printing_order__customer", "product")
|
||||
queryset = queryset.annotate(
|
||||
annotated_is_sales_order_bound=Exists(
|
||||
SalesOrderItem.objects.filter(
|
||||
|
||||
Reference in New Issue
Block a user