forked from erp-dev/erp
fix: business cancel service merged
This commit is contained in:
@@ -1305,6 +1305,32 @@ class CustomerBalanceAPITestCase(TestCase):
|
||||
response = self.client.get(f'/api/v1/customers/{self.customer.id}/balance/')
|
||||
self.assertEqual(response.data['balance'], '30.00')
|
||||
|
||||
business_models.ExternalCustomerStatementOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
category=business_models.ExternalCustomerStatementCategoryEnum.SALE,
|
||||
external_source_id='XS-BAL-001',
|
||||
occurred_at=datetime.date(2025, 11, 28),
|
||||
total_amount=Decimal('100.00'),
|
||||
zk_amount=Decimal('10.00'),
|
||||
sf_amount=Decimal('20.00'),
|
||||
)
|
||||
business_models.ExternalCustomerStatementOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
category=business_models.ExternalCustomerStatementCategoryEnum.SALE_RETURN,
|
||||
external_source_id='XT-BAL-001',
|
||||
occurred_at=datetime.date(2025, 11, 29),
|
||||
total_amount=Decimal('15.00'),
|
||||
zk_amount=Decimal('5.00'),
|
||||
)
|
||||
|
||||
response = self.client.get(f'/api/v1/customers/{self.customer.id}/balance/')
|
||||
self.assertEqual(response.data['balance'], '80.00')
|
||||
|
||||
statement_payload = services.build_customer_statement(merchant=self.merchant, customer=self.customer)
|
||||
self.assertEqual(statement_payload['records'][0]['current_balance'], response.data['balance'])
|
||||
|
||||
|
||||
class SupplierBalanceAPITestCase(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -22,7 +22,7 @@ class CustomerBalanceView(StockChangeViewMixin, views.APIView):
|
||||
except basic_models.Customer.DoesNotExist:
|
||||
return self.not_found_response('客户不存在')
|
||||
|
||||
balance: Decimal = business_services.BalanceService.get_customer_balance(
|
||||
balance: Decimal = business_services.BalanceService.get_customer_statement_balance(
|
||||
merchant=merchant,
|
||||
customer=customer,
|
||||
)
|
||||
|
||||
@@ -96,6 +96,8 @@ class SalesOrderView(StockChangeViewMixin, views.APIView):
|
||||
order_date = data.get('order_date')
|
||||
items = data.get('items', [])
|
||||
remarks = data.get('remarks', '')
|
||||
kind = data.get('kind')
|
||||
|
||||
|
||||
if not customer_id:
|
||||
return Response({'error': '缺少客户 ID'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -128,7 +130,9 @@ class SalesOrderView(StockChangeViewMixin, views.APIView):
|
||||
items=items,
|
||||
remarks=remarks,
|
||||
created_by=request.user,
|
||||
kind=kind,
|
||||
)
|
||||
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -199,6 +203,7 @@ class SalesOrderDetailView(StockChangeViewMixin, views.APIView):
|
||||
if not isinstance(items, list) or not items:
|
||||
return Response({'error': 'items 需要为非空数组'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
remarks = data.get('remarks', sales_order.remarks)
|
||||
kind = data.get('kind')
|
||||
|
||||
try:
|
||||
updated_order = business_services.update_sales_order(
|
||||
@@ -208,7 +213,9 @@ class SalesOrderDetailView(StockChangeViewMixin, views.APIView):
|
||||
warehouse=warehouse,
|
||||
items=items,
|
||||
remarks=remarks,
|
||||
kind=kind,
|
||||
)
|
||||
|
||||
except ValueError as exc:
|
||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@@ -91,15 +91,20 @@
|
||||
- `unit`: 单位 (模糊查询)
|
||||
- `quantity_min`/`max`: 数量范围
|
||||
- `pieces_min`/`max`: 件数范围
|
||||
- `include_sales_order_bound`: 是否返回 `is_sales_order_bound` 字段,默认 `true`;传 `false`/`0`/`no` 时不返回
|
||||
- `search`: 全文搜索 (产品名称, 单位, 尺寸, 备注)
|
||||
- `ordering`: 排序字段。
|
||||
- **响应**: `PrintingJobListSerializer` 列表。
|
||||
- **重要变更**: 列表中已**包含** `business_object_id` 字段。
|
||||
- **字段**: `is_sales_order_bound` 表示该印染任务是否已被销售单明细绑定。
|
||||
|
||||
- **GET** `/api/v1/printing-jobs/{id}/`
|
||||
- **描述**: 获取单个印染任务详情。
|
||||
- **查询参数**:
|
||||
- `include_sales_order_bound`: 是否返回 `is_sales_order_bound` 字段,默认 `true`;传 `false`/`0`/`no` 时不返回
|
||||
- **响应**: `PrintingJobDetailSerializer`。
|
||||
- **重要变更**: `business_object_id` 字段现在会安全地返回 `null` 而不是报错(当 `business_object` 不存在时)。
|
||||
- **字段**: `is_sales_order_bound` 表示该印染任务是否已被销售单明细绑定。
|
||||
|
||||
- **POST** `/api/v1/printing-jobs/`
|
||||
- **描述**: 创建一个新的印染任务。
|
||||
|
||||
@@ -13,6 +13,21 @@ from .services import PrintingOrderService, PrintingJobService
|
||||
from basic_info.models import Customer, Employee
|
||||
|
||||
|
||||
def _include_sales_order_bound(context) -> bool:
|
||||
request = context.get('request') if context else None
|
||||
if not request:
|
||||
return True
|
||||
raw_value = request.query_params.get('include_sales_order_bound')
|
||||
return str(raw_value).lower() not in {'0', 'false', 'no'}
|
||||
|
||||
|
||||
def _get_is_sales_order_bound(obj) -> bool:
|
||||
annotated_value = getattr(obj, 'annotated_is_sales_order_bound', None)
|
||||
if annotated_value is not None:
|
||||
return bool(annotated_value)
|
||||
return bool(obj.is_sales_order_bound)
|
||||
|
||||
|
||||
def _build_absolute_media_url(url: str | None, request):
|
||||
return build_public_media_url(url, request=request)
|
||||
|
||||
@@ -346,6 +361,8 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
|
||||
business_object_id = serializers.SerializerMethodField()
|
||||
batch_advance_records = serializers.SerializerMethodField()
|
||||
saleitems = serializers.SerializerMethodField()
|
||||
is_sales_order_bound = serializers.SerializerMethodField()
|
||||
billed_quantity = serializers.DecimalField(max_digits=18, decimal_places=2, read_only=True)
|
||||
merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
@@ -353,10 +370,11 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
|
||||
fields = [
|
||||
'id', 'original_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name',
|
||||
'product_image_url', 'has_started',
|
||||
'quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'work_state', 'work_state_display',
|
||||
'status', 'is_completed', 'is_production_completed', 'progress_percentage', 'last_completed_state',
|
||||
'business_object_id',
|
||||
'is_sales_order_bound',
|
||||
'batch_advance_records',
|
||||
'saleitems',
|
||||
'created_at', 'updated_at'
|
||||
@@ -364,8 +382,14 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at',
|
||||
'status', 'is_completed', 'is_production_completed', 'progress_percentage', 'last_completed_state',
|
||||
'business_object_id', 'merchant_id'
|
||||
'business_object_id', 'is_sales_order_bound', 'billed_quantity', 'merchant_id'
|
||||
]
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
if not _include_sales_order_bound(self.context):
|
||||
data.pop('is_sales_order_bound', None)
|
||||
return data
|
||||
|
||||
def get_business_object_id(self, obj):
|
||||
"""安全地获取 business_object_id"""
|
||||
@@ -422,6 +446,9 @@ class PrintingJobListSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
return SalesItemSerializer(items, many=True).data
|
||||
|
||||
def get_is_sales_order_bound(self, obj):
|
||||
return _get_is_sales_order_bound(obj)
|
||||
|
||||
|
||||
class PrintingJobDetailSerializer(serializers.ModelSerializer):
|
||||
"""印染款式明细详情序列化器"""
|
||||
@@ -440,17 +467,20 @@ class PrintingJobDetailSerializer(serializers.ModelSerializer):
|
||||
business_object_id = serializers.SerializerMethodField()
|
||||
batch_advance_records = serializers.SerializerMethodField()
|
||||
saleitems = serializers.SerializerMethodField()
|
||||
is_sales_order_bound = serializers.SerializerMethodField()
|
||||
billed_quantity = serializers.DecimalField(max_digits=18, decimal_places=2, read_only=True)
|
||||
merchant_id = serializers.IntegerField(source='merchant.id', read_only=True, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = models.PrintingJob
|
||||
fields = [
|
||||
'id', 'original_id', 'merchant_id', 'printing_order', 'printing_order_id', 'external_order_id', 'product', 'product_name', 'product_code',
|
||||
'quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'quantity', 'billed_quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'work_state', 'work_state_display',
|
||||
'status', 'status_id', 'is_completed', 'is_production_completed', 'has_started',
|
||||
'progress_percentage', 'last_completed_state',
|
||||
'business_object_id',
|
||||
'is_sales_order_bound',
|
||||
'batch_advance_records',
|
||||
'saleitems',
|
||||
'created_at', 'updated_at'
|
||||
@@ -459,8 +489,14 @@ class PrintingJobDetailSerializer(serializers.ModelSerializer):
|
||||
'id', 'created_at', 'updated_at',
|
||||
'status', 'status_id', 'is_completed', 'is_production_completed', 'has_started',
|
||||
'progress_percentage', 'last_completed_state',
|
||||
'business_object_id', 'merchant_id'
|
||||
'business_object_id', 'is_sales_order_bound', 'billed_quantity', 'merchant_id'
|
||||
]
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
if not _include_sales_order_bound(self.context):
|
||||
data.pop('is_sales_order_bound', None)
|
||||
return data
|
||||
|
||||
def get_business_object_id(self, obj):
|
||||
"""安全地获取 business_object_id"""
|
||||
@@ -485,19 +521,23 @@ class PrintingJobDetailSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
return SalesItemSerializer(items, many=True).data
|
||||
|
||||
def get_is_sales_order_bound(self, obj):
|
||||
return _get_is_sales_order_bound(obj)
|
||||
|
||||
|
||||
class PrintingJobCreateUpdateSerializer(serializers.ModelSerializer):
|
||||
"""印染款式明细创建/更新序列化器"""
|
||||
external_order_id = serializers.CharField(source='printing_order.external_order_id', read_only=True)
|
||||
batch_advance_records = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.PrintingJob
|
||||
fields = [
|
||||
'id', 'original_id', 'printing_order', 'product', 'quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'id', 'original_id', 'printing_order', 'external_order_id', 'product', 'quantity', 'unit', 'size', 'pieces', 'description',
|
||||
'work_state',
|
||||
'batch_advance_records',
|
||||
]
|
||||
read_only_fields = ['id']
|
||||
read_only_fields = ['id', 'external_order_id']
|
||||
extra_kwargs = {
|
||||
'size': {'required': False, 'allow_null': True, 'allow_blank': True},
|
||||
'pieces': {'required': False, 'allow_null': True},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
PrintingJob API 测试
|
||||
"""
|
||||
from decimal import Decimal
|
||||
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
from rest_framework.test import APIClient
|
||||
@@ -9,6 +11,7 @@ from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from basic_info import models as basic_models
|
||||
from business import models as business_models
|
||||
from printing import models as printing_models
|
||||
from shipment import models as shipment_models
|
||||
from stateflow import models as stateflow_models
|
||||
@@ -101,6 +104,9 @@ class PrintingJobAPITestCase(TestCase):
|
||||
|
||||
def test_create_printing_job(self):
|
||||
"""测试创建印染款式明细"""
|
||||
self.printing_order.external_order_id = 'KD20410611'
|
||||
self.printing_order.save(update_fields=['external_order_id'])
|
||||
|
||||
data = {
|
||||
'printing_order': self.printing_order.id,
|
||||
'product': self.product.id,
|
||||
@@ -113,6 +119,7 @@ class PrintingJobAPITestCase(TestCase):
|
||||
|
||||
response = self.client.post('/api/v1/printing-jobs/', data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(response.data['external_order_id'], 'KD20410611')
|
||||
|
||||
# 新增字段:批量推进记录(稳定输出 key,默认空数组)
|
||||
self.assertIn('batch_advance_records', response.data)
|
||||
@@ -176,6 +183,128 @@ class PrintingJobAPITestCase(TestCase):
|
||||
self.assertIsInstance(item['batch_advance_records'], list)
|
||||
self.assertEqual(len(item['batch_advance_records']), 0)
|
||||
|
||||
def test_list_printing_jobs_includes_is_sales_order_bound(self):
|
||||
"""测试列表返回是否已绑定销售单字段,并支持关闭该字段"""
|
||||
unbound_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=100,
|
||||
unit='米',
|
||||
)
|
||||
bound_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=200,
|
||||
unit='米',
|
||||
)
|
||||
warehouse = basic_models.WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='测试仓库',
|
||||
mode=basic_models.WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
sales_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date='2026-06-10',
|
||||
operator=self.employee,
|
||||
warehouse=warehouse,
|
||||
status=business_models.SalesOrderStatusEnum.APPROVED,
|
||||
)
|
||||
business_models.SalesOrderItem.objects.create(
|
||||
sales_order=sales_order,
|
||||
product=self.product,
|
||||
printing_job=bound_job,
|
||||
price='10.00',
|
||||
quantity='1.00',
|
||||
unit='米',
|
||||
empty_diff_percent='0.00',
|
||||
)
|
||||
cancelled_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date='2026-06-10',
|
||||
operator=self.employee,
|
||||
warehouse=warehouse,
|
||||
status=business_models.SalesOrderStatusEnum.CANCELLED,
|
||||
)
|
||||
cancelled_only_job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=300,
|
||||
unit='米',
|
||||
)
|
||||
business_models.SalesOrderItem.objects.create(
|
||||
sales_order=cancelled_order,
|
||||
product=self.product,
|
||||
printing_job=cancelled_only_job,
|
||||
price='10.00',
|
||||
quantity='1.00',
|
||||
unit='米',
|
||||
empty_diff_percent='0.00',
|
||||
)
|
||||
|
||||
response = self.client.get('/api/v1/printing-jobs/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
bound_by_id = {item['id']: item['is_sales_order_bound'] for item in response.data['results']}
|
||||
billed_by_id = {item['id']: item['billed_quantity'] for item in response.data['results']}
|
||||
self.assertFalse(bound_by_id[unbound_job.id])
|
||||
self.assertTrue(bound_by_id[bound_job.id])
|
||||
self.assertFalse(bound_by_id[cancelled_only_job.id])
|
||||
self.assertEqual(billed_by_id[unbound_job.id], '0.00')
|
||||
self.assertEqual(billed_by_id[bound_job.id], '1.00')
|
||||
self.assertEqual(billed_by_id[cancelled_only_job.id], '0.00')
|
||||
|
||||
response = self.client.get('/api/v1/printing-jobs/?include_sales_order_bound=false')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
for item in response.data['results']:
|
||||
self.assertNotIn('is_sales_order_bound', item)
|
||||
|
||||
def test_billed_quantity_ignores_cancelled_sales_order_items(self):
|
||||
"""测试开单数量忽略已作废销售单明细"""
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=100,
|
||||
unit='米',
|
||||
)
|
||||
warehouse = basic_models.WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='开单数量测试仓库',
|
||||
mode=basic_models.WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
approved_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date='2026-06-10',
|
||||
operator=self.employee,
|
||||
warehouse=warehouse,
|
||||
status=business_models.SalesOrderStatusEnum.APPROVED,
|
||||
)
|
||||
cancelled_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date='2026-06-10',
|
||||
operator=self.employee,
|
||||
warehouse=warehouse,
|
||||
status=business_models.SalesOrderStatusEnum.CANCELLED,
|
||||
)
|
||||
for order, quantity in [(approved_order, '7.50'), (cancelled_order, '20.00')]:
|
||||
business_models.SalesOrderItem.objects.create(
|
||||
sales_order=order,
|
||||
product=self.product,
|
||||
printing_job=job,
|
||||
price='10.00',
|
||||
quantity=quantity,
|
||||
unit='米',
|
||||
empty_diff_percent='0.00',
|
||||
)
|
||||
|
||||
self.assertEqual(job.billed_quantity, Decimal('7.50'))
|
||||
|
||||
response = self.client.get(f'/api/v1/printing-jobs/{job.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['billed_quantity'], '7.50')
|
||||
|
||||
def test_mark_production_completed(self):
|
||||
"""测试显式标记完成生产接口"""
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
@@ -283,6 +412,81 @@ class PrintingJobAPITestCase(TestCase):
|
||||
self.assertIsInstance(response.data['batch_advance_records'], list)
|
||||
self.assertEqual(len(response.data['batch_advance_records']), 0)
|
||||
|
||||
def test_retrieve_printing_job_includes_is_sales_order_bound(self):
|
||||
"""测试详情返回是否已绑定销售单字段"""
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=100,
|
||||
unit='米',
|
||||
)
|
||||
warehouse = basic_models.WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='详情测试仓库',
|
||||
mode=basic_models.WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
sales_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date='2026-06-10',
|
||||
operator=self.employee,
|
||||
warehouse=warehouse,
|
||||
status=business_models.SalesOrderStatusEnum.APPROVED,
|
||||
)
|
||||
business_models.SalesOrderItem.objects.create(
|
||||
sales_order=sales_order,
|
||||
product=self.product,
|
||||
printing_job=job,
|
||||
price='10.00',
|
||||
quantity='1.00',
|
||||
unit='米',
|
||||
empty_diff_percent='0.00',
|
||||
)
|
||||
|
||||
response = self.client.get(f'/api/v1/printing-jobs/{job.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertTrue(response.data['is_sales_order_bound'])
|
||||
self.assertEqual(response.data['billed_quantity'], '1.00')
|
||||
|
||||
response = self.client.get(f'/api/v1/printing-jobs/{job.id}/?include_sales_order_bound=false')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertNotIn('is_sales_order_bound', response.data)
|
||||
|
||||
def test_retrieve_printing_job_ignores_cancelled_sales_order_bound(self):
|
||||
"""测试详情判断销售单绑定时忽略已作废销售单"""
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
quantity=100,
|
||||
unit='米',
|
||||
)
|
||||
warehouse = basic_models.WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='详情作废测试仓库',
|
||||
mode=basic_models.WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
cancelled_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date='2026-06-10',
|
||||
operator=self.employee,
|
||||
warehouse=warehouse,
|
||||
status=business_models.SalesOrderStatusEnum.CANCELLED,
|
||||
)
|
||||
business_models.SalesOrderItem.objects.create(
|
||||
sales_order=cancelled_order,
|
||||
product=self.product,
|
||||
printing_job=job,
|
||||
price='10.00',
|
||||
quantity='1.00',
|
||||
unit='米',
|
||||
empty_diff_percent='0.00',
|
||||
)
|
||||
|
||||
response = self.client.get(f'/api/v1/printing-jobs/{job.id}/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertFalse(response.data['is_sales_order_bound'])
|
||||
|
||||
def test_batch_advance_records_in_list_and_detail(self):
|
||||
"""测试 PrintingJob 序列化输出包含批量推进记录(有记录时返回明细,无记录返回空)"""
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
@@ -417,6 +621,9 @@ class PrintingJobAPITestCase(TestCase):
|
||||
|
||||
def test_update_printing_job(self):
|
||||
"""测试更新款式明细"""
|
||||
self.printing_order.external_order_id = 'KD20410611'
|
||||
self.printing_order.save(update_fields=['external_order_id'])
|
||||
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
@@ -442,6 +649,7 @@ class PrintingJobAPITestCase(TestCase):
|
||||
format='json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['external_order_id'], 'KD20410611')
|
||||
|
||||
job.refresh_from_db()
|
||||
self.assertEqual(job.quantity, 200)
|
||||
@@ -502,6 +710,9 @@ class PrintingJobAPITestCase(TestCase):
|
||||
|
||||
def test_partial_update_printing_job(self):
|
||||
"""测试部分更新款式明细"""
|
||||
self.printing_order.external_order_id = 'KD20410611'
|
||||
self.printing_order.save(update_fields=['external_order_id'])
|
||||
|
||||
job = printing_models.PrintingJob.objects.create(
|
||||
printing_order=self.printing_order,
|
||||
product=self.product,
|
||||
@@ -522,6 +733,7 @@ class PrintingJobAPITestCase(TestCase):
|
||||
format='json'
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['external_order_id'], 'KD20410611')
|
||||
|
||||
job.refresh_from_db()
|
||||
self.assertEqual(job.quantity, 150)
|
||||
|
||||
@@ -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, Prefetch
|
||||
from django.db.models import CharField, Exists, OuterRef, Prefetch
|
||||
from django.db.models.functions import Cast, Coalesce
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.decorators.http import condition
|
||||
@@ -486,7 +486,16 @@ class PrintingJobViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
"""优化查询"""
|
||||
queryset = super().get_queryset()
|
||||
if self.action in ["list", "retrieve"]:
|
||||
from business.models import SalesOrderItem, SalesOrderStatusEnum
|
||||
|
||||
queryset = queryset.select_related("printing_order", "product")
|
||||
queryset = queryset.annotate(
|
||||
annotated_is_sales_order_bound=Exists(
|
||||
SalesOrderItem.objects.filter(
|
||||
printing_job_id=OuterRef("pk"),
|
||||
).exclude(sales_order__status=SalesOrderStatusEnum.CANCELLED)
|
||||
)
|
||||
)
|
||||
# 预取批量推进记录(避免 serializer 产生 N+1)
|
||||
queryset = queryset.prefetch_related(
|
||||
Prefetch(
|
||||
|
||||
Reference in New Issue
Block a user