1
0
forked from erp-dev/erp

feat: all the business models supported human_id now but it wont change statements api. Added new field 'order_quantity' into sales_order model (integer, allow null).

This commit is contained in:
2026-01-30 19:31:02 +08:00
parent cebaf85f6f
commit b7178f72b2
9 changed files with 127 additions and 5 deletions

View File

@@ -127,7 +127,7 @@ class SalesReturnOrderItemAdmin(admin.ModelAdmin):
@admin.register(models.SalesOrder)
class SalesOrderAdmin(admin.ModelAdmin):
list_display = ('id', 'human_id', 'customer', 'sales_date', 'operator', 'status', '_total_amount', '_diff_quantity', '_total_quantity', 'created_at')
list_display = ('id', 'human_id', 'customer', 'sales_date', 'operator', 'status', 'order_quantity', '_total_amount', '_diff_quantity', '_total_quantity', 'created_at')
search_fields = ('customer__name',)
list_filter = ('operator', 'warehouse', 'status', 'created_at')
ordering = ('-created_at',)

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-01-30 10:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('business', '0019_alter_salesorderitem_quantity_of_rolls'),
]
operations = [
migrations.AddField(
model_name='salesorder',
name='order_quantity',
field=models.IntegerField(blank=True, null=True, verbose_name='下单数量'),
),
]

View File

@@ -133,6 +133,17 @@ class PurchaseOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCounte
def __str__(self):
return f'采购订单 {self.id} - {self.supplier.name}'
@property
def human_id(self) -> str:
"""
人类可读编号(虚拟编号,不落库)。
规则与 printing.PrintingOrder.human_id / SalesOrder.human_id 保持一致YYYYMMDD{6位id}
"""
if self.id is None or self.created_at is None:
return ""
return f"{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
class Meta:
verbose_name = '采购单'
verbose_name_plural = '采购单'
@@ -252,6 +263,7 @@ class SalesOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCounterpa
default=SalesOrderStatusEnum.PENDING,
verbose_name='状态',
)
order_quantity = models.IntegerField(null=True, blank=True, verbose_name='下单数量')
remarks = models.TextField(blank=True, null=True, verbose_name='备注')
def __str__(self):
@@ -405,6 +417,17 @@ class PurchaseReturnOrder(OrderItemsAggregationMixin, OrderDirectionMixin, Order
def __str__(self):
return f'采购退货 {self.id} - {self.supplier.name}'
@property
def human_id(self) -> str:
"""
人类可读编号(虚拟编号,不落库)。
规则与 printing.PrintingOrder.human_id / SalesOrder.human_id 保持一致YYYYMMDD{6位id}
"""
if self.id is None or self.created_at is None:
return ""
return f"{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
def get_direction(self) -> int:
return -1
@@ -519,6 +542,17 @@ class SalesReturnOrder(OrderItemsAggregationMixin, OrderDirectionMixin, OrderCou
def __str__(self):
return f'销售退货 {self.id} - {self.customer.name}'
@property
def human_id(self) -> str:
"""
人类可读编号(虚拟编号,不落库)。
规则与 printing.PrintingOrder.human_id / SalesOrder.human_id 保持一致YYYYMMDD{6位id}
"""
if self.id is None or self.created_at is None:
return ""
return f"{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}"
def get_direction(self) -> int:
return 1

View File

@@ -150,6 +150,22 @@ class PurchaseOrderServiceTestCase(TestCase):
self.assertEqual(item.split_quantity_of_rolls(), [10, 5])
mock_delay.assert_not_called()
def test_purchase_order_human_id_generation(self):
purchase_order = services.create_purchase_order(
merchant=self.merchant,
supplier=self.supplier,
order_date=timezone.now().date(),
warehouse=self.warehouse_strict,
operator=self.operator,
items=self.strict_items,
created_by=self.user,
)
self.assertIsNotNone(purchase_order.human_id)
self.assertEqual(
purchase_order.human_id,
f"{purchase_order.created_at.strftime('%Y%m%d')}{purchase_order.id:06d}",
)
def test_review_purchase_order_approval_triggers_task(self):
purchase_order = services.create_purchase_order(
merchant=self.merchant,
@@ -605,6 +621,22 @@ class PurchaseReturnServiceTestCase(TestCase):
self.assertEqual(item.quantity_of_rolls, '4,2')
self.assertEqual(item.split_quantity_of_rolls(), [4, 2])
def test_purchase_return_order_human_id_generation(self):
order = services.create_purchase_return_order(
merchant=self.merchant,
supplier=self.supplier,
return_date=timezone.now().date(),
warehouse=self.warehouse_strict,
operator=self.operator,
items=self.strict_items,
created_by=self.user,
)
self.assertIsNotNone(order.human_id)
self.assertEqual(
order.human_id,
f"{order.created_at.strftime('%Y%m%d')}{order.id:06d}",
)
def test_review_purchase_return_order_triggers_task_and_balance(self):
order = services.create_purchase_return_order(
merchant=self.merchant,
@@ -744,6 +776,22 @@ class SalesReturnServiceTestCase(TestCase):
item = order.items.first()
self.assertEqual(item.split_quantity_of_rolls(), [])
def test_sales_return_order_human_id_generation(self):
order = services.create_sales_return_order(
merchant=self.merchant,
customer=self.customer,
return_date=timezone.now().date(),
warehouse=self.warehouse_relaxed,
operator=self.operator,
items=[{'product_id': self.product.id, 'quantity': 50, 'num_of_rolls': 2, 'price': '19.5'}],
created_by=self.user,
)
self.assertIsNotNone(order.human_id)
self.assertEqual(
order.human_id,
f"{order.created_at.strftime('%Y%m%d')}{order.id:06d}",
)
def test_review_sales_return_order_updates_balance(self):
order = services.create_sales_return_order(
merchant=self.merchant,