forked from erp-dev/erp
feat: field pre_purchase_order_id for purchase_order (allow null)
This commit is contained in:
@@ -230,6 +230,22 @@ class PurchaseOrderAPITestCase(TestCase):
|
|||||||
self.assertEqual(response.data['human_id'], expected_human_id)
|
self.assertEqual(response.data['human_id'], expected_human_id)
|
||||||
mock_delay.assert_not_called()
|
mock_delay.assert_not_called()
|
||||||
|
|
||||||
|
def test_create_purchase_order_with_pre_purchase_binding(self):
|
||||||
|
pre_order = business_models.PrePurchaseOrder.objects.create(
|
||||||
|
merchant=self.merchant,
|
||||||
|
supplier=self.supplier,
|
||||||
|
warehouse=self.warehouse_strict,
|
||||||
|
operator=self.employee,
|
||||||
|
created_by=self.user,
|
||||||
|
)
|
||||||
|
payload = {**self.strict_payload, 'from_pre_purchase_order_id': pre_order.id}
|
||||||
|
with patch('business.services.create_purchase_order_stock_entries.delay'):
|
||||||
|
response = self.client.post('/api/v1/purchase-orders/', payload, format='json')
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
order = business_models.PurchaseOrder.objects.get(id=response.data['id'])
|
||||||
|
self.assertEqual(order.from_pre_purchase_order_id, pre_order.id)
|
||||||
|
|
||||||
def test_bind_purchase_order_stock_change_record(self):
|
def test_bind_purchase_order_stock_change_record(self):
|
||||||
MerchantSetting.objects.filter(
|
MerchantSetting.objects.filter(
|
||||||
merchant=self.merchant,
|
merchant=self.merchant,
|
||||||
@@ -321,6 +337,13 @@ class PurchaseOrderAPITestCase(TestCase):
|
|||||||
|
|
||||||
def test_update_purchase_order_success(self):
|
def test_update_purchase_order_success(self):
|
||||||
order_id = self._create_purchase_order(self.strict_payload)
|
order_id = self._create_purchase_order(self.strict_payload)
|
||||||
|
pre_order = business_models.PrePurchaseOrder.objects.create(
|
||||||
|
merchant=self.merchant,
|
||||||
|
supplier=self.supplier,
|
||||||
|
warehouse=self.warehouse_relaxed,
|
||||||
|
operator=self.employee,
|
||||||
|
created_by=self.user,
|
||||||
|
)
|
||||||
payload = {
|
payload = {
|
||||||
'supplier': self.supplier.id,
|
'supplier': self.supplier.id,
|
||||||
'warehouse': self.warehouse_relaxed.id,
|
'warehouse': self.warehouse_relaxed.id,
|
||||||
@@ -334,10 +357,13 @@ class PurchaseOrderAPITestCase(TestCase):
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
'remarks': '更新采购单',
|
'remarks': '更新采购单',
|
||||||
|
'from_pre_purchase_order_id': pre_order.id,
|
||||||
}
|
}
|
||||||
response = self.client.put(f'/api/v1/purchase-orders/{order_id}/', payload, format='json')
|
response = self.client.put(f'/api/v1/purchase-orders/{order_id}/', payload, format='json')
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(response.data['remarks'], '更新采购单')
|
self.assertEqual(response.data['remarks'], '更新采购单')
|
||||||
|
updated_order = business_models.PurchaseOrder.objects.get(id=order_id)
|
||||||
|
self.assertEqual(updated_order.from_pre_purchase_order_id, pre_order.id)
|
||||||
|
|
||||||
def test_update_purchase_order_rejects_non_pending(self):
|
def test_update_purchase_order_rejects_non_pending(self):
|
||||||
order_id = self._create_purchase_order(self.relaxed_payload)
|
order_id = self._create_purchase_order(self.relaxed_payload)
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class PurchaseOrderView(StockChangeViewMixin, views.APIView):
|
|||||||
order_date = data.get('order_date')
|
order_date = data.get('order_date')
|
||||||
items = data.get('items', [])
|
items = data.get('items', [])
|
||||||
remarks = data.get('remarks', '')
|
remarks = data.get('remarks', '')
|
||||||
|
from_pre_purchase_order_id = data.get('from_pre_purchase_order_id')
|
||||||
|
|
||||||
if not supplier_id:
|
if not supplier_id:
|
||||||
return Response({'error': '缺少供应商 ID'}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': '缺少供应商 ID'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
@@ -126,6 +127,7 @@ class PurchaseOrderView(StockChangeViewMixin, views.APIView):
|
|||||||
operator=operator,
|
operator=operator,
|
||||||
items=items,
|
items=items,
|
||||||
remarks=remarks,
|
remarks=remarks,
|
||||||
|
from_pre_purchase_order_id=from_pre_purchase_order_id,
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
)
|
)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
@@ -192,6 +194,7 @@ class PurchaseOrderDetailView(StockChangeViewMixin, views.APIView):
|
|||||||
if not isinstance(items, list) or not items:
|
if not isinstance(items, list) or not items:
|
||||||
return Response({'error': 'items 需要为非空数组'}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': 'items 需要为非空数组'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
remarks = data.get('remarks', purchase_order.remarks)
|
remarks = data.get('remarks', purchase_order.remarks)
|
||||||
|
from_pre_purchase_order_id = data.get('from_pre_purchase_order_id')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
updated_order = business_services.update_purchase_order(
|
updated_order = business_services.update_purchase_order(
|
||||||
@@ -201,6 +204,7 @@ class PurchaseOrderDetailView(StockChangeViewMixin, views.APIView):
|
|||||||
warehouse=warehouse,
|
warehouse=warehouse,
|
||||||
items=items,
|
items=items,
|
||||||
remarks=remarks,
|
remarks=remarks,
|
||||||
|
from_pre_purchase_order_id=from_pre_purchase_order_id,
|
||||||
)
|
)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|||||||
16
business/migrations/0027_purchaseorder_from_pre_purchase.py
Normal file
16
business/migrations/0027_purchaseorder_from_pre_purchase.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('business', '0026_salesorder_from_pre_sales_order'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='from_pre_purchase_order_id',
|
||||||
|
field=models.BigIntegerField(blank=True, null=True, verbose_name='关联预采购单ID'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -130,6 +130,11 @@ class PurchaseOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCounte
|
|||||||
default=PurchaseOrderStatusEnum.PENDING,
|
default=PurchaseOrderStatusEnum.PENDING,
|
||||||
verbose_name='状态',
|
verbose_name='状态',
|
||||||
)
|
)
|
||||||
|
from_pre_purchase_order_id = models.BigIntegerField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name='关联预采购单ID',
|
||||||
|
)
|
||||||
remarks = models.TextField(blank=True, null=True, verbose_name='备注')
|
remarks = models.TextField(blank=True, null=True, verbose_name='备注')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ def create_purchase_order(
|
|||||||
items: List[Dict[str, Any]],
|
items: List[Dict[str, Any]],
|
||||||
remarks: str | None = '',
|
remarks: str | None = '',
|
||||||
created_by=None,
|
created_by=None,
|
||||||
|
from_pre_purchase_order_id: int | None = None,
|
||||||
) -> models.PurchaseOrder:
|
) -> models.PurchaseOrder:
|
||||||
"""
|
"""
|
||||||
创建采购订单并触发异步创建入库单任务。
|
创建采购订单并触发异步创建入库单任务。
|
||||||
@@ -214,6 +215,7 @@ def create_purchase_order(
|
|||||||
operator=operator,
|
operator=operator,
|
||||||
warehouse=warehouse,
|
warehouse=warehouse,
|
||||||
remarks=remarks,
|
remarks=remarks,
|
||||||
|
from_pre_purchase_order_id=from_pre_purchase_order_id,
|
||||||
)
|
)
|
||||||
bulk_objects = [
|
bulk_objects = [
|
||||||
models.PurchaseOrderItem(
|
models.PurchaseOrderItem(
|
||||||
@@ -246,6 +248,7 @@ def update_purchase_order(
|
|||||||
warehouse: basic_info_models.WareHouse | None = None,
|
warehouse: basic_info_models.WareHouse | None = None,
|
||||||
items: List[Dict[str, Any]] | None = None,
|
items: List[Dict[str, Any]] | None = None,
|
||||||
remarks: str | None = '',
|
remarks: str | None = '',
|
||||||
|
from_pre_purchase_order_id: int | None = None,
|
||||||
) -> models.PurchaseOrder:
|
) -> models.PurchaseOrder:
|
||||||
"""
|
"""
|
||||||
更新采购订单(仅限审批中状态)。
|
更新采购订单(仅限审批中状态)。
|
||||||
@@ -273,8 +276,17 @@ def update_purchase_order(
|
|||||||
purchase_order.purchase_date = new_order_date
|
purchase_order.purchase_date = new_order_date
|
||||||
purchase_order.warehouse = new_warehouse
|
purchase_order.warehouse = new_warehouse
|
||||||
purchase_order.remarks = remarks
|
purchase_order.remarks = remarks
|
||||||
|
if from_pre_purchase_order_id is not None:
|
||||||
|
purchase_order.from_pre_purchase_order_id = from_pre_purchase_order_id
|
||||||
purchase_order.save(
|
purchase_order.save(
|
||||||
update_fields=['supplier', 'purchase_date', 'warehouse', 'remarks', 'updated_at']
|
update_fields=[
|
||||||
|
'supplier',
|
||||||
|
'purchase_date',
|
||||||
|
'warehouse',
|
||||||
|
'remarks',
|
||||||
|
'from_pre_purchase_order_id',
|
||||||
|
'updated_at',
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
purchase_order.items.all().delete()
|
purchase_order.items.all().delete()
|
||||||
|
|||||||
@@ -586,16 +586,16 @@ CELERY_BEAT_SCHEDULE = {
|
|||||||
if MDY_ISOLATED_MERCHANT_ID and MDY_ISOLATED_MERCHANT_ID > 0:
|
if MDY_ISOLATED_MERCHANT_ID and MDY_ISOLATED_MERCHANT_ID > 0:
|
||||||
CELERY_BEAT_SCHEDULE.update(
|
CELERY_BEAT_SCHEDULE.update(
|
||||||
{
|
{
|
||||||
'mdy_product_sync_isolated': {
|
# 'mdy_product_sync_isolated': {
|
||||||
'task': 'api_v1.tasks.sync_mdy_products_for_merchant',
|
# 'task': 'api_v1.tasks.sync_mdy_products_for_merchant',
|
||||||
'schedule': crontab(minute='3,13,23,33,43,53'),
|
# 'schedule': crontab(minute='3,13,23,33,43,53'),
|
||||||
'kwargs': {
|
# 'kwargs': {
|
||||||
'merchant_id': MDY_ISOLATED_MERCHANT_ID,
|
# 'merchant_id': MDY_ISOLATED_MERCHANT_ID,
|
||||||
'page_size': MDY_SYNC_PAGE_SIZE,
|
# 'page_size': MDY_SYNC_PAGE_SIZE,
|
||||||
'max_pages': MDY_SYNC_MAX_PAGES,
|
# 'max_pages': MDY_SYNC_MAX_PAGES,
|
||||||
'max_records': MDY_SYNC_MAX_RECORDS,
|
# 'max_records': MDY_SYNC_MAX_RECORDS,
|
||||||
},
|
# },
|
||||||
},
|
# },
|
||||||
# 'mdy_customer_sync_isolated': {
|
# 'mdy_customer_sync_isolated': {
|
||||||
# 'task': 'api_v1.tasks.sync_mdy_customers_for_merchant',
|
# 'task': 'api_v1.tasks.sync_mdy_customers_for_merchant',
|
||||||
# 'schedule': crontab(minute='1,11,21,31,41,51'),
|
# 'schedule': crontab(minute='1,11,21,31,41,51'),
|
||||||
|
|||||||
Reference in New Issue
Block a user