forked from erp-dev/erp
fix
This commit is contained in:
@@ -229,3 +229,180 @@ class PreSalesOrderAPITestCase(TestCase):
|
||||
self.assertEqual(convert_resp.data['status'], business_models.SalesOrderStatusEnum.PENDING)
|
||||
sales_order = business_models.SalesOrder.objects.get(id=convert_resp.data['id'])
|
||||
self.assertEqual(sales_order.from_pre_sales_order_id, order_id)
|
||||
|
||||
def test_detail_filters_cancelled_allocation_records(self):
|
||||
"""单体查询应过滤掉已撤销的配货记录"""
|
||||
resp = self.client.post('/api/v1/pre-sales-orders/', self.payload, format='json')
|
||||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
||||
order_id = resp.data['id']
|
||||
item_id = resp.data['items'][0]['id']
|
||||
|
||||
# 创建库存明细
|
||||
stock_record = stock_models.StockChangeRecord.objects.create(
|
||||
merchant=self.merchant,
|
||||
type=stock_models.StockChangeTypeEnum.ADD,
|
||||
warehouse=self.warehouse,
|
||||
source_type=stock_models.StockChangeSourceEnum.PURCHASE,
|
||||
created_by=self.user,
|
||||
)
|
||||
stock_detail1 = stock_models.StockChangeDetail.objects.create(
|
||||
merchant=self.merchant,
|
||||
product=self.product,
|
||||
unit=ProductUnitEnum.METER,
|
||||
stock_change_record=stock_record,
|
||||
quantity='10',
|
||||
)
|
||||
stock_detail2 = stock_models.StockChangeDetail.objects.create(
|
||||
merchant=self.merchant,
|
||||
product=self.product,
|
||||
unit=ProductUnitEnum.METER,
|
||||
stock_change_record=stock_record,
|
||||
quantity='5',
|
||||
)
|
||||
|
||||
# 创建两条配货记录
|
||||
item = business_models.PreSalesOrderItem.objects.get(id=item_id)
|
||||
allocation1 = allocation_services.create_allocation_record(
|
||||
item=item,
|
||||
stock_ids=[stock_detail1.id],
|
||||
quantity='10',
|
||||
unit='米',
|
||||
scanned_by=self.employee,
|
||||
)
|
||||
allocation2 = allocation_services.create_allocation_record(
|
||||
item=item,
|
||||
stock_ids=[stock_detail2.id],
|
||||
quantity='5',
|
||||
unit='米',
|
||||
scanned_by=self.employee,
|
||||
)
|
||||
|
||||
# 撤销第一条记录
|
||||
allocation_services.cancel_allocation_record_with_unfreeze(record=allocation1)
|
||||
|
||||
# 查询单体
|
||||
detail_resp = self.client.get(f'/api/v1/pre-sales-orders/{order_id}/')
|
||||
self.assertEqual(detail_resp.status_code, status.HTTP_200_OK)
|
||||
records = detail_resp.data['items'][0]['allocation_records']
|
||||
|
||||
# 应该只包含未撤销的记录
|
||||
self.assertEqual(len(records), 1)
|
||||
self.assertEqual(records[0]['id'], allocation2.id)
|
||||
self.assertEqual(records[0]['status'], business_models.AllocationRecordStatusEnum.ACTIVE)
|
||||
|
||||
def test_list_includes_progress_fields(self):
|
||||
"""列表查询应包含allocated_quantity和progress字段"""
|
||||
# 创建订单
|
||||
resp = self.client.post('/api/v1/pre-sales-orders/', self.payload, format='json')
|
||||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
||||
order_id = resp.data['id']
|
||||
item_id = resp.data['items'][0]['id']
|
||||
|
||||
# 创建库存并配货
|
||||
stock_record = stock_models.StockChangeRecord.objects.create(
|
||||
merchant=self.merchant,
|
||||
type=stock_models.StockChangeTypeEnum.ADD,
|
||||
warehouse=self.warehouse,
|
||||
source_type=stock_models.StockChangeSourceEnum.PURCHASE,
|
||||
created_by=self.user,
|
||||
)
|
||||
stock_detail = stock_models.StockChangeDetail.objects.create(
|
||||
merchant=self.merchant,
|
||||
product=self.product,
|
||||
unit=ProductUnitEnum.METER,
|
||||
stock_change_record=stock_record,
|
||||
quantity='12.5',
|
||||
)
|
||||
item = business_models.PreSalesOrderItem.objects.get(id=item_id)
|
||||
allocation_services.create_allocation_record(
|
||||
item=item,
|
||||
stock_ids=[stock_detail.id],
|
||||
quantity='5.0',
|
||||
unit='米',
|
||||
scanned_by=self.employee,
|
||||
)
|
||||
|
||||
# 查询列表
|
||||
list_resp = self.client.get('/api/v1/pre-sales-orders/')
|
||||
self.assertEqual(list_resp.status_code, status.HTTP_200_OK)
|
||||
self.assertIn('results', list_resp.data)
|
||||
|
||||
# 查找当前订单
|
||||
order_data = None
|
||||
for order in list_resp.data['results']:
|
||||
if order['id'] == order_id:
|
||||
order_data = order
|
||||
break
|
||||
|
||||
self.assertIsNotNone(order_data)
|
||||
# 验证进度字段存在
|
||||
self.assertIn('allocated_quantity', order_data)
|
||||
self.assertIn('progress', order_data)
|
||||
# 需求量12.5,已配货5.0
|
||||
self.assertEqual(order_data['allocated_quantity'], '5.00')
|
||||
# 进度 = 5.0 / 12.5 = 0.4
|
||||
self.assertEqual(order_data['progress'], '0.4000')
|
||||
|
||||
def test_list_progress_excludes_cancelled_allocations(self):
|
||||
"""列表查询的进度字段应排除已撤销的配货记录"""
|
||||
resp = self.client.post('/api/v1/pre-sales-orders/', self.payload, format='json')
|
||||
self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
|
||||
order_id = resp.data['id']
|
||||
item_id = resp.data['items'][0]['id']
|
||||
|
||||
# 创建库存
|
||||
stock_record = stock_models.StockChangeRecord.objects.create(
|
||||
merchant=self.merchant,
|
||||
type=stock_models.StockChangeTypeEnum.ADD,
|
||||
warehouse=self.warehouse,
|
||||
source_type=stock_models.StockChangeSourceEnum.PURCHASE,
|
||||
created_by=self.user,
|
||||
)
|
||||
stock_detail1 = stock_models.StockChangeDetail.objects.create(
|
||||
merchant=self.merchant,
|
||||
product=self.product,
|
||||
unit=ProductUnitEnum.METER,
|
||||
stock_change_record=stock_record,
|
||||
quantity='10',
|
||||
)
|
||||
stock_detail2 = stock_models.StockChangeDetail.objects.create(
|
||||
merchant=self.merchant,
|
||||
product=self.product,
|
||||
unit=ProductUnitEnum.METER,
|
||||
stock_change_record=stock_record,
|
||||
quantity='5',
|
||||
)
|
||||
|
||||
# 创建两条配货,然后撤销一条
|
||||
item = business_models.PreSalesOrderItem.objects.get(id=item_id)
|
||||
allocation1 = allocation_services.create_allocation_record(
|
||||
item=item,
|
||||
stock_ids=[stock_detail1.id],
|
||||
quantity='10',
|
||||
unit='米',
|
||||
scanned_by=self.employee,
|
||||
)
|
||||
allocation2 = allocation_services.create_allocation_record(
|
||||
item=item,
|
||||
stock_ids=[stock_detail2.id],
|
||||
quantity='2.5',
|
||||
unit='米',
|
||||
scanned_by=self.employee,
|
||||
)
|
||||
allocation_services.cancel_allocation_record_with_unfreeze(record=allocation1)
|
||||
|
||||
# 查询列表
|
||||
list_resp = self.client.get('/api/v1/pre-sales-orders/')
|
||||
self.assertEqual(list_resp.status_code, status.HTTP_200_OK)
|
||||
|
||||
order_data = None
|
||||
for order in list_resp.data['results']:
|
||||
if order['id'] == order_id:
|
||||
order_data = order
|
||||
break
|
||||
|
||||
self.assertIsNotNone(order_data)
|
||||
# 应该只统计未撤销的配货(2.5),而不是10+2.5
|
||||
self.assertEqual(order_data['allocated_quantity'], '2.50')
|
||||
# 进度 = 2.5 / 12.5 = 0.2
|
||||
self.assertEqual(order_data['progress'], '0.2000')
|
||||
|
||||
@@ -122,13 +122,20 @@ class PreSalesOrderItemSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class PreSalesOrderItemDetailSerializer(PreSalesOrderItemSerializer):
|
||||
allocation_records = AllocationRecordSerializer(many=True, read_only=True)
|
||||
allocation_records = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta(PreSalesOrderItemSerializer.Meta):
|
||||
fields = PreSalesOrderItemSerializer.Meta.fields + [
|
||||
'allocation_records',
|
||||
]
|
||||
|
||||
def get_allocation_records(self, obj: business_models.PreSalesOrderItem):
|
||||
"""返回非已撤销的配货记录"""
|
||||
active_records = obj.allocation_records.exclude(
|
||||
status=business_models.AllocationRecordStatusEnum.CANCELLED
|
||||
)
|
||||
return AllocationRecordSerializer(active_records, many=True).data
|
||||
|
||||
|
||||
class PreSalesOrderSerializer(serializers.ModelSerializer):
|
||||
human_id = serializers.CharField(read_only=True)
|
||||
@@ -136,6 +143,8 @@ class PreSalesOrderSerializer(serializers.ModelSerializer):
|
||||
warehouse_name = serializers.CharField(source='warehouse.name', read_only=True)
|
||||
operator_name = serializers.CharField(source='operator.name', read_only=True)
|
||||
items = PreSalesOrderItemSerializer(many=True, read_only=True)
|
||||
allocated_quantity = serializers.SerializerMethodField(read_only=True)
|
||||
progress = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = business_models.PreSalesOrder
|
||||
@@ -153,6 +162,8 @@ class PreSalesOrderSerializer(serializers.ModelSerializer):
|
||||
'remarks',
|
||||
'created_at',
|
||||
'items',
|
||||
'allocated_quantity',
|
||||
'progress',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id',
|
||||
@@ -162,8 +173,34 @@ class PreSalesOrderSerializer(serializers.ModelSerializer):
|
||||
'warehouse_name',
|
||||
'operator_name',
|
||||
'items',
|
||||
'allocated_quantity',
|
||||
'progress',
|
||||
]
|
||||
|
||||
def get_allocated_quantity(self, obj: business_models.PreSalesOrder) -> str:
|
||||
"""计算整单已配货总量"""
|
||||
total = Decimal('0')
|
||||
for item in obj.items.all():
|
||||
result = item.allocation_records.filter(
|
||||
status=business_models.AllocationRecordStatusEnum.ACTIVE
|
||||
).aggregate(total=Sum('quantity'))
|
||||
total += result.get('total') or Decimal('0')
|
||||
return str(total)
|
||||
|
||||
def get_progress(self, obj: business_models.PreSalesOrder) -> str:
|
||||
"""计算整单配货进度(已配货总量/总需求量)"""
|
||||
required_total = Decimal('0')
|
||||
allocated_total = Decimal('0')
|
||||
for item in obj.items.all():
|
||||
required_total += item.quantity or Decimal('0')
|
||||
result = item.allocation_records.filter(
|
||||
status=business_models.AllocationRecordStatusEnum.ACTIVE
|
||||
).aggregate(total=Sum('quantity'))
|
||||
allocated_total += result.get('total') or Decimal('0')
|
||||
if required_total <= 0:
|
||||
return '0'
|
||||
return str((allocated_total / required_total).quantize(Decimal('0.0001')))
|
||||
|
||||
|
||||
class PreSalesOrderDetailSerializer(PreSalesOrderSerializer):
|
||||
items = PreSalesOrderItemDetailSerializer(many=True, read_only=True)
|
||||
|
||||
Reference in New Issue
Block a user