InvenTree 采购管理模块全解析:采购订单、供应商与制造商部分实战指南
【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree
采购(Purchasing)是 InvenTree 开源库存管理系统中将“外部购入”转化为“库存资产”的核心环节。本篇以官方采购文档 docs/docs/purchasing/index.md 为主体,结合其子文档 采购订单、供应商、制造商,并对照order与company应用的源码实现,完整讲解采购订单生命周期、供应商部分与制造商部分的管理方式、收货入库流程以及全部相关全局设置。读完本文,你将能够熟练完成从创建供应商、关联供应商部分、下达采购订单到收货入库的完整采购闭环,并理解其背后的状态机与数据模型逻辑。
采购系统概览
InvenTree 提供了一套完整的采购(Purchasing)体系,用来简化采购流程、管理供应商并跟踪采购订单。其设计目标在于无缝地控制外部物料向 InvenTree 数据库的流入(ingestion),确保所有部件在整个生命周期内都被正确记账与追踪。
整个采购体系由三个核心概念构成,它们也是采购模块的三级结构:
- 采购订单(Purchase Order, PO):向供应商提出的、要求提供特定部件或材料的正式请求,是采购流程的核心载体;
- 供应商部分(Supplier Part):从外部供应商采购的单个可购条目,可与 InvenTree 数据库中的部件(Part)绑定;
- 制造商部分(Manufacturer Part):由特定制造商生产、可能同时由多个供应商销售的部件。
在 订单数据模型 中,PurchaseOrder类继承了Order抽象基类与TotalPriceMixin,其get_absolute_url指向/purchasing/purchase-order/{pk},这也印证了采购模块是围绕「订单(order)— 行项目(line items)— 供应商部分(supplier part)」三层关系建模的。
供应商(Suppliers)
供应商(Supplier)本质上是一种被标记为外部部件/原材料销售方的 公司(Company) 记录。在模型层面,PurchaseOrder.supplier字段通过limit_choices_to={'is_supplier': True}(见 order/models.py)来约束——只有启用了 "Supplier" 属性的公司才能被选为采购订单的供应商。
访问供应商页面
点击主导航栏中的Buy(购物车图标)标签页,在下拉列表中选择Suppliers(大楼图标)选项即可进入供应商列表页面。
权限要求:查看、添加、编辑和删除供应商都需要相应的 采购订单用户权限。
添加供应商
在供应商页面点击New Supplier(加号图标)按钮,会打开 "Create new Supplier" 表单。填写供应商信息(其中Company name和Company description为必填项)后点击Submit提交即可。
供应商 vs 制造商:如果该供应商本身就是直接面向客户销售的制造商,可以在提交表单前勾选
is manufacturer复选框(也可以在之后随时开启)。
从源码角度看,「供应商」只是 Company 模型 上的布尔属性组合,采购功能实际依赖的是SupplierPart(供应商部分)这一数据对象。
供应商部分(Supplier Parts)
供应商部分是链接到某个供应商、被定义为「可采购项目」的记录。每个供应商部分通过 SKU(供应商部件号)唯一标识,并被链接到一个 Part 或 ManufacturerPart 对象;同一 Part 可以有多个供应商提供(见 company/models.py 中 SupplierPart 类注释)。
权限要求:查看、添加、编辑和删除供应商部分同样需要对应的 采购订单用户权限。
添加供应商部分
创建供应商部分有以下三种入口:
- 进入某个 Part 的详情页,点击Suppliers侧边标签页;
- 进入某个 Supplier 的详情页,点击Supplied Parts侧边标签页;
- 进入某个 Manufacturer 的详情页,点击Supplied Parts侧边标签页。
无论从哪个入口进入,点击New Supplier Part(加号图标)按钮打开 "Create new Supplier Part" 表单,填写供应商部分信息后点击Submit提交。
编辑供应商部分
首先进入供应商部分详情页(方式与添加时一致:在 Part 详情页的Part Suppliers表格中点击对应链接,或在 Supplier / Manufacturer 详情页点击对应Supplier Part链接)。加载详情后,点击供应商部分图片旁的编辑图标,修改信息后点击Submit。
禁用供应商部分
供应商部分可以被单独禁用(disable),例如当该供应商部分不再可供采购时。在 InvenTree 系统中禁用后,它将不再出现在新采购订单的可选项中,但任何已引用它的现有采购订单仍会保持完整不受影响。
供应商部分的 "active" 状态在界面中清晰可见:
要改变其 "active" 状态,只需编辑该供应商部分详情并将active属性置为否:
建议:推荐使用「禁用」而非「删除」供应商部分,这样能保护历史数据的完整性。模型层通过
active布尔字段(见 SupplierPart 类文档注释)实现该开关。
删除供应商部分
与编辑类似,先进入供应商部分详情页,然后点击图片旁的红色垃圾桶图标,核对要删除的信息、勾选确认复选框后点击Submit。
供应商部分可用性(Availability)
InvenTree 支持跟踪供应商部分的「可用性」信息。虽然该信息可以手动更新,但结合 InvenTree 插件系统使用时更有价值:一个自定义插件可以周期性地通过供应商 API 请求可用性信息,并为每个供应商部分更新这些数据。可用性信息一旦提供,会显示在供应商部分详情页上。
可用性信息也可以直接在用户界面中手动更新:
在源码中,SupplierPart模型定义了availability_updated字段(见 company/models.py),用于记录可用性数据的最后更新时间,这正是插件定期同步可用性信息的落点。
供应商部分包装规格(Pack Size)
供应商部分可以定义包装规格(pack size),在创建或编辑部分时设定,默认值为 1。
采购时部件是按「包」购买的,这一点会在采购订单中被考虑:如果包装规格为 5 的供应商部分以数量 4 采购,收货时将有 20 个部件加入库存(4 × 5 = 20)。
手动添加库存时,供应商部分可以按「包」或按「单个部件」添加,这是为了允许录入已拆包的物品。将 "Use pack size" 标志(API 中为use_pack_size)设为 True,即可按包添加。
从实现上看,SupplierPart的base_quantity(quantity)方法(见 company/models.py)负责将采购数量换算为基础部件数量;在 收货流程 中,stock_quantity = supplier_part.base_quantity(quantity)正是包装规格计算的实际落地逻辑。
关于捆绑套件(Bundled Items):包装规格只表示同一部件的多个单位。如果供应商把多种不同部件打包成一个可购项目(套件/组合),请参阅 捆绑套件(Bundled Items) 文档。
制造商(Manufacturers)
制造商(Manufacturer)是一种被标记为外部部件/原材料生产方的 公司 记录。制造商可以是也可以不是这些部件的供应商。
权限要求:查看、添加、编辑和删除制造商需要相应的 采购订单用户权限。
点击Buy导航标签页,在下拉列表中选择Manufacturers(工厂图标)选项即可访问制造商列表。
添加制造商
在制造商页面点击New Manufacturer按钮打开 "Create new Manufacturer" 表单,填写制造商信息(Company name和Company description必填)后点击Submit。
制造商 vs 供应商:如果制造商直接向客户销售,可以在提交前勾选
is supplier复选框(或之后开启)。采购订单完全依赖供应商部分(supplier parts),因此该制造商也必须同时被设置为供应商,才能真正参与采购流程。
制造商部分(Manufacturer Parts)
制造商部分被链接到一个制造商、被定义为「可生产项目」。
添加制造商部分
有两种入口:
- 进入 Part 详情页,点击Suppliers标签页;
- 进入 Manufacturer 详情页,点击Manufactured Parts标签页。
点击New Manufacturer Part按钮打开 "Create New Manufacturer Part" 表单,填写信息后点击Submit。
编辑制造商部分
进入制造商部分详情页(在 Part 详情页的Part Manufacturers表格中点击MPN链接,或在 Manufacturer 详情页的Manufactured Parts标签页点击MPN链接),点击图片旁的编辑图标,修改后提交。
删除制造商部分
与编辑类似进入详情页,点击图片旁的红色垃圾桶图标,核对信息、勾选确认复选框后提交。
采购订单(Purchase Orders)
采购订单是采购系统的核心。它用于跟踪从供应商和制造商处购买了哪些部件,从而将外部购入的物品转换为库存条目(stock items / inventory)。
在数据模型层面,PurchaseOrder(见 order/models.py)拥有以下关键字段:
| 字段 | 含义 |
|---|---|
reference | 订单引用号,全局唯一,默认由generate_next_purchase_order_reference自动生成 |
status | 订单状态(PENDING / PLACED / ON_HOLD / COMPLETE / CANCELLED 等) |
supplier | 供货公司(仅is_supplier=True的公司可选) |
supplier_reference | 供应商侧的订单参考代码 |
target_date | 期望交付日期,超过该日期订单将标记为逾期 |
destination | 收货目的地库存位置(StockLocation) |
responsible | 负责管理该订单的用户或组 |
project_code | 订单关联的项目代码 |
contact/address | 订单联系人及公司地址 |
查看采购订单
从主导航菜单选择Purchasing,再从侧边栏选择Purchase Orders即可进入采购订单显示页面:
有以下两种视图模式:
表格视图(Table View):提供采购订单列表,可根据用户提供的参数进行过滤,仅显示订单子集。
日历视图(Calendar View):基于每个订单指定的各种日期,以日历方式展示未完成的采购订单。
采购订单状态码
每个采购订单都有一个特定的状态码,表示订单的当前状态。状态码定义于 order/status_codes.py:
| 状态 | 数值 | 含义 |
|---|---|---|
PENDING | 10 | 订单待处理(尚未下达) |
PLACED | 20 | 订单已下达给供应商 |
ON_HOLD | 25 | 订单被挂起 |
COMPLETE | 30 | 订单已完成 |
CANCELLED | 40 | 订单已取消 |
LOST | 50 | 订单丢失 |
RETURNED | 60 | 订单被退回 |
其中开放订单(OPEN)组包含 PENDING、ON_HOLD、PLACED;失败订单(FAILED)组包含 CANCELLED、LOST、RETURNED。采购订单状态支持 自定义状态(custom states)。
这些状态转换由 状态机装饰器inventree_transition驱动:place_order()允许从 PENDING/ON_HOLD 转到 PLACED;complete_order()要求订单处于 PLACED;hold_order()允许从 PENDING/PLACED 转到 ON_HOLD;cancel_order()则允许从 PENDING/ON_HOLD/PLACED 取消。每次转换都会触发相应事件(如PurchaseOrderEvents.PLACED),并通知订阅用户。
采购订单货币
可以为单个采购订单指定货币代码。如果未指定,将使用 供应商 上设置的默认货币;此外,还可以为每个行项目单独指定货币。
因此,在确定采购订单中每个行项目的成本时,优先级顺序如下:
- 行项目货币(Line item currency)
- 采购订单货币(Purchase order currency)
- 供应商货币(Supplier currency)
- 默认(基础)货币(Default / base currency)
在源码中,TotalPriceMixin.currency属性(见 order/models.py)精确实现了这一优先级链:先读order_currency,再回退到公司货币,最后使用currency_code_default()。而calculate_total_price()(见 order/models.py)会遍历所有行项目与额外行项目,通过convert_money进行汇率换算,并按(1 - discount / 100)应用折扣百分比来计算订单总价。
创建采购订单
在采购订单页面点击New Purchase Order(加号图标)按钮,打开 "Create Purchase Order" 表单。
采购订单必须关联到一个具体的供应商,请从现有供应商列表中选择一个。
注意:只有启用了 "Supplier" 属性的公司才会显示并可选。
填写其余订单信息后点击Submit。
采购订单引用号(Reference)
每个采购订单通过其Reference字段唯一标识,默认格式为PO-{ref:04d}(例如PO-0001)。更多信息请阅读 引用字段(reference fields)。该模式由全局设置PURCHASEORDER_REFERENCE_PATTERN控制(见下文设置章节),订单保存时会调用rebuild_reference_field自动重建引用号(见 order/models.py)。
添加行项目(Line Items)
在采购订单详情页,选择Order Items标签页,点击Add Line Item按钮即可将部件链接到采购订单。
打开 "Add Line Item" 表单后,从列表中选择一个供应商部分。
注意:只有来自该采购订单所选供应商的部分才会显示并可选择。这一约束在 模型校验 中强制实现:
add_line_item()会检查supplier_part.supplier != self.supplier并抛出ValidationError。
自动定价(Auto Pricing):启用Auto Pricing选项后,将根据 供应商部分定价数据 依据行项目数量自动计算Unit Price。启用期间Unit Price字段自动计算、不可手动编辑。该选项默认关闭。
折扣(Discount):每个行项目可以应用可选的 折扣 百分比。
上传文件导入行项目
可以上传从供应商处导出的采购订单文件,而不是手动逐条录入行项目。点击Add Line Item按钮旁的Upload File按钮,按照引导步骤操作即可。
支持格式:此流程仅支持表格数据,支持的格式包括:CSV、TSV、XLS、XLSX、JSON 和 YAML。
额外行项目(Extra Line Items)
行项目 必须引用特定的供应商部分,而额外行项目(Extra Line Items)则可用于任何需要在订单中传达的其他逐项信息——例如运费(freight charges)或服务费(service fees)。额外行项目与普通行项目一样,支持可选的折扣百分比。在TotalPriceMixin.calculate_total_price()中,额外行项目与普通行项目一起参与总价计算(见 order/models.py)。
下达订单(Issue Order)
添加完所有行项目后,点击主采购订单详情面板上的发送图标按钮,确认订单已提交给供应商。此操作对应模型中的place_order()转换(见 order/models.py),会将状态从 PENDING/ON_HOLD 推进到 PLACED,写入issue_date,并通知负责人员及订阅用户。
收货(Receive Line Items)
收到订单中的全部物品后,采购订单会将行项目转换为库存条目(stock items)。
有两种方式将条目标记为 "received":
- 逐条标记:点击每个行项目上的对勾(clipboard-check)按钮;
- 整体标记:点击主采购订单详情面板上的对勾按钮,并确认订单中的所有条目均已收到。
权限说明:将行项目标记为已收货需要 "Purchase order" 的 ADD 权限。
在源码层面,receive_line_items()(见 order/models.py)是收货流程的核心:它要求订单状态必须为 PLACED,然后为每个已收行项目创建新的StockItem记录(写入supplier_part、purchase_order、purchase_price、location、batch等字段),并为每条记录生成RECEIVED_AGAINST_PURCHASE_ORDER类型的库存跟踪记录(tracking entry),同时触发PurchaseOrderEvents.ITEM_RECEIVED事件。值得注意的实现细节包括:
- 通过
select_for_update()锁定行项目行,防止并发收货时的丢失更新(lost update); - 若启用了
PURCHASEORDER_CONVERT_CURRENCY设置,收货时会将采购价换算为基础货币(见 order/models.py); - 收货完成后若
pending_line_count == 0且启用了PURCHASEORDER_AUTO_COMPLETE,订单会自动完成(见 order/models.py)。
物品位置(Item Location)
收货时,物品的位置必须指定,有多种指定方式:
- 订单目的地(Order Destination):采购订单的destination字段可以设置为特定位置。收货时,位置将默认使用该目的地位置。
- 行项目位置(Line Item Location):每个行项目可以设置特定位置。收货时,位置将默认使用行项目位置。注意:行项目级别指定的目的地会覆盖订单级别指定的目的地。
已收物品(Received Items)
每个被标记为 "received" 的物品都会自动转换为一个库存条目(stock item)。要查看从该采购订单创建的库存条目列表,点击Received Items侧边标签页即可。
序列号(Serial Numbers)
如果被收货的部件是 可追溯部件(trackable),则可以在收货时可选地输入序列号。如果提供,则为收到的每个单位创建一个单独的(序列化的)库存条目。
序列号并非接收可追溯部件的必需项——如果留空,则为收到的数量创建一个(非序列化的)库存条目。例如,在按 外部制造订单 接收一批单位、且该批单位将在制造流程后期 序列化 的场景下,这种处理方式非常有用。源码中receive_line_items()通过serials参数区分两种模式:提供序列号时调用StockItem._create_serial_numbers()逐个创建,否则创建一个聚合库存条目(见 order/models.py)。
物品价值货币(Item Value Currency)
采购订单行项目的单位成本会传递到所创建的库存条目。默认情况下,库存条目使用与采购订单行项目相同的货币。
但是,如果启用了 Convert Currency 设置,则库存条目的货币将在收货时转换为系统的 默认货币。当以不同货币订购库存时,这一设置非常有用,可确保库存条目的单位成本在收货时换算为基础货币。
捆绑套件(Bundled Items)
有些供应商只以单个捆绑或"套件"(kit)产品出售一组组件,而不是作为单独的采购行项目——例如包含各种不同螺丝的紧固件套件,或包含某个用途所需多个组件的"入门套件"。
InvenTree 允许将捆绑套件建模为装配体(assembly),以便在需要时拆分为各个组件,而不是将捆绑物作为单个不透明的库存数量接收:
- 创建一个代表捆绑物本身的部件,并将其标记为 装配件(assembly);
- 将一个 供应商部分 链接到该捆绑物部件,代表它从供应商处如何采购;
- 为捆绑物部件定义 物料清单(Bill of Materials),列出每个单独组件及单个捆绑物中所含的数量;
- 照常针对捆绑物的供应商部分创建并接收采购订单——将为捆绑物创建一个库存条目,保留整个订单的采购价和来源采购订单信息。
当真正需要这些单独组件时,可以将已收的捆绑物库存条目 拆解(disassemble) 为其组件部分。原始捆绑物的采购价格和可追溯性数据(批次代码、来源采购订单)会自动分摊到新生成的组件库存条目上。
包装规格 vs 捆绑套件:包装规格大于 1 的供应商部分仍然代表同一部件的多个单位——包装规格仅决定每订购一个单位会向库存添加多少个物理单位。而捆绑套件则不同:一个供应商部分代表不同组件的集合,必须先拆解,各组件才能单独使用或销售。
完成订单(Complete Order)
一旦所有已收货条目的数量等于或大于所有行项目的数量,订单将自动标记为complete(完成)。
也可以在全部物品到货之前完成订单(或存在缺货时)。为此,点击主采购订单详情面板上的绿色对勾(circle-check)按钮,确认订单已完成。对应源码中的complete_order()转换(见 order/models.py),会写入complete_date并触发已完成部件的定价刷新。
取消订单(Cancel Order)
如果订单将不再处理,用户可以选择取消订单。只需点击主采购订单详情面板上的红色圆形叉(circle-x)按钮,确认该采购订单已被取消。源码中的cancel_order()(见 order/models.py)仅允许取消开放状态的订单,并会通知负责人员。
复制采购订单(Duplicate Purchase Order)
复制采购订单允许用户快速创建现有订单的新副本,复用相同的供应商和行项目信息。
要复制现有订单,请从屏幕右上角的菜单中选择Duplicate Order操作:
这会打开一个对话框,你可以在此调整新订单的参数后再实际创建。表单中提供了一些额外选项,用于控制单个行项目的复制:
随后将基于当前所选订单创建一个新的采购订单:
订单排程(Order Scheduling)
采购订单可以排程到未来日期,以便规划未来的订单。
- 开始日期(Start Date):计划向供应商下达订单的日期;
- 目标日期(Target Date):预计从供应商完成/收到订单的日期;
- 逾期订单(Overdue Orders):如果到了Target Date订单仍未完成,订单将被标记为overdue(逾期)。
源码中 overdue_filter() 实现了逾期判定逻辑:状态属于 OPEN 组、存在target_date且target_date早于今天。模型字段target_date的 help_text 也明确写着 "Order will be overdue after this date"(见 order/models.py)。
日历视图与 ICS 导出
使用采购订单列表右上角的日历按钮,视图可以切换到日历视图。此视图仅显示定义了目标日期的订单。
该视图还可以作为 ICS 日历被外部访问,URL 形如:
http://inventree.example.org/api/order/calendar/purchase-order/calendar.ics默认情况下,已完成的订单不会被导出。可以通过在 URL 后追加?include_completed=True将其包含在内。
采购订单设置
以下是可用的采购订单 全局设置,其默认值与描述定义于 common/setting/system.py:
| 设置键 | 说明 | 默认值 |
|---|---|---|
PURCHASEORDER_ENABLED | 在用户界面中启用采购订单功能 | True |
PURCHASEORDER_REFERENCE_PATTERN | 生成采购订单引用号的必需模式 | PO-{ref:04d} |
PURCHASEORDER_REQUIRE_RESPONSIBLE | 每个订单必须指定负责的所有者(用户或组) | False |
PURCHASEORDER_CONVERT_CURRENCY | 收货时将物品价值换算为基础货币 | False |
PURCHASEORDER_EDIT_COMPLETED_ORDERS | 允许编辑已完成/已发货后的采购订单 | False |
PURCHASEORDER_AUTO_COMPLETE | 当所有行项目都收货后自动将采购订单标记为完成 | True |
PURCHASEORDER_MERGE_LINE_ITEMS | 将新的采购订单行项目与共享相同部件、目的地和目标日期的现有行合并 | True |
各设置的实际作用可以在源码中找到对应证据:
PURCHASEORDER_REQUIRE_RESPONSIBLE:在Order.clean()中强制校验responsible字段(见 order/models.py);PURCHASEORDER_EDIT_COMPLETED_ORDERS:作为UNLOCK_SETTING控制已完成订单是否可被编辑——当设置为False时,完成后的订单会被锁定(见 order/models.py);PURCHASEORDER_CONVERT_CURRENCY:在receive_line_items()中决定是否调用convert_money()换算收货价(见 order/models.py),相关行为由 order/tests.py 的测试用例验证;PURCHASEORDER_AUTO_COMPLETE:在收货循环结束时判断是否自动完成订单(见 order/models.py);PURCHASEORDER_MERGE_LINE_ITEMS:控制是否将相同供应商部分的新行项目合并到既有行中,在 order/api.py 与 order/serializers.py 中被读取,并有对应测试覆盖(见 order/test_api.py)。
采购全流程速览
综合以上内容,一次完整的 InvenTree 采购操作可归纳为以下步骤:
- 建立供应商/制造商:创建公司记录并启用
is supplier(如需直销还可启用is manufacturer); - 定义供应商部分:为每个可采购部件创建 Supplier Part(关联 SKU、包装规格、定价);
- 创建采购订单:选择供应商,生成唯一 Reference,添加行项目(可开启自动定价或上传文件批量导入);
- 下达订单:点击发送按钮,将状态推进至 PLACED;
- 收货入库:逐条或批量标记已收,指定收货位置、批次与序列号(可追溯部件),系统自动生成库存条目与跟踪记录;
- 完成/取消/复制/排程:根据实际业务流转订单状态,或复制历史订单复用行项目,亦可为目标日期设置排程跟踪逾期。
整个流程的状态机、货币换算、包装规格换算与自动完成逻辑均可在 order/models.py、order/status_codes.py 及 company/models.py 中找到对应实现,配合 order/test_api.py 与 order/tests.py 中的测试用例,可作为深入学习与二次开发的起点。
【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考