FEATURED · 精选文章

OpenProject 预算模块完全指南:从创建项目预算、规划人工与材料成本到工时成本核算

发布时间 / 2026/9/17 19:50:33
来源 / 创域科博编辑部
栏目 / 资讯中心
OpenProject 预算模块完全指南:从创建项目预算、规划人工与材料成本到工时成本核算 OpenProject 预算模块完全指南从创建项目预算、规划人工与材料成本到工时成本核算【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject导读预算Budgets是 OpenProject 中用于跟踪项目可用资金与已花费成本的核心模块。本文以官方用户指南为骨架结合modules/budgets插件源码模型、控制器、数据库迁移与modules/costs成本引擎实现系统讲解如何在项目中创建预算、添加计划内的材料单位成本与人工成本、将工作包关联到预算以自动记账、查看/更新/复制/删除预算并深入剖析成本计算、固定日期取费率与预算花费比例等底层原理帮助你从「会操作」进阶到「懂原理」。前置条件激活 Budgets 模块[!TIP] 在创建项目预算前需要先在项目设置中激活Budgets 模块。从插件引擎注册逻辑看engine.rb模块启用与否直接决定了预算菜单是否出现在项目菜单中project_module :budgets do permission :view_budgets, { budgets: %i[index show] }, permissible_on: :project permission :edit_budgets, { budgets: %i[index show edit update destroy destroy_info new create copy] }, permissible_on: :project, dependencies: :view_budgets end menu :project_menu, :budgets, { controller: /budgets, action: index }, if: -(project) { project.module_enabled?(:budgets) }, after: :costs, caption: :budgets_title, icon: op-budget从中可以读出的关键信息view_budgets查看预算允许访问预算列表与详情页index、showedit_budgets编辑预算覆盖创建、编辑、更新、复制、删除等全部写操作且声明了dependencies: :view_budgets即授予编辑权限必须同时授予查看权限两个权限都属于项目级权限permissible_on: :project需要配合角色在项目成员中分配预算菜单被注册在after: :costs成本菜单之后意味着 Budgets 模块与 Time and costs时间与成本模块在导航上是紧密相邻的一对。创建项目预算模块激活后进入项目的Budgets模块点击右上角的绿色 Budget按钮即可打开创建表单对应 new.html.erb。在表单中定义预算的以下字段Subject主题为预算起一个清晰易识别的名称。源码中要求必填且长度限制在 1–255 个字符budget.rb。Description描述补充预算负责人、状态或其他备注信息。Attachments附件将支持性文件拖入上传区或点击选择本地文件。Budget模型通过acts_as_attachable支持附件budget.rbAPI 侧由 attachments_by_budget_api.rb 提供附件的增删查接口。Fixed date固定日期指定一个固定日期该日期决定成本计算所采用的费率具体取值取决于用户资料中配置的工时费率管理员设置的成本类型cost types。由于费率可以按不同日期区间配置多档固定日期保证了预算按「正确时点」的费率计算。新表单默认以Date.today作为固定日期budgets_controller.rb。Base amount基础金额以整笔lump-sum金额分配资金无需逐项规划材料或人工。适合只需给一个总金额的粗粒度预算场景。该字段由迁移 add_base_amount_to_budget.rb 新增数据类型为decimal(20,2)默认值为0.0。数据模型视角一张预算表从数据库迁移文件可以清晰看到预算及其明细项的持久化结构budgets.rbbudgets表存储project_id、author_id、subject、description、fixed_date并对project_id updated_at建立复合索引material_budget_items.rbmaterial_budget_items表存储units单位数量、cost_type_id成本类型、comments、amountdecimal(15,4)手工覆盖金额labor_budget_items.rblabor_budget_items表存储hours计划小时数、user_id、comments、amount手工覆盖金额budget_journals.rb配合acts_as_journalized记录预算的历史变更便于审计追溯。在模型层budget.rbBudget与Project、Author(User)关联has_many :work_packages且删除预算时对工作包执行nullify同时通过has_many :cost_entries与has_many :time_entries均经由work_packages间接获得「实际花费」的数据来源——这正是「工作包记账 → 预算汇总」这一链路的数据基础。添加计划内的单位材料成本[!NOTE] 计划单位成本的前提是成本类型Cost types已由系统管理员在系统管理 → Time and costs中创建并维护。成本类型属于系统级配置普通项目用户只能选用不能新建。在预算表单的Unit costs单位成本区域输入该成本类型的units单位数量从下拉列表选择Cost type成本类型Unit name单位名称会根据系统管理中成本类型的配置自动带出如「小时」「天」「件」的单复数形式可填写comment备注说明该项单位成本的用途Planned costs计划成本由系统依据该成本类型的「单位成本cost per unit」与预算固定日期自动计算点击**编辑图标小笔**可手动覆盖该成本类型的计算金额点击删除图标可移除该条计划单位成本点击 图标可为该预算新增一行单位成本。底层计算逻辑rate × units在 material_budget_item.rb 中材料成本项的成本计算一目了然def costs amount || calculated_costs # 手工覆盖金额优先 end def calculated_costs(fixed_date budget.fixed_date) if units cost_type rate cost_type.rate_at(fixed_date) rate.rate * units else 0.0 end end若用户在表单中手工覆盖了金额amount有值则以覆盖值为准否则取cost_type.rate_at(fixed_date)在 cost_type.rb 中该方法查询CostRate表中valid_from 固定日期且valid_from最新的那条费率记录再乘以单位数量——这就是「固定日期决定取哪一档费率」的源码级解释若该日期没有生效费率成本按0.0计算界面同时会提示用户配置费率。添加计划内的人工成本在预算表单的Labor costs人工成本区域设置该用户在本预算上的计划hours小时数从下拉列表选择user用户可按需填写comment备注Planned costs依据用户资料中配置的工时费率与小时数自动计算费率同样取自预算的固定日期点击编辑图标可手工覆盖计算出的计划人工成本金额点击删除图标可移除该条人工成本计划点击 图标可为不同用户继续添加计划人工成本。填写完成后点击Create按钮保存提交。底层计算逻辑适用费率 × 小时数在 labor_budget_item.rb 中def costs amount || calculated_costs end def calculated_costs(fixed_date budget.fixed_date, project_id budget.project_id) rate applicable_rate(fixed_date, project_id) return 0.0 unless rate hours rate.rate * hours end def applicable_rate(fixed_date budget.fixed_date, project_id budget.project_id) return if user_id.blank? applicable_rates.fetch([fixed_date, project_id]) do |key| applicable_rates[key] HourlyRate.at_date_for_user_in_project(fixed_date, user_id, project_id) end end关键细节与材料成本一致手工覆盖金额amount优先于自动计算费率查询走HourlyRate.at_date_for_user_in_projecthourly_rate.rb先取用户在指定项目层级内、指定日期生效的专项目工时费率找不到再回退到用户的默认费率DefaultHourlyRate.at_for_user结果按[固定日期, 项目]做缓存applicable_rates哈希避免同一预算内重复查库模型校验要求hours必须为数值principal用户与budget必填且用户必须是预算所属项目的成员user_is_member_of_budget_project见 labor_budget_item.rb有趣的是Principal被用作关联对象belongs_to :principal, foreign_key: user_id因此可以给群组Group也做人工成本预算但群组没有费率按设计按0.0计算budgets_controller.rb 注释明确说明。控制器中的实时费率预览创建/编辑表单中当你输入数量或选择用户后页面会通过 AJAX 调update_material_budget_item/update_labor_budget_item两个接口实时回填成本budgets_controller.rb。其中update_labor_budget_item还会返回一个cost_hint当用户在该固定日期没有生效费率时界面会提示「该日期无可用费率」权限方面只有拥有view_cost_rates/view_hourly_rates或view_own_hourly_rate的用户才能看到对应成本金额render_item_as_json中按权限过滤budgets_controller.rb。将工作包关联到预算要让「实际花费」自动记入某个预算需要把工作包分配给该预算打开对应工作包的详情视图在Costs成本区域的下拉列表中选择希望该工作包归属的预算列表只包含当前项目中已配置的预算保存后该工作包上记录的所有时间与成本都会记入对应的预算。从源码可验证这一行为工作包与预算是多对一关系has_many :work_packagesdependent: :nullifybudget.rbBudget#cost_entries与Budget#time_entries都经由work_packages关联获得budget.rb因此工作包上的成本条目CostEntry和工时条目TimeEntry天然汇入所属预算在移动工作包时也可直接指定目标预算插件向move_work_package额外放行了budget_id参数engine.rb工作包查询页还提供了按预算过滤的过滤器budget_filter.rb方便集中查看某预算下的所有工作包。花费比例ratio spent是如何算出来的在预算详情页会展示「已花费比例」。其计算逻辑位于 budget.rbdef budget base_amount material_budget labor_budget # 预算总额 基础金额 材料计划 人工计划 end def spent spent_material spent_labor # 已花费 实际材料 实际人工 end def budget_ratio return 0.0 if budget.nil? || budget 0.0 ((spent / budget) * 100).round # 花费比例四舍五入的整数百分比 end def available budget - spent # 可用余额 end实际花费的取数逻辑spent_material/spent_labor使用 SQL 聚合对工作包关联的cost_entries/time_entries求和并优先取overridden_costs被覆盖的成本否则取costs自动计算成本budget.rb。同时受可见性作用域限制只有拥有view_cost_rates等成本查看权限的用户才会看到真实金额。预算总额为 0 时比例返回0.0避免除零。查看预算详情、更新、复制与删除在预算列表页可以看到当前项目下所有预算index.html.erb点击**主题subject**进入详情页。详情页show.html.erb会展示计划成本planned与已花费成本spent的概览可用成本available 预算总额 − 已花费总进度花费比例ratio spent固定日期人工与单位成本费率取值的基准日期全部计划单位成本明细已分配且产生实际单位成本的工作包列表本预算的计划人工成本明细产生实际人工成本已记工时的、关联到该预算的工作包列表。[!NOTE] 成本计算全部基于成本类型的配置与用户资料中配置的工时费率。页面右上角提供三个操作按钮Update更新修改预算例如调整计划单位成本或计划人工成本。更新由 update_service.rb 完成并受 update_contract.rb 校验若发生乐观锁冲突ActiveRecord::StaleObjectError控制器会提示锁定冲突budgets_controller.rb。Copy复制基于当前预算配置创建新预算。实现上是Budget.new_copybudget.rb复制project_id / subject / description / fixed_date / base_amount并重新挂到当前用户为作者同时深拷贝全部人工与材料预算项含手工覆盖金额amount。这非常适合「按模板批量建预算」的场景。Delete删除删除预算。删除前若该预算下仍有关联工作包控制器会先跳转到destroy_info页面让用户选择将工作包重新分配reassign到另一个可见的同类项目预算或解除关联delete批量重分配按每批 100 条工作包执行并写入Journal::CausedByBudgetDeletion作为变更原因budgets_controller.rb。预算列表页还支持按 ID/主题/固定日期排序并可导出 CSVindex动作中的format.csvbudgets_controller.rb。通过 API 与项目导出使用预算预算功能不止停留在界面还向外部开放了两类能力REST API v3插件注册了budgets与budgets_by_project两个 API 路径engine.rb由 budgets_api.rb、budgets_by_project_api.rb 与对应的 budget_representer.rb 实现可用于编程式地查询项目预算及其花费情况。项目导出项目列表/导出支持「预算花费比例budget spent ratio」与「预算币种金额」列budget_spent_ratio.rb、budget_currency_attribute.rb方便在项目组合层面横向对比各项目预算消耗情况。常见问题FAQ如何准备一份预算目前预算仅限单个项目使用不能跨项目共享。如果需要为不同主项目/子项目做预算必须分别在各个项目中单独创建。不过OpenProject 的**成本报表cost reports**可以跨多个项目分析工时与成本花费详情参见时间与成本报表用户指南。预算项中为什么有的用户没有费率从源码可以确认为群组Group做人工成本计划时群组本身不持有费率成本按0.0显示设计如此而为具体用户做计划时如果该用户在固定日期当天没有生效的项目费率也没有默认费率成本同样为0.0并会在表单中收到「该日期无可用费率」的提示此时应在用户资料中为相应日期区间配置工时费率。手工覆盖金额与自动计算冲突时以谁为准以手工覆盖金额amount为准。材料与人工成本项的costs方法均为amount || calculated_costs只有未填写覆盖金额时才回退到按固定日期费率 × 数量/小时自动计算。关键源码路径速查用途文件预算模型总额、花费、比例、复制、明细维护budget.rb材料成本项单位成本计算、覆盖金额material_budget_item.rb人工成本项工时费率取数、成员校验labor_budget_item.rb控制器增删改查、实时费率预览、删除重分配budgets_controller.rb权限与菜单注册engine.rb数据库表结构budgets.rb成本类型费率查询cost_type.rb工时费率查询hourly_rate.rb特性测试新增/复制/删除/更新预算spec/features/budgets【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻