forked from erp-dev/erp
feat: relaxed in restrict out
This commit is contained in:
@@ -4,6 +4,7 @@ from django.db.models import Sum
|
||||
from django.utils import timezone
|
||||
from sse.services import push_simple_message_with_object_id
|
||||
from basic_info import models as basic_models
|
||||
from decimal import Decimal, InvalidOperation
|
||||
import logging
|
||||
from typing import List, Dict, Any, Tuple
|
||||
|
||||
@@ -11,6 +12,26 @@ from typing import List, Dict, Any, Tuple
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _raise_unimplemented_mode():
|
||||
raise ValueError('仓库出入库模式为【严进严出】,该模式暂未支持创建出入库记录')
|
||||
|
||||
|
||||
def _ensure_strict_mode(warehouse: basic_models.WareHouse):
|
||||
if warehouse.mode == basic_models.WareHouseModeEnum.RESTRICT_IN:
|
||||
return
|
||||
if warehouse.mode == basic_models.WareHouseModeEnum.RESTRICT_IN_OUT:
|
||||
_raise_unimplemented_mode()
|
||||
raise ValueError('仓库出入库模式为【宽进宽出】,请使用宽松模式接口创建出入库记录')
|
||||
|
||||
|
||||
def _ensure_relaxed_mode(warehouse: basic_models.WareHouse):
|
||||
if warehouse.mode == basic_models.WareHouseModeEnum.UNRESTRICTED:
|
||||
return
|
||||
if warehouse.mode == basic_models.WareHouseModeEnum.RESTRICT_IN_OUT:
|
||||
_raise_unimplemented_mode()
|
||||
raise ValueError('仓库出入库模式为【严进宽出】,请使用严谨模式接口创建出入库记录')
|
||||
|
||||
|
||||
def create_stock_change_record_with_details(
|
||||
*,
|
||||
merchant: basic_models.Merchant,
|
||||
@@ -48,6 +69,7 @@ def create_stock_change_record_with_details(
|
||||
|
||||
if warehouse.merchant_id != merchant.id:
|
||||
raise ValueError('仓库不属于当前商户')
|
||||
_ensure_strict_mode(warehouse)
|
||||
|
||||
created_details: List[models.StockChangeDetail] = []
|
||||
created_count = 0
|
||||
@@ -96,6 +118,116 @@ def create_stock_change_record_with_details(
|
||||
return stock_change_record, created_details, created_count
|
||||
|
||||
|
||||
def _to_decimal(value, field_name: str) -> Decimal:
|
||||
try:
|
||||
return Decimal(str(value))
|
||||
except (InvalidOperation, TypeError):
|
||||
raise ValueError(f'{field_name} 必须是合法的数值')
|
||||
|
||||
|
||||
def _split_quantities(total: Decimal, unit_size: Decimal) -> List[Decimal]:
|
||||
if total <= 0:
|
||||
raise ValueError('quantity.value 必须大于 0')
|
||||
if unit_size <= 0:
|
||||
raise ValueError('quantity.unit_count 必须大于 0')
|
||||
|
||||
quantities: List[Decimal] = []
|
||||
num_full = int(total // unit_size)
|
||||
remainder = total % unit_size
|
||||
|
||||
if num_full == 0:
|
||||
quantities.append(total)
|
||||
else:
|
||||
quantities.extend([unit_size] * num_full)
|
||||
if remainder > 0:
|
||||
quantities.append(remainder)
|
||||
|
||||
return quantities
|
||||
|
||||
|
||||
def create_stock_change_record_relaxed(
|
||||
*,
|
||||
merchant: basic_models.Merchant,
|
||||
created_by,
|
||||
type: int,
|
||||
warehouse_id: int,
|
||||
source_type: int,
|
||||
source_id: int | None = None,
|
||||
products: List[Dict[str, Any]] | None = None,
|
||||
) -> Tuple[models.StockChangeRecord, List[models.StockChangeDetail], int]:
|
||||
"""
|
||||
宽松模式:根据总数量和单条数量自动拆分明细
|
||||
"""
|
||||
|
||||
if not merchant:
|
||||
raise ValueError('必须提供商户信息')
|
||||
if not warehouse_id:
|
||||
raise ValueError('必须提供仓库ID')
|
||||
if not products:
|
||||
raise ValueError('产品列表不能为空')
|
||||
|
||||
try:
|
||||
warehouse = basic_models.WareHouse.objects.get(id=warehouse_id)
|
||||
except basic_models.WareHouse.DoesNotExist:
|
||||
raise ValueError(f'仓库ID {warehouse_id} 不存在')
|
||||
|
||||
if warehouse.merchant_id != merchant.id:
|
||||
raise ValueError('仓库不属于当前商户')
|
||||
_ensure_relaxed_mode(warehouse)
|
||||
|
||||
created_details: List[models.StockChangeDetail] = []
|
||||
|
||||
with transaction.atomic():
|
||||
stock_change_record = models.StockChangeRecord.objects.create(
|
||||
type=type,
|
||||
warehouse=warehouse,
|
||||
source_type=source_type,
|
||||
source_id=source_id,
|
||||
merchant=merchant,
|
||||
created_by=created_by,
|
||||
)
|
||||
logger.info('创建宽松模式库存变动记录 ID: %s', stock_change_record.id)
|
||||
|
||||
for product_data in products:
|
||||
product_id = product_data.get('product')
|
||||
quantity_data = product_data.get('quantity') or {}
|
||||
|
||||
if not product_id:
|
||||
raise ValueError('产品ID不能为空')
|
||||
|
||||
total_value = quantity_data.get('value')
|
||||
unit_size = quantity_data.get('unit_count', 1)
|
||||
|
||||
total_value = _to_decimal(total_value, 'quantity.value')
|
||||
unit_size = _to_decimal(unit_size, 'quantity.unit_count')
|
||||
|
||||
try:
|
||||
product = basic_models.Product.objects.get(id=product_id)
|
||||
except basic_models.Product.DoesNotExist:
|
||||
raise ValueError(f'产品ID {product_id} 不存在')
|
||||
|
||||
if product.merchant_id != merchant.id:
|
||||
raise ValueError(f'产品ID {product_id} 不属于当前商户')
|
||||
|
||||
quantities = _split_quantities(total_value, unit_size)
|
||||
|
||||
for quantity in quantities:
|
||||
detail = models.StockChangeDetail.objects.create(
|
||||
stock_change_record=stock_change_record,
|
||||
product=product,
|
||||
quantity=quantity,
|
||||
merchant=merchant,
|
||||
unit=product.unit,
|
||||
)
|
||||
created_details.append(detail)
|
||||
|
||||
if warehouse.merchant.auto_complete_stock_change:
|
||||
make_stock_change_completed(stock_change_record)
|
||||
logger.info('自动确认宽松模式库存变动记录 ID: %s', stock_change_record.id)
|
||||
|
||||
return stock_change_record, created_details, len(created_details)
|
||||
|
||||
|
||||
def find_inventory(product_id: int, warehouse_id: int) -> models.Inventory | None:
|
||||
"""根据产品ID和仓库ID查找库存记录"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user