Django仓储系统:同商品入库自动合并数量与均价计算方案咨询
解决方案:合并同一商品入库记录并计算加权均价
当然可行!你的需求本质是对同一商品的入库记录做「更新或插入(Upsert)」操作,而非每次都新建对象。下面给你具体的实现思路、代码示例,以及同类场景的其他解决方案:
一、核心实现逻辑
当触发入库操作时,我们需要先判断该商品是否已有对应的Collections记录:
- 如果存在:合并数量,并按照加权平均逻辑计算新的单价(这才是仓储成本核算的正确方式,后面会解释)
- 如果不存在:直接创建新的
Collections记录
二、代码实现示例
你可以在视图函数、信号处理或者自定义服务类中实现这个逻辑,这里以视图处理为例:
from django.shortcuts import get_object_or_404 from django.db import transaction from .models import Products, Collections from decimal import Decimal def process_inbound(request): # 假设从请求中获取入库参数:商品名、入库数量、入库单价 product_name = request.POST.get("product_name") inbound_qnt = int(request.POST.get("inbound_qnt")) inbound_price = Decimal(request.POST.get("inbound_price")) # 先找到对应的商品对象 product = get_object_or_404(Products, name=product_name) # 用事务+锁保证并发入库时的数据一致性 with transaction.atomic(): # 查询或创建该商品的库存汇总记录 collection, is_new = Collections.objects.get_or_create(name=product) if not is_new: # 计算合并后的总数量和加权均价 total_qnt = collection.qnt + inbound_qnt total_cost = collection.qnt * collection.price + inbound_qnt * inbound_price avg_price = total_cost / total_qnt # 更新现有记录 collection.qnt = total_qnt collection.price = avg_price collection.save() else: # 首次入库,直接初始化数量和单价 collection.qnt = inbound_qnt collection.price = inbound_price collection.save() # 后续返回响应或其他逻辑...
三、关键细节说明
- 加权均价的必要性:你例子里写的
(20+15)/2是简单平均,但实际仓储管理中应该用加权平均(总金额/总数量),更符合成本逻辑:比如你举的例子:上周入库20个鼠标(20元/个),今日入库10个(15元/个)
合并后总金额=2020 +1015=550元,总数量30个,均价≈18.33元,而非简单平均的17.5元 - 并发安全:如果是多用户同时入库,一定要用
transaction.atomic()+select_for_update()(或者get_or_create在事务内)避免数据不一致,比如把查询部分改成:collection = Collections.objects.select_for_update().get(name=product) - 模型命名优化:你的
Collections模型里的外键字段叫name有点歧义,建议改成product,这样代码可读性更高。
四、同类问题的其他解决方案
- 封装模型方法:把合并逻辑封装到
Collections模型中,让业务代码更简洁:
之后在业务逻辑里直接调用class Collections(models.Model): name = models.ForeignKey(Products, on_delete=models.CASCADE) qnt = models.IntegerField() price = models.DecimalField(max_digits=20, decimal_places=3, null=True, blank=True) def merge_inbound(self, add_qnt, add_price): total_qnt = self.qnt + add_qnt total_cost = self.qnt * self.price + add_qnt * add_price self.qnt = total_qnt self.price = total_cost / total_qnt self.save()collection.merge_inbound(inbound_qnt, inbound_price)即可。 - 保留入库明细:如果后续需要追溯每一次的入库记录,可以新增
InboundRecord模型存储单次入库详情,Collections只保留当前库存的汇总数据,这样既满足合并需求,又能实现历史溯源。
内容的提问来源于stack exchange,提问作者art_cs
相关产品推荐
相关产品推荐

