forked from erp-dev/erp
feat: human_id for all objects in business module
This commit is contained in:
@@ -12,12 +12,13 @@ class PaymentOrderSerializer(serializers.ModelSerializer):
|
||||
supplier_name = serializers.CharField(source='supplier.name', read_only=True)
|
||||
operator_name = serializers.CharField(source='operator.name', read_only=True)
|
||||
bank_account_name = serializers.CharField(source='bank_account.name', read_only=True)
|
||||
human_id = serializers.CharField(read_only=True)
|
||||
settlement_amount = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = business_models.PaymentOrder
|
||||
fields = [
|
||||
'id', 'supplier', 'supplier_name', 'bank_account', 'bank_account_name',
|
||||
'id', 'human_id', 'supplier', 'supplier_name', 'bank_account', 'bank_account_name',
|
||||
'payment_date', 'amount', 'discount_amount', 'settlement_amount',
|
||||
'operator', 'operator_name', 'status', 'is_external_source', 'external_source_id',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
@@ -25,6 +26,7 @@ class PaymentOrderSerializer(serializers.ModelSerializer):
|
||||
]
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'human_id',
|
||||
'supplier_name',
|
||||
'operator_name',
|
||||
'bank_account_name',
|
||||
|
||||
@@ -12,12 +12,13 @@ class ReceiptOrderSerializer(serializers.ModelSerializer):
|
||||
customer_name = serializers.CharField(source='customer.name', read_only=True)
|
||||
operator_name = serializers.CharField(source='operator.name', read_only=True)
|
||||
bank_account_name = serializers.CharField(source='bank_account.name', read_only=True)
|
||||
human_id = serializers.CharField(read_only=True)
|
||||
settlement_amount = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = business_models.ReceiptOrder
|
||||
fields = [
|
||||
'id', 'customer', 'customer_name', 'bank_account', 'bank_account_name',
|
||||
'id', 'human_id', 'customer', 'customer_name', 'bank_account', 'bank_account_name',
|
||||
'receipt_date', 'amount', 'discount_amount', 'settlement_amount',
|
||||
'operator', 'operator_name', 'status', 'is_external_source', 'external_source_id',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
@@ -25,6 +26,7 @@ class ReceiptOrderSerializer(serializers.ModelSerializer):
|
||||
]
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'human_id',
|
||||
'customer_name',
|
||||
'operator_name',
|
||||
'bank_account_name',
|
||||
|
||||
@@ -19,6 +19,7 @@ class StatementRecordSerializer(serializers.Serializer):
|
||||
source_type = serializers.CharField()
|
||||
source_label = serializers.CharField()
|
||||
source_id = serializers.IntegerField()
|
||||
human_id = serializers.CharField()
|
||||
occurred_at = serializers.DateField()
|
||||
recorded_at = serializers.DateTimeField()
|
||||
status = serializers.IntegerField()
|
||||
|
||||
125
api_v2/tests.py
125
api_v2/tests.py
@@ -282,6 +282,131 @@ class PrintingJobByCustomerAPITest(TestCase):
|
||||
self.assertEqual(data['billed_quantity'], '20.00')
|
||||
|
||||
|
||||
class PrintingOrderWithoutSalesOrderAPITest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.url = '/api/v2/printing-orders/without-sales-order/'
|
||||
|
||||
self.merchant = basic_models.Merchant.objects.create(
|
||||
name='印染商户',
|
||||
type=basic_models.MerchantTypeEnum.FACTORY,
|
||||
)
|
||||
self.other_merchant = basic_models.Merchant.objects.create(
|
||||
name='其他商户',
|
||||
type=basic_models.MerchantTypeEnum.FACTORY,
|
||||
)
|
||||
self.user = get_user_model().objects.create_user(username='factory_order_user', password='pass12345')
|
||||
basic_models.Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
sys_user=self.user,
|
||||
name='工厂员工',
|
||||
)
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
self.customer = basic_models.Customer.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='客户A',
|
||||
created_by=None,
|
||||
)
|
||||
self.other_customer = basic_models.Customer.objects.create(
|
||||
merchant=self.other_merchant,
|
||||
name='客户B',
|
||||
created_by=None,
|
||||
)
|
||||
category = basic_models.ProductCategory.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='品类',
|
||||
product_prefix='FAB',
|
||||
)
|
||||
self.product = basic_models.Product.objects.create(
|
||||
merchant=self.merchant,
|
||||
category=category,
|
||||
name='产品A',
|
||||
human_id='FAB-PO-001',
|
||||
unit=basic_models.ProductUnitEnum.METER,
|
||||
)
|
||||
self.warehouse = basic_models.WareHouse.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='仓库A',
|
||||
mode=basic_models.WareHouseModeEnum.UNRESTRICTED,
|
||||
)
|
||||
self.operator = basic_models.Employee.objects.create(
|
||||
merchant=self.merchant,
|
||||
name='操作员',
|
||||
)
|
||||
|
||||
self.unbound_order = self._create_printing_order('未绑定订单')
|
||||
self.legacy_unbound_order = self._create_printing_order('历史未绑定订单', merchant=None)
|
||||
self.active_bound_order = self._create_printing_order('已绑定有效销售单')
|
||||
self.cancelled_bound_order = self._create_printing_order('只绑定作废销售单')
|
||||
self.other_customer_order = self._create_printing_order('其他客户订单', customer=self.other_customer, merchant=self.other_merchant)
|
||||
|
||||
active_job = self._create_job(self.active_bound_order)
|
||||
cancelled_job = self._create_job(self.cancelled_bound_order)
|
||||
self._create_sales_order_item(active_job, status=business_models.SalesOrderStatusEnum.PENDING)
|
||||
self._create_sales_order_item(cancelled_job, status=business_models.SalesOrderStatusEnum.CANCELLED)
|
||||
|
||||
def _create_printing_order(self, fabric, customer=None, merchant='__default__'):
|
||||
if merchant == '__default__':
|
||||
merchant = self.merchant
|
||||
return printing_models.PrintingOrder.objects.create(
|
||||
merchant=merchant,
|
||||
customer=customer or self.customer,
|
||||
fabric=fabric,
|
||||
width='150cm',
|
||||
)
|
||||
|
||||
def _create_job(self, printing_order):
|
||||
return printing_models.PrintingJob.objects.create(
|
||||
merchant=self.merchant,
|
||||
printing_order=printing_order,
|
||||
product=self.product,
|
||||
quantity=10,
|
||||
unit='米',
|
||||
)
|
||||
|
||||
def _create_sales_order_item(self, printing_job, status):
|
||||
sales_order = business_models.SalesOrder.objects.create(
|
||||
merchant=self.merchant,
|
||||
customer=self.customer,
|
||||
sales_date=datetime.date(2026, 6, 24),
|
||||
operator=self.operator,
|
||||
warehouse=self.warehouse,
|
||||
status=status,
|
||||
)
|
||||
return business_models.SalesOrderItem.objects.create(
|
||||
sales_order=sales_order,
|
||||
product=self.product,
|
||||
printing_job=printing_job,
|
||||
price=Decimal('10'),
|
||||
quantity=Decimal('10'),
|
||||
unit='米',
|
||||
empty_diff_percent=Decimal('0'),
|
||||
num_of_rolls=1,
|
||||
)
|
||||
|
||||
def test_list_orders_without_non_cancelled_sales_order(self):
|
||||
response = self.client.get(self.url, {'customer_id': self.customer.id})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
result_ids = {item['id'] for item in response.data}
|
||||
self.assertIn(self.unbound_order.id, result_ids)
|
||||
self.assertIn(self.legacy_unbound_order.id, result_ids)
|
||||
self.assertIn(self.cancelled_bound_order.id, result_ids)
|
||||
self.assertNotIn(self.active_bound_order.id, result_ids)
|
||||
self.assertNotIn(self.other_customer_order.id, result_ids)
|
||||
|
||||
def test_customer_id_is_required(self):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.data['detail'], 'customer_id 为必填参数')
|
||||
|
||||
def test_customer_id_must_be_integer(self):
|
||||
response = self.client.get(self.url, {'customer_id': 'abc'})
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.data['detail'], 'customer_id 必须为数字')
|
||||
|
||||
|
||||
@override_settings(AGENT_ACCESS_KEY='agent-test-key')
|
||||
class AgentUnshippedShipmentListAPITest(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -6,6 +6,7 @@ from api_v2.views import (
|
||||
QuickCreateEmployeeUserView,
|
||||
RoleListView,
|
||||
PrintingJobByCustomerView,
|
||||
PrintingOrderWithoutSalesOrderView,
|
||||
PrintingJobBatchAdvancePreviewView,
|
||||
PrintingJobBatchAdvanceSubmitView,
|
||||
PrintingJobBatchAddParametersView,
|
||||
@@ -57,6 +58,7 @@ urlpatterns = [
|
||||
path('customers/bind-employee/', CustomerEmployeeBindingView.as_view(), name='api_v2_customer_bind_employee'),
|
||||
path('me/visible-pages/', MyVisiblePagesView.as_view(), name='api_v2_my_visible_pages'),
|
||||
path('printing-jobs/by-customer/', PrintingJobByCustomerView.as_view(), name='api_v2_printing_job_by_customer'),
|
||||
path('printing-orders/without-sales-order/', PrintingOrderWithoutSalesOrderView.as_view(), name='api_v2_printing_order_without_sales_order'),
|
||||
path('printing-jobs/batch-advance/preview/', PrintingJobBatchAdvancePreviewView.as_view(), name='api_v2_printing_job_batch_advance_preview'),
|
||||
path('printing-jobs/batch-advance/', PrintingJobBatchAdvanceSubmitView.as_view(), name='api_v2_printing_job_batch_advance_submit'),
|
||||
path('printing-jobs/batch-add-parameters/', PrintingJobBatchAddParametersView.as_view(), name='api_v2_printing_job_batch_add_parameters'),
|
||||
|
||||
@@ -7,6 +7,7 @@ from .users import QuickCreateEmployeeUserView, RoleListView
|
||||
from .printing import (
|
||||
PrintingJobByCustomerView,
|
||||
PrintingJobV2Serializer,
|
||||
PrintingOrderWithoutSalesOrderView,
|
||||
PrintingJobBatchAdvancePreviewView,
|
||||
PrintingJobBatchAdvanceSubmitView,
|
||||
PrintingJobBatchAddParametersView,
|
||||
@@ -62,6 +63,7 @@ __all__ = [
|
||||
'RoleListView',
|
||||
'PrintingJobByCustomerView',
|
||||
'PrintingJobV2Serializer',
|
||||
'PrintingOrderWithoutSalesOrderView',
|
||||
'PrintingJobBatchAdvancePreviewView',
|
||||
'PrintingJobBatchAdvanceSubmitView',
|
||||
'PrintingJobBatchAddParametersView',
|
||||
|
||||
@@ -12,6 +12,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from basic_info import models as basic_models
|
||||
from business import models as business_models
|
||||
from printing import models as printing_models
|
||||
from api_v1.tasks import (
|
||||
ExternalPrintingOrderSnapshotSyncError,
|
||||
@@ -180,6 +181,73 @@ class PrintingJobByCustomerView(APIView):
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class PrintingOrderWithoutSalesOrderSerializer(serializers.ModelSerializer):
|
||||
"""未关联非作废销售单的印染订单列表序列化器。"""
|
||||
|
||||
customer_name = serializers.CharField(source='customer.name', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = printing_models.PrintingOrder
|
||||
fields = [
|
||||
'id',
|
||||
'human_id',
|
||||
'external_order_id',
|
||||
'customer',
|
||||
'customer_name',
|
||||
'fabric',
|
||||
'width',
|
||||
'is_urgent',
|
||||
'is_invalid',
|
||||
'outgoing_date',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
class PrintingOrderWithoutSalesOrderView(APIView):
|
||||
"""
|
||||
按客户查询尚未关联非作废销售单的印染订单。
|
||||
|
||||
关联路径:
|
||||
PrintingOrder -> PrintingJob -> SalesOrderItem -> SalesOrder
|
||||
已作废 SalesOrder 不计为有效关联。
|
||||
"""
|
||||
|
||||
serializer_class = PrintingOrderWithoutSalesOrderSerializer
|
||||
permission_classes = [permissions.IsAuthenticated, IsPrintingFactory]
|
||||
|
||||
def get(self, request):
|
||||
customer_id = request.query_params.get('customer_id')
|
||||
if not customer_id:
|
||||
return Response({'detail': 'customer_id 为必填参数'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
customer_id_int = int(customer_id)
|
||||
except (TypeError, ValueError):
|
||||
return Response({'detail': 'customer_id 必须为数字'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
merchant = request.user.employee.merchant
|
||||
active_sales_order_items = business_models.SalesOrderItem.objects.filter(
|
||||
printing_job__printing_order_id=OuterRef('pk'),
|
||||
).exclude(
|
||||
sales_order__status=business_models.SalesOrderStatusEnum.CANCELLED,
|
||||
)
|
||||
|
||||
queryset = (
|
||||
printing_models.PrintingOrder.objects
|
||||
.select_related('customer')
|
||||
.filter(customer_id=customer_id_int)
|
||||
.filter(Q(merchant=merchant) | Q(merchant__isnull=True, customer__merchant=merchant))
|
||||
.annotate(_has_active_sales_order=Exists(active_sales_order_items))
|
||||
.filter(_has_active_sales_order=False)
|
||||
.order_by('-created_at', '-id')
|
||||
)
|
||||
|
||||
serializer = self.serializer_class(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class PrintingJobBatchAdvancePreviewRequestSerializer(serializers.Serializer):
|
||||
"""批量推进:预览/校验请求"""
|
||||
|
||||
|
||||
@@ -9,6 +9,13 @@ from flower.common import ModelBase
|
||||
from basic_info import models as basic_info_models
|
||||
|
||||
|
||||
def build_business_human_id(prefix: str, created_at, object_id) -> str:
|
||||
"""生成业务单据的人类可读编号(虚拟编号,不落库)。"""
|
||||
if object_id is None or created_at is None:
|
||||
return ''
|
||||
return f"{prefix}{created_at.year}{created_at.month:02d}{created_at.day:02d}{object_id:06d}"
|
||||
|
||||
|
||||
class PurchaseOrderKindEnum(models.IntegerChoices):
|
||||
"""采购单类型"""
|
||||
WHOLESALE = 1, '大货'
|
||||
@@ -154,9 +161,7 @@ class PurchaseOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCounte
|
||||
|
||||
规则与 SalesOrder.human_id 保持一致:CG + YYYYMMDD{6位id}
|
||||
"""
|
||||
if self.id is None or self.created_at is None:
|
||||
return ""
|
||||
return f"CG{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
|
||||
return build_business_human_id('CG', self.created_at, self.id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '采购单'
|
||||
@@ -309,9 +314,7 @@ class SalesOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCounterpa
|
||||
|
||||
规则:XS + YYYYMMDD{6位id}
|
||||
"""
|
||||
if self.id is None or self.created_at is None:
|
||||
return ""
|
||||
return f"XS{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
|
||||
return build_business_human_id('XS', self.created_at, self.id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '销售单'
|
||||
@@ -505,9 +508,7 @@ class PreSalesOrder(ModelBase):
|
||||
|
||||
规则:YS + YYYYMMDD{6位id}
|
||||
"""
|
||||
if self.id is None or self.created_at is None:
|
||||
return ""
|
||||
return f"YS{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
|
||||
return build_business_human_id('YS', self.created_at, self.id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '预销售单'
|
||||
@@ -710,9 +711,7 @@ class PrePurchaseOrder(ModelBase):
|
||||
规则:YC + YYYYMMDD{6位id}
|
||||
"""
|
||||
|
||||
if self.id is None or self.created_at is None:
|
||||
return ''
|
||||
return f"YC{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
|
||||
return build_business_human_id('YC', self.created_at, self.id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '预采购单'
|
||||
@@ -823,9 +822,7 @@ class PurchaseReturnOrder(OrderItemsAggregationMixin, OrderDirectionMixin, Order
|
||||
|
||||
规则与 SalesOrder.human_id 保持一致:CT + YYYYMMDD{6位id}
|
||||
"""
|
||||
if self.id is None or self.created_at is None:
|
||||
return ""
|
||||
return f"CT{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
|
||||
return build_business_human_id('CT', self.created_at, self.id)
|
||||
|
||||
def get_direction(self) -> int:
|
||||
return -1
|
||||
@@ -955,9 +952,7 @@ class SalesReturnOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCou
|
||||
|
||||
规则与 SalesOrder.human_id 保持一致:XT + YYYYMMDD{6位id}
|
||||
"""
|
||||
if self.id is None or self.created_at is None:
|
||||
return ""
|
||||
return f"XT{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
|
||||
return build_business_human_id('XT', self.created_at, self.id)
|
||||
|
||||
def get_direction(self) -> int:
|
||||
return 1
|
||||
@@ -1149,6 +1144,11 @@ class PaymentOrder(OrderDirectionMixin, OrderCounterpartyMixin, ModelBase):
|
||||
def __str__(self):
|
||||
return f'付款单 {self.id} - {self.supplier.name}'
|
||||
|
||||
@property
|
||||
def human_id(self) -> str:
|
||||
"""人类可读编号(虚拟编号,不落库):FK + YYYYMMDD{6位id}。"""
|
||||
return build_business_human_id('FK', self.created_at, self.id)
|
||||
|
||||
@property
|
||||
def settlement_amount(self) -> Decimal:
|
||||
discount = self.discount_amount or Decimal('0')
|
||||
@@ -1235,6 +1235,11 @@ class ReceiptOrder(OrderDirectionMixin, OrderCounterpartyMixin, ModelBase):
|
||||
def __str__(self):
|
||||
return f'收款单 {self.id} - {self.customer.name}'
|
||||
|
||||
@property
|
||||
def human_id(self) -> str:
|
||||
"""人类可读编号(虚拟编号,不落库):SK + YYYYMMDD{6位id}。"""
|
||||
return build_business_human_id('SK', self.created_at, self.id)
|
||||
|
||||
@property
|
||||
def settlement_amount(self) -> Decimal:
|
||||
discount = self.discount_amount or Decimal('0')
|
||||
|
||||
@@ -2434,6 +2434,7 @@ class _StatementBuilder:
|
||||
remarks: str | None = '',
|
||||
items: List[dict] | None = None,
|
||||
extra: dict | None = None,
|
||||
human_id: str | None = None,
|
||||
) -> dict:
|
||||
items = items or []
|
||||
record = {
|
||||
@@ -2442,6 +2443,7 @@ class _StatementBuilder:
|
||||
'source_type': source_type,
|
||||
'source_label': source_label,
|
||||
'source_id': source_id,
|
||||
'human_id': str(human_id if human_id is not None else source_id),
|
||||
'occurred_at': occurred_at,
|
||||
'recorded_at': recorded_at,
|
||||
'status': status,
|
||||
@@ -2565,6 +2567,7 @@ class _CustomerStatementBuilder(_StatementBuilder):
|
||||
source_type='sales_order',
|
||||
source_label='销售单',
|
||||
source_id=order.id,
|
||||
human_id=order.human_id,
|
||||
occurred_at=order.sales_date,
|
||||
recorded_at=order.created_at,
|
||||
status=order.status,
|
||||
@@ -2599,6 +2602,7 @@ class _CustomerStatementBuilder(_StatementBuilder):
|
||||
source_type='sales_return_order',
|
||||
source_label='销售退货单',
|
||||
source_id=order.id,
|
||||
human_id=order.human_id,
|
||||
occurred_at=order.return_date,
|
||||
recorded_at=order.created_at,
|
||||
status=order.status,
|
||||
@@ -2634,6 +2638,7 @@ class _CustomerStatementBuilder(_StatementBuilder):
|
||||
source_type='receipt_order',
|
||||
source_label='收款单',
|
||||
source_id=order.id,
|
||||
human_id=order.human_id,
|
||||
occurred_at=order.receipt_date,
|
||||
recorded_at=order.created_at,
|
||||
status=order.status,
|
||||
@@ -2674,6 +2679,7 @@ class _CustomerStatementBuilder(_StatementBuilder):
|
||||
source_type=source_type,
|
||||
source_label=source_label,
|
||||
source_id=order.id,
|
||||
human_id=str(order.id),
|
||||
occurred_at=order.occurred_at,
|
||||
recorded_at=order.recorded_at or order.created_at,
|
||||
status=models.ReceiptOrderStatusEnum.APPROVED,
|
||||
@@ -2721,6 +2727,7 @@ class _SupplierStatementBuilder(_StatementBuilder):
|
||||
source_type='purchase_order',
|
||||
source_label='采购单',
|
||||
source_id=order.id,
|
||||
human_id=order.human_id,
|
||||
occurred_at=order.purchase_date,
|
||||
recorded_at=order.created_at,
|
||||
status=order.status,
|
||||
@@ -2755,6 +2762,7 @@ class _SupplierStatementBuilder(_StatementBuilder):
|
||||
source_type='purchase_return_order',
|
||||
source_label='采购退货单',
|
||||
source_id=order.id,
|
||||
human_id=order.human_id,
|
||||
occurred_at=order.return_date,
|
||||
recorded_at=order.created_at,
|
||||
status=order.status,
|
||||
@@ -2787,6 +2795,7 @@ class _SupplierStatementBuilder(_StatementBuilder):
|
||||
source_type='payment_order',
|
||||
source_label='付款单',
|
||||
source_id=order.id,
|
||||
human_id=order.human_id,
|
||||
occurred_at=order.payment_date,
|
||||
recorded_at=order.created_at,
|
||||
status=order.status,
|
||||
|
||||
@@ -260,6 +260,7 @@ class PaymentReceiptServiceTestCase(TestCase):
|
||||
)
|
||||
self.assertFalse(order.is_external_source)
|
||||
self.assertIsNone(order.external_source_id)
|
||||
self.assertEqual(order.human_id, f'FK{order.created_at:%Y%m%d}{order.id:06d}')
|
||||
|
||||
order.is_external_source = True
|
||||
order.external_source_id = 'XT20260001'
|
||||
@@ -269,6 +270,7 @@ class PaymentReceiptServiceTestCase(TestCase):
|
||||
serializer = PaymentOrderSerializer(order)
|
||||
self.assertTrue(serializer.data['is_external_source'])
|
||||
self.assertEqual(serializer.data['external_source_id'], 'XT20260001')
|
||||
self.assertEqual(serializer.data['human_id'], order.human_id)
|
||||
|
||||
def test_receipt_order_external_source_fields_default_and_serialized(self):
|
||||
order = services.create_receipt_order(
|
||||
@@ -280,6 +282,7 @@ class PaymentReceiptServiceTestCase(TestCase):
|
||||
)
|
||||
self.assertFalse(order.is_external_source)
|
||||
self.assertIsNone(order.external_source_id)
|
||||
self.assertEqual(order.human_id, f'SK{order.created_at:%Y%m%d}{order.id:06d}')
|
||||
|
||||
order.is_external_source = True
|
||||
order.external_source_id = 'SK20225323'
|
||||
@@ -289,3 +292,4 @@ class PaymentReceiptServiceTestCase(TestCase):
|
||||
serializer = ReceiptOrderSerializer(order)
|
||||
self.assertTrue(serializer.data['is_external_source'])
|
||||
self.assertEqual(serializer.data['external_source_id'], 'SK20225323')
|
||||
self.assertEqual(serializer.data['human_id'], order.human_id)
|
||||
|
||||
@@ -66,7 +66,7 @@ class BusinessStatementServiceTestCase(TestCase):
|
||||
target_status=business_models.ReceiptOrderStatusEnum.APPROVED,
|
||||
)
|
||||
|
||||
business_models.ExternalCustomerStatementOrder.objects.create(
|
||||
external_sale_order = business_models.ExternalCustomerStatementOrder.objects.create(
|
||||
merchant=merchant,
|
||||
customer=customer,
|
||||
category=business_models.ExternalCustomerStatementCategoryEnum.SALE,
|
||||
@@ -77,7 +77,7 @@ class BusinessStatementServiceTestCase(TestCase):
|
||||
zk_amount=Decimal('3'),
|
||||
items_payload=[{'product_name': '外部销售'}],
|
||||
)
|
||||
business_models.ExternalCustomerStatementOrder.objects.create(
|
||||
external_return_order = business_models.ExternalCustomerStatementOrder.objects.create(
|
||||
merchant=merchant,
|
||||
customer=customer,
|
||||
category=business_models.ExternalCustomerStatementCategoryEnum.SALE_RETURN,
|
||||
@@ -102,11 +102,19 @@ class BusinessStatementServiceTestCase(TestCase):
|
||||
},
|
||||
)
|
||||
sales_record = next(record for record in payload['records'] if record['source_type'] == 'sales_order')
|
||||
self.assertEqual(sales_record['human_id'], sales_order.human_id)
|
||||
self.assertEqual(sales_record['items'][0]['quantity'], Decimal('10'))
|
||||
self.assertEqual(sales_record['items'][0]['quantity_of_rolls'], [6, 4])
|
||||
return_record = next(record for record in payload['records'] if record['source_type'] == 'sales_return_order')
|
||||
self.assertEqual(return_record['human_id'], return_order.human_id)
|
||||
receipt_record = next(record for record in payload['records'] if record['source_type'] == 'receipt_order')
|
||||
self.assertEqual(receipt_record['human_id'], receipt_order.human_id)
|
||||
self.assertEqual(receipt_record['positive_amount'], Decimal('-2.00'))
|
||||
self.assertEqual(receipt_record['negative_amount'], Decimal('20.00'))
|
||||
external_sale_record = next(record for record in payload['records'] if record['source_type'] == 'external_sales_order')
|
||||
self.assertEqual(external_sale_record['human_id'], str(external_sale_order.id))
|
||||
external_return_record = next(record for record in payload['records'] if record['source_type'] == 'external_sales_return_order')
|
||||
self.assertEqual(external_return_record['human_id'], str(external_return_order.id))
|
||||
summary = services.build_statement_summary(payload)
|
||||
self.assertIn('positive_total', summary)
|
||||
self.assertIn('negative_total', summary)
|
||||
@@ -162,9 +170,13 @@ class BusinessStatementServiceTestCase(TestCase):
|
||||
source_types = {record['source_type'] for record in payload['records']}
|
||||
self.assertEqual(source_types, {'purchase_order', 'purchase_return_order', 'payment_order'})
|
||||
purchase_record = next(record for record in payload['records'] if record['source_type'] == 'purchase_order')
|
||||
self.assertEqual(purchase_record['human_id'], purchase_order.human_id)
|
||||
self.assertEqual(purchase_record['items'][0]['quantity'], Decimal('10'))
|
||||
self.assertEqual(purchase_record['items'][0]['num_of_rolls'], 2)
|
||||
return_record = next(record for record in payload['records'] if record['source_type'] == 'purchase_return_order')
|
||||
self.assertEqual(return_record['human_id'], return_order.human_id)
|
||||
payment_record = next(record for record in payload['records'] if record['source_type'] == 'payment_order')
|
||||
self.assertEqual(payment_record['human_id'], payment_order.human_id)
|
||||
self.assertEqual(payment_record['negative_amount'], Decimal('15.00'))
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,10 +1,14 @@
|
||||
"""
|
||||
自定义中间件模块
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import QueryDict
|
||||
from django.http.multipartparser import MultiPartParserError
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -528,6 +528,53 @@ class PrintingJobBatchAdvanceRecordAdmin(admin.ModelAdmin):
|
||||
return obj.printing_jobs.count()
|
||||
|
||||
|
||||
@admin.register(models.PrintingOrderExternalSnapshotSyncAudit)
|
||||
class PrintingOrderExternalSnapshotSyncAuditAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'id',
|
||||
'external_order_id',
|
||||
'printing_order',
|
||||
'operator_user',
|
||||
'operator_employee',
|
||||
'allow_reset_stateflow',
|
||||
'is_success',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
)
|
||||
list_filter = (
|
||||
'is_success',
|
||||
'allow_reset_stateflow',
|
||||
'created_at',
|
||||
)
|
||||
search_fields = (
|
||||
'external_order_id',
|
||||
'printing_order__id',
|
||||
'operator_user__username',
|
||||
'operator_employee__name',
|
||||
'failure_reason',
|
||||
)
|
||||
readonly_fields = (
|
||||
'printing_order',
|
||||
'external_order_id',
|
||||
'operator_user',
|
||||
'operator_employee',
|
||||
'before_snapshot',
|
||||
'allow_reset_stateflow',
|
||||
'is_success',
|
||||
'failure_reason',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
)
|
||||
date_hierarchy = 'created_at'
|
||||
ordering = ('-created_at', '-id')
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(models.PlateOrderTiiaUploadFailure)
|
||||
class PlateOrderTiiaUploadFailureAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'run_date', 'plate_order_id', 'attempts', 'last_attempt_at', 'created_at')
|
||||
|
||||
Reference in New Issue
Block a user