1
0
forked from erp-dev/erp
Files
erpnew/stock/services.py

134 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from . import models
from django.db.models import Sum
from django.utils import timezone
from sse.services import push_simple_message_with_object_id
import logging
logger = logging.getLogger(__name__)
def find_inventory(product_id: int, warehouse_id: int) -> models.Inventory | None:
"""根据产品ID和仓库ID查找库存记录"""
try:
inventory = models.Inventory.objects.get(
product_id=product_id,
warehouse_id=warehouse_id,
)
return inventory
except models.Inventory.DoesNotExist:
return None
def create_stock_snapshot(stock_change_detail: models.StockChangeDetail, inventory: models.Inventory) -> models.StockSnapshot:
"""创建库存快照记录"""
if not stock_change_detail or stock_change_detail.id is None:
raise ValueError("无效的库存变动明细记录")
delta = stock_change_detail.quantity if stock_change_detail.stock_change_record.is_incoming else -stock_change_detail.quantity
rolls_delta = 1 if stock_change_detail.stock_change_record.is_incoming else -1
# 必须在库存变动发生后创建快照
snapshot = models.StockSnapshot(
merchant_id=stock_change_detail.merchant_id,
product_id=inventory.product_id,
warehouse_id=inventory.warehouse_id,
delta=delta,
quantity_before=inventory.quantity - delta,
quantity_after=inventory.quantity,
stock_change_record_id=stock_change_detail.stock_change_record.id,
unit=stock_change_detail.unit,
num_of_rolls=inventory.num_of_rolls + rolls_delta,
)
snapshot.save()
logger.info(
f'创建库存快照库存ID: {inventory.id}, 数量: {inventory.quantity}, '
f'匹数: {inventory.num_of_rolls}'
)
return snapshot
def make_stock_change_completed(stock_change_record: models.StockChangeRecord) -> bool:
"""处理库存变动完成后的逻辑, 实际扣减库存也在这里发生"""
# TODO: 这里可以添加事务处理以确保数据一致性
if not stock_change_record or stock_change_record.id is None:
# 无效的库存变动记录
return False
if stock_change_record.is_finished:
return True
# TODO: 逻辑更改 = 出入库单现在不记录库存总量了
# 需要根据产品分别统计变动数量(要考虑不同单位)
for detail in stock_change_record.details.all():
detail: models.StockChangeDetail
logger.info(
f'处理库存变动明细产品ID: {detail.product_id}, '
f'数量: {detail.quantity}, 单位: {detail.get_unit_display()}'
)
inventory_record = find_inventory(
product_id=detail.product_id,
warehouse_id=stock_change_record.warehouse_id,
)
# 确定方向
positive = 1 if stock_change_record.is_incoming else -1
if inventory_record:
# 更新现有库存记录
inventory_record.quantity += detail.quantity * positive
inventory_record.num_of_rolls += positive
logger.info(
f'更新库存记录 {inventory_record.id},新数量: {inventory_record.quantity}, '
f'新匹数: {inventory_record.num_of_rolls}'
)
else:
# 创建新库存记录
inventory_record = models.Inventory(
merchant_id=stock_change_record.merchant_id,
product_id=detail.product_id,
warehouse_id=stock_change_record.warehouse_id,
quantity=detail.quantity * positive,
num_of_rolls=1 if stock_change_record.is_incoming else -1,
)
logger.info(
f'创建新库存记录 {inventory_record.id},数量: {inventory_record.quantity}, '
f'匹数: {inventory_record.num_of_rolls}'
)
inventory_record.save()
# 发送库存变动通知
push_simple_message_with_object_id(
event_type='stock_change',
message=f'产品ID {detail.product_id} 位于 {stock_change_record.warehouse_id} 的库存已更新',
object_id=inventory_record.id
)
# 无论库存记录原本是否存在都创建库存快照
create_stock_snapshot(detail, inventory_record)
# 所有明细处理完毕,标记库存变动记录为已完成
stock_change_record.is_finished = True
stock_change_record.finished_at = timezone.now()
stock_change_record.save()
logger.info(
f'库存变动记录 {stock_change_record.id} 标记为已完成。'
)
return True
def get_frozen_stock_count(product_id: int, warehouse_id: int) -> int:
"""获取指定产品在指定仓库的冻结库存数量"""
total_frozen = models.StockFreeze.objects.filter(
product_id=product_id,
warehouse_id=warehouse_id,
status=models.StockFreezeStatusEnum.FROZEN
).aggregate(total=Sum('quantity'))['total'] or 0
return total_frozen