You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
    
    # 后续返回响应或其他逻辑...

三、关键细节说明

  1. 加权均价的必要性:你例子里写的(20+15)/2是简单平均,但实际仓储管理中应该用加权平均(总金额/总数量),更符合成本逻辑:

    比如你举的例子:上周入库20个鼠标(20元/个),今日入库10个(15元/个)
    合并后总金额=2020 +1015=550元,总数量30个,均价≈18.33元,而非简单平均的17.5元

  2. 并发安全:如果是多用户同时入库,一定要用transaction.atomic()+select_for_update()(或者get_or_create在事务内)避免数据不一致,比如把查询部分改成:
    collection = Collections.objects.select_for_update().get(name=product)
    
  3. 模型命名优化:你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 14:27:56