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

Django种子销售库存模型开发:按入库批次定价销售需求

嘿,你的需求正好是典型的先进先出(FIFO)库存计价场景,针对种子的固定包装规格来设计模型和业务逻辑就完全能搞定。我帮你梳理一套具体的实现方案,包括模型设计、核心业务逻辑和注意事项:

1. 核心数据库模型设计

首先得把种子品种、包装规格、入库记录这几个核心实体拆解开,每个入库记录绑定对应的品种、包装规格,同时追踪剩余可售数量和入库单价:

from django.db import models
from decimal import Decimal

class SeedVariety(models.Model):
    name = models.CharField(max_length=100, unique=True)
    # 可按需添加描述、品类等扩展字段
    def __str__(self):
        return self.name

class PackageSize(models.Model):
    size = models.IntegerField(
        unique=True, 
        choices=[(3, '3粒装'), (5, '5粒装'), (10, '10粒装')]
    )
    def __str__(self):
        return f"{self.size}粒装"

class InventoryReceipt(models.Model):
    variety = models.ForeignKey(SeedVariety, on_delete=models.CASCADE, related_name='receipts')
    package_size = models.ForeignKey(PackageSize, on_delete=models.CASCADE, related_name='receipts')
    total_quantity = models.PositiveIntegerField()  # 该批次入库总数量
    remaining_quantity = models.PositiveIntegerField()  # 剩余可售数量
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)  # 该批次入库单价
    receipt_date = models.DateTimeField(auto_now_add=True)  # 入库时间,用于FIFO排序

    class Meta:
        ordering = ['receipt_date']  # 默认按入库时间升序,保证先进先出逻辑

    def __str__(self):
        return f"{self.variety.name} - {self.package_size} - 剩余{self.remaining_quantity}份 - 单价{self.unit_price}"

# 可选:出库记录,用于追踪每笔销售对应的入库批次
class InventoryIssue(models.Model):
    receipt = models.ForeignKey(InventoryReceipt, on_delete=models.CASCADE, related_name='issues')
    quantity = models.PositiveIntegerField()
    issue_date = models.DateTimeField(auto_now_add=True)
    # 可按需关联订单、客户等外键

    def __str__(self):
        return f"出库{self.quantity}份 - 来自批次{self.receipt.id}"

模型说明

  • SeedVariety 和 PackageSize 作为基础数据,避免重复存储品种名称和包装规格
  • InventoryReceipt 是核心:remaining_quantity 用来跟踪批次剩余可售量,receipt_date 保证FIFO的顺序
  • 新增入库记录时,可以重写save方法自动同步remaining_quantity和total_quantity:
def save(self, *args, **kwargs):
    if not self.pk:  # 仅新建记录时执行
        self.remaining_quantity = self.total_quantity
    super().save(*args, **kwargs)

2. 核心销售业务逻辑实现

重点是实现按入库顺序扣减库存、计算对应批次价格的逻辑,同时用事务保证数据一致性:

from django.db import transaction

def process_seed_sale(variety_id, package_size_id, sale_quantity):
    """处理种子销售,按FIFO规则扣减对应入库批次的剩余数量"""
    with transaction.atomic():  # 事务保证并发场景下的数据一致性
        # 获取对应品种+包装规格、有剩余库存的入库批次,按入库时间升序
        eligible_receipts = InventoryReceipt.objects.filter(
            variety_id=variety_id,
            package_size_id=package_size_id,
            remaining_quantity__gt=0
        ).order_by('receipt_date')

        remaining_sale = sale_quantity
        total_sale_price = Decimal('0.00')
        issue_records = []

        for receipt in eligible_receipts:
            if remaining_sale <= 0:
                break
            
            # 计算当前批次可扣减的数量
            deduct_qty = min(receipt.remaining_quantity, remaining_sale)
            # 更新批次剩余数量
            receipt.remaining_quantity -= deduct_qty
            receipt.save()
            # 累加该批次的销售金额
            total_sale_price += receipt.unit_price * deduct_qty
            # 生成出库记录(可选)
            issue_records.append(InventoryIssue(receipt=receipt, quantity=deduct_qty))

            remaining_sale -= deduct_qty

        if remaining_sale > 0:
            # 库存不足时抛出异常或返回错误信息
            raise ValueError(f"库存不足,还差{remaining_sale}份")

        # 批量保存出库记录
        InventoryIssue.objects.bulk_create(issue_records)
        return {
            'total_price': total_sale_price,
            'used_batches': issue_records
        }

逻辑说明

  • 用transaction.atomic()包裹整个操作,防止并发销售时出现超卖或数据不一致
  • 按入库顺序遍历可用批次,优先扣减最早入库的库存
  • 如果遍历完所有批次仍未满足销售数量,说明库存不足,抛出异常

3. 辅助查询方法

可以写一个方法,快速查询某个品种+包装规格的可用库存及各批次信息:

def get_available_inventory(variety_id, package_size_id):
    """获取对应品种和包装规格的可用库存及批次详情"""
    receipts = InventoryReceipt.objects.filter(
        variety_id=variety_id,
        package_size_id=package_size_id,
        remaining_quantity__gt=0
    ).order_by('receipt_date')

    total_available = sum(r.remaining_quantity for r in receipts)
    batch_details = [
        {
            'batch_id': r.id,
            'remaining': r.remaining_quantity,
            'unit_price': r.unit_price,
            'in_stock_since': r.receipt_date.strftime('%Y-%m-%d %H:%M')
        } for r in receipts
    ]

    return {
        'total_available': total_available,
        'batches': batch_details
    }

4. 关键注意事项

  • 并发控制:务必使用事务,避免多用户同时销售时出现库存数据错误
  • 价格精度:始终用DecimalField处理价格,避免浮点数精度问题
  • 包装规格约束:通过PackageSize的choices限制只能是3/5/10粒装,也可以添加clean方法做额外校验
  • 库存预警:可以在模型中添加信号或定期任务,监控剩余库存低于阈值的批次

内容的提问来源于stack exchange,提问作者quasimoto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:34:11