1
0
forked from erp-dev/erp

feat: human_id for all objects in business module

This commit is contained in:
2026-06-25 13:41:17 +08:00
parent 70b3a0d246
commit e8bf4b5e49
14 changed files with 305 additions and 22 deletions

View File

@@ -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):

View File

@@ -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'),

View File

@@ -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',

View File

@@ -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):
"""批量推进:预览/校验请求"""