InvenTree 生产工单需求数量计算:基础数量、损耗率、准备数量与圆整规则的完整解析
【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree
在 InvenTree 中创建生产工单(Build Order)时,系统会根据装配件的 BOM(物料清单)自动计算每种组件的“需求数量”(Required Quantity),并据此生成生产明细行(BuildLine)。本篇技术文章以官方文档 Required Build Quantity 为核心,完整讲解需求数量的四级计算公式(基础数量 → 损耗率 → 准备数量 → 圆整倍数),并结合BomItem模型源码、生产工单的信号触发链与单元测试用例,说明这一计算在 InvenTree 中的实际实现方式、触发时机与边界行为。
读完本文,你将掌握:
- 需求数量计算的完整公式链路,以及每个 BOM 字段的含义、默认值与取值约束;
- 官方示例(100 件装配、最终需求 325)的逐步推导;
- 源码层面
BomItem.get_required_quantity()的实现细节,以及生产工单创建/变更时如何自动触发重算; - 单元测试对该公式的验证方式,便于你在定制或排查数据问题时快速定位。
一、需求数量计算的总体流程
当一张新的生产工单被创建时,InvenTree 会针对该工单所生产装配件的每一条 BOM 行项目,计算生产该批次所需组件的总数量。文档给出的计算按以下四个阶段顺序进行,其中后三个阶段均为可选字段,缺省值为“无影响”(0 或空):
- 基础数量(Base Quantity):BOM 行项目的
Quantity字段,表示生产一件装配体所需的该组件数量;乘以生产工单的装配数量,得到基础需求。 - 损耗率(Attrition):以百分比形式计入生产过程中预期的损耗,在基础需求上按百分比加码。
- 准备数量(Setup Quantity):计入生产过程中的固定损耗(如试产、换线消耗),以绝对数量加到需求上。
- 圆整倍数(Rounding Multiple):将最终需求量向上圆整到指定倍数的整数倍,满足整包装、标准包装等采购/生产要求。
这一流程在源码中的入口是 BomItem.get_required_quantity 方法,它在单条 BOM 行项目上按文档描述的顺序完成全部四步计算(详见后文源码解析)。
二、四个计算公式详解
2.1 基础数量(Base Quantity)
基础数量由 BOM 行项目的Quantity字段定义,即“生产一件装配体所需的组件件数”。将其乘以正在生产的装配体数量,即得到该组件的总需求量:
Required Quantity = Base Quantity * Number of Assemblies2.2 损耗率(Attrition)
BOM 行项目的Attrition字段用于计入生产过程中预期的损耗,以Base Quantity的百分比表示(例如 2%)。当指定了非零损耗百分比时,它作用在已算出的Required Quantity之上:
Required Quantity = Required Quantity * (1 + Attrition Percentage)损耗率是可选字段,未指定时默认为 0%。
2.3 准备数量(Setup Quantity)
Setup Quantity字段用于计入生产过程中的固定损耗——它是为保证整批生产顺利完成而额外需要的组件数量,直接加到已算出的需求数量上:
Required Quantity = Required Quantity + Setup Quantity准备数量是可选字段,未指定时默认为 0。
注意它与损耗率的语义差异:损耗率是随批量线性放大的比例项,而准备数量是不随批量变化的固定项(例如产线换型试机消耗的部件,生产 1 件和生产 100 件都需要同样的准备量)。
2.4 圆整倍数(Rounding Multiple)
Rounding Multiple字段将计算出的需求数量向上圆整到指定倍数的最近整数倍,用于保证数量为整数或满足特定包装规格:
Required Quantity = ceil(Required Quantity / Rounding Multiple) * Rounding Multiple圆整倍数是可选字段,未指定时不对计算结果做任何圆整。
2.5 字段定义与取值约束(源码级补充)
文档只说明了字段的语义,而在 InvenTree 的模型定义中(BomItem 字段定义,位于src/backend/InvenTree/part/models.py),这些字段的精确类型与校验规则如下,对配置 BOM 时输入合法值有直接参考价值:
| 字段 | 模型定义 | 默认值 | 校验规则 | 说明 |
|---|---|---|---|---|
quantity | DecimalField(max_digits=15, decimal_places=5) | 1.0 | MinValueValidator(0) | 基础数量;可带小数(5 位精度) |
attrition | DecimalField(max_digits=6, decimal_places=3) | 0 | MinValueValidator(0)、MaxValueValidator(100) | 损耗百分比,取值范围 0–100 |
setup_quantity | DecimalField(max_digits=15, decimal_places=5) | 0 | MinValueValidator(0) | 固定准备/试产数量 |
rounding_multiple | DecimalField(max_digits=15, decimal_places=5),可空 | None | MinValueValidator(0) | 为空时表示不圆整 |
两点值得注意:
attrition的上限由MaxValueValidator(100)硬性约束,即损耗率不能超过 100%,这在模型层就拒绝了非法配置;- 从源码结构看,BOM 行项目还包含一个文档未提及的
piece_count(件数)字段,用于“按长度裁切”类物料(如线缆、管材):quantity表示每件的尺寸/长度,piece_count表示每件装配需要的裁切段数,源码中的总需求量公式因此扩展为quantity * piece_count * build_quantity(见 get_required_quantity 的 docstring 与测试 test_piece_count_multiplier)。这是文档公式之上的一个实现细节,默认piece_count=1时行为与文档完全一致。
三、官方示例计算:100 件装配 → 需求 325
文档给出了一个贯穿四步的完整算例。考虑一条 BOM 行项目:
- 基础数量(Base Quantity):3
- 损耗率(Attrition):2%(0.02)
- 准备数量(Setup Quantity):10
- 圆整倍数(Rounding Multiple):25
当生产工单要生产 100 件装配体时,需求数量按如下顺序推导:
Required Quantity = Base Quantity * Number of Assemblies = 3 * 100 = 300 Attrition Value = Required Quantity * Attrition Percentage = 300 * 0.02 = 6 Required Quantity = Required Quantity + Attrition Value = 300 + 6 = 306 Required Quantity = Required Quantity + Setup Quantity = 306 + 10 = 316 Required Quantity = ceil(Required Quantity / Rounding Multiple) * Rounding Multiple = ceil(316 / 25) * 25 = 13 * 25 = 325因此该组件的最终需求生产数量为325。该计算在新生产工单创建时由系统自动执行,无需人工干预。
四、源码实现:BomItem.get_required_quantity()的逐步对应
上述公式在 InvenTree 中的权威实现位于 BomItem.get_required_quantity(src/backend/InvenTree/part/models.py)。将源码逻辑与文档公式逐段对照:
def get_required_quantity(self, build_quantity: float) -> float: # 1) 基础需求(quantity 为单件用量,piece_count 为裁切件数) required = self.quantity * self.piece_count * build_quantity # 2) 损耗率:仅当 attrition > 0 时,乘以 (1 + attrition/100) if self.attrition > 0: attrition = Decimal(self.attrition) / Decimal(100) required *= 1 + attrition # 3) 准备数量:仅当 setup_quantity > 0 时直接累加 if self.setup_quantity > 0: required += Decimal(self.setup_quantity) # 4) 圆整:仅当 rounding_multiple 非空且 > 0 时,向上圆整 if self.rounding_multiple and self.rounding_multiple > 0: round_up = Decimal(self.rounding_multiple) value = Decimal(required) value = math.ceil(value / round_up) * round_up required = float(value) return required实现上有几个细节值得注意:
- 顺序与文档严格一致:先乘批量、再乘损耗、再加准备量、最后圆整。由于圆整在最后,前一步的浮点结果也会被向上取整,保证最终值严格落在倍数格点上;
- 全程使用
Decimal运算:三个计算分支都包裹在try/except中,异常时调用log_error('bom_item.get_required_quantity')记录而不抛出,保证单个 BOM 数据异常不会阻断整张工单的行项目生成; - 可选分支显式判空:
rounding_multiple and self.rounding_multiple > 0的写法同时处理了“字段为空(None)”和“字段为 0”两种“不圆整”的语义,与文档中“未指定时不做圆整”的描述一致; - 裁切件数的扩展:文档公式的第一步在这里体现为
quantity * piece_count * build_quantity,默认piece_count=1时退化为文档公式。
4.1 触发时机:生产工单的信号链
文档提到“新生产工单创建时自动计算”。在源码中,这一自动化的实现是一条 Django 信号链(build/models.py 的 after_save_build):
- 创建工单(
created=True):Build实例保存后触发after_save_build信号,调用 Build.create_build_line_items。它遍历装配件的所有非虚拟BOM 行(get_bom_items(include_virtual=False)),对每条调用bom_item.get_required_quantity(self.quantity)得到数量,批量创建BuildLine对象(batch_size=250); - 修改工单数量(
created=False):同一信号回调中调用 Build.update_build_line_items,逐行用新的self.quantity重新计算line.quantity并bulk_update,保证 BOM 明细行始终与工单数量同步; - BOM 变更时反向同步:part/models.py 中的 update_bom_build_lines 信号接收器 在
BomItem创建/更新时,通过后台任务 update_build_order_lines 找到所有引用该 BOM 行的活跃生产工单,逐张重算对应BuildLine.quantity,BOM 行指向虚拟件时则会删除该明细行; - 库存告警联动:工单创建后还会执行 check_build_stock 任务,比较需求数量与可用库存,库存不足时向订阅用户推送“生产工单需要额外库存”的通知。
4.2 逆向计算:can_build_quantity()与unallocated_quantity
需求数量还反向服务于两个生产决策:
- BomItem.can_build_quantity:给定可用库存,反推该 BOM 行最多能生产多少件装配——先把准备数量从可用库存中扣除,再按
quantity * (1 + attrition/100)折算每件装配的消耗量; - BuildLine.unallocated_quantity:从需求数量中扣除已消耗(
consumed)与已分配(allocated)数量,得出该行还需要分配的库存量,is_fully_allocated/is_overallocated判断均以此为基础(build/models.py)。
因此“需求数量”不仅是报表数字,而是驱动库存分配、消耗校验与告警通知的核心输入。
五、单元测试:公式的正确性验证
InvenTree 在 part/test_bom_item.py 中为四个阶段分别编写了测试,是验证本文公式理解的可靠依据:
- 损耗率(test_attrition):
quantity=300、attrition=5%、生产 10 件 →get_required_quantity(10) == 3150(即 3000 + 5%); - 准备数量(test_setup_quantity):
quantity=3默认setup_quantity=0时get_required_quantity(10) == 30;改为setup_quantity=5后 →8(3+5)与35(30+5),确认准备数量不随批量放大; - 圆整倍数(test_round_up):
quantity=3、rounding_multiple=17时,生产 1/2/5/10/100 件分别得到17/17/17/34/306,精确验证了ceil向上圆整语义;测试还进一步创建生产工单(quantity=21)断言BuildLine自动创建且数量为3*21=63 → 68(63 向上圆整到 17 的倍数),印证了第 4.1 节的信号链; - 裁切件数(test_piece_count_multiplier):
quantity=200mm、piece_count=10、生产 5 件 → 总长 10000mm,验证源码对文档公式的扩展。
六、小结
InvenTree 的“需求生产数量”是一套四级、可选、顺序确定的计算公式:基础数量 × 批量乘以(1 + 损耗率),再加准备数量,最后按圆整倍数向上取整。文档定义的公式与源码实现 BomItem.get_required_quantity 一一对应,并由 part/test_bom_item.py 中的分组测试完整覆盖。生产工单的创建与数量变更通过 Django 信号自动调用 create_build_line_items / update_build_line_items 完成重算,BOM 编辑则由 update_build_order_lines 后台任务反向同步所有活跃工单——整条链路上需求数量始终是自动维护的派生数据。
配置 BOM 时的实践建议:把与批量成比例的正常损耗放进Attrition,把换线/试产等固定消耗放进Setup Quantity,采购有整包要求时设置Rounding Multiple,并注意Attrition的合法范围是 0–100 的百分比值。如需了解 BOM 行的其他属性(可选件、可消耗件、校验和等),可继续阅读 BOM 文档 与 生产示例文档。
【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考