1
0
forked from erp-dev/erp

feat: running on docker fully, and added rabbitmq/worker container

This commit is contained in:
2025-11-25 15:56:13 +08:00
parent cd6a2370fc
commit d90917e764
34 changed files with 2474 additions and 74 deletions

View File

@@ -6,6 +6,7 @@ from unittest.mock import patch, MagicMock
import logging
from basic_info.models import Product, WareHouse, Supplier, ProductUnitEnum, ProductCategory, Merchant, MerchantTypeEnum, WareHouseModeEnum
from business.models import PurchaseOrder
from . import models, services
@@ -83,7 +84,7 @@ class StockServicesTestCase(TestCase):
def setUp(self):
"""每个测试方法执行前的设置"""
# 创建采购单
self.purchase_order = models.PurchaseOrder.objects.create(
self.purchase_order = PurchaseOrder.objects.create(
merchant=self.merchant,
supplier=self.supplier,
order_date=timezone.now().date(),
@@ -456,23 +457,129 @@ class CreateStockChangeRecordWithDetailsTestCase(StockServicesTestCase):
self.assertIn('宽进宽出', str(ctx.exception))
def test_strict_mode_not_implemented_for_restrict_in_out(self):
"""严进严出模式暂不支持"""
def test_restrict_in_out_incoming_behaves_like_strict(self):
"""严进严出模式的入库沿用严谨逻辑"""
self.warehouse_main.mode = WareHouseModeEnum.RESTRICT_IN_OUT
self.warehouse_main.save()
with self.assertRaises(ValueError) as ctx:
record, details, created_count = services.create_stock_change_record_with_details(
merchant=self.merchant,
created_by=None,
type=models.StockChangeTypeEnum.ADD,
warehouse_id=self.warehouse_main.id,
source_type=models.StockChangeSourceEnum.PURCHASE,
source_id=self.purchase_order.id,
products=[{'product': self.product_fabric_a.id, 'quantity': [Decimal('5.00'), Decimal('6.00')]}],
)
self.assertEqual(created_count, 2)
self.assertEqual(record.details.count(), 2)
def test_restrict_in_out_outgoing_consumes_detail(self):
"""严进严出模式的出库需要消耗入库明细"""
self.warehouse_main.mode = WareHouseModeEnum.RESTRICT_IN_OUT
self.warehouse_main.save()
products = [{
'product': self.product_fabric_a.id,
'consume_with': [self.stock_detail_in.id],
}]
record, details, created_count = services.create_stock_change_record_with_details(
merchant=self.merchant,
created_by=None,
type=models.StockChangeTypeEnum.REMOVE,
warehouse_id=self.warehouse_main.id,
source_type=models.StockChangeSourceEnum.SALES,
source_id=123,
products=products,
)
self.assertEqual(created_count, 1)
detail = details[0]
self.stock_detail_in.refresh_from_db()
self.assertTrue(self.stock_detail_in.is_consumed)
self.assertEqual(detail.consume_with_id, self.stock_detail_in.id)
self.assertEqual(detail.quantity, self.stock_detail_in.quantity)
def test_restrict_in_out_outgoing_requires_consume_ids(self):
self.warehouse_main.mode = WareHouseModeEnum.RESTRICT_IN_OUT
self.warehouse_main.save()
with self.assertRaises(ValueError):
services.create_stock_change_record_with_details(
merchant=self.merchant,
created_by=None,
type=models.StockChangeTypeEnum.ADD,
type=models.StockChangeTypeEnum.REMOVE,
warehouse_id=self.warehouse_main.id,
source_type=models.StockChangeSourceEnum.PURCHASE,
source_id=self.purchase_order.id,
products=[{'product': self.product_fabric_a.id, 'quantity': [Decimal('10.00')]}],
source_type=models.StockChangeSourceEnum.SALES,
source_id=123,
products=[{'product': self.product_fabric_a.id}],
)
self.assertIn('严进严出', str(ctx.exception))
def test_restrict_in_out_outgoing_rejects_consumed_detail(self):
self.warehouse_main.mode = WareHouseModeEnum.RESTRICT_IN_OUT
self.warehouse_main.save()
products = [{
'product': self.product_fabric_a.id,
'consume_with': [self.stock_detail_in.id],
}]
services.create_stock_change_record_with_details(
merchant=self.merchant,
created_by=None,
type=models.StockChangeTypeEnum.REMOVE,
warehouse_id=self.warehouse_main.id,
source_type=models.StockChangeSourceEnum.SALES,
source_id=123,
products=products,
)
with self.assertRaises(ValueError):
services.create_stock_change_record_with_details(
merchant=self.merchant,
created_by=None,
type=models.StockChangeTypeEnum.REMOVE,
warehouse_id=self.warehouse_main.id,
source_type=models.StockChangeSourceEnum.SALES,
source_id=124,
products=products,
)
def test_restrict_in_out_outgoing_rejects_other_warehouse_detail(self):
self.warehouse_main.mode = WareHouseModeEnum.RESTRICT_IN_OUT
self.warehouse_main.save()
other_record = models.StockChangeRecord.objects.create(
merchant=self.merchant,
type=models.StockChangeTypeEnum.ADD,
source_type=models.StockChangeSourceEnum.PURCHASE,
warehouse=self.warehouse_backup,
)
other_detail = models.StockChangeDetail.objects.create(
merchant=self.merchant,
stock_change_record=other_record,
product=self.product_fabric_a,
quantity=Decimal('8.00'),
unit=self.unit_meter,
)
products = [{
'product': self.product_fabric_a.id,
'consume_with': [other_detail.id],
}]
with self.assertRaises(ValueError):
services.create_stock_change_record_with_details(
merchant=self.merchant,
created_by=None,
type=models.StockChangeTypeEnum.REMOVE,
warehouse_id=self.warehouse_main.id,
source_type=models.StockChangeSourceEnum.SALES,
source_id=125,
products=products,
)
class CreateStockChangeRecordRelaxedTestCase(StockServicesTestCase):
@@ -748,5 +855,82 @@ class StockServicesIntegrationTestCase(StockServicesTestCase):
self.assertEqual(out_snapshot.quantity_after, 50)
class StockFlowServiceTestCase(TestCase):
"""StockFlowService 统一出入库服务测试"""
def setUp(self):
self.merchant = Merchant.objects.create(
name='统一服务商户',
type=MerchantTypeEnum.FACTORY,
)
self.category = ProductCategory.objects.create(
merchant=self.merchant,
name='面料',
product_prefix='FAB',
)
self.product = Product.objects.create(
merchant=self.merchant,
category=self.category,
name='测试面料',
human_id='FAB-0001',
unit=ProductUnitEnum.METER,
)
self.strict_warehouse = WareHouse.objects.create(
merchant=self.merchant,
name='严谨仓',
mode=WareHouseModeEnum.RESTRICT_IN,
)
self.relaxed_warehouse = WareHouse.objects.create(
merchant=self.merchant,
name='宽松仓',
mode=WareHouseModeEnum.UNRESTRICTED,
)
self.restrict_in_out = WareHouse.objects.create(
merchant=self.merchant,
name='严进严出仓',
mode=WareHouseModeEnum.RESTRICT_IN_OUT,
)
self.service = services.StockFlowService(merchant=self.merchant, created_by=None)
def test_stock_in_strict_mode_uses_quantities(self):
record, details, count = self.service.stock_in(
warehouse_id=self.strict_warehouse.id,
source_type=models.StockChangeSourceEnum.PURCHASE,
source_id=10,
items=[{'product_id': self.product.id, 'quantities': ['5', '3']}],
)
self.assertEqual(count, 2)
self.assertEqual(record.warehouse_id, self.strict_warehouse.id)
self.assertEqual(len(details), 2)
def test_stock_in_relaxed_mode_splits_value(self):
_, details, count = self.service.stock_in(
warehouse_id=self.relaxed_warehouse.id,
source_type=models.StockChangeSourceEnum.PURCHASE,
source_id=11,
items=[{'product_id': self.product.id, 'value': '9', 'num_of_rolls': 4}],
)
self.assertEqual(count, 3) # 4 + 4 + 1
self.assertEqual(len(details), 3)
def test_stock_out_restrict_mode_consumes_details(self):
_, inbound_details, _ = self.service.stock_in(
warehouse_id=self.restrict_in_out.id,
source_type=models.StockChangeSourceEnum.PURCHASE,
source_id=12,
items=[{'product_id': self.product.id, 'quantities': ['6']}],
)
detail_id = inbound_details[0].id
_, outbound_details, _ = self.service.stock_out(
warehouse_id=self.restrict_in_out.id,
source_type=models.StockChangeSourceEnum.SALES,
source_id=13,
items=[{'product_id': self.product.id, 'consume_detail_ids': [detail_id]}],
)
inbound_details[0].refresh_from_db()
self.assertTrue(inbound_details[0].is_consumed)
self.assertEqual(outbound_details[0].consume_with_id, detail_id)
# 禁用测试期间的日志输出
logging.disable(logging.CRITICAL)