odoo14流程配置

Odoo 审批流程 (Approval Flow) 实现彻底分析

概览

这个审批流程引擎是一个基于状态机和有向图的通用工作流引擎,核心代码位于 web_approval 模块,业务扩展在 jl_base 模块。它通过 _register_hook 机制动态 patch 业务模型的 create/write/unlink/fields_view_get 方法,实现"业务模型无需写审批代码即可接入"的解耦设计。

注意:代码库中没有名为 button_approve 的引擎方法。button_approve 是各个业务模型自定义的方法,其职责是更新业务状态字段并调用 common.mixin 提供的 start_approval() 来发起审批。真正的引擎入口是 approval.flow.commit_approval() 和 common.mixin.start_approval()。


1. button_approve 方法的定义

button_approve 不是审批引擎的方法,而是每个业务模型自己定义的"发起审批"按钮回调。它不属于任何单一模型,而是约定俗成地存在于各业务模型中。其通用模式:

def button_approve(self):
    self.with_context(approval_callback=True).write({'state': 'in_approval', 'initiate': True})
    self.start_approval()  # 来自 common.mixin

定义位置(部分示例):

模型文件路径行号
jl.supervise d:\www\lims_log\vlims_backend\jl_person\models\jl_supervise.py 57, 595
jl.translate.plan 等 d:\www\lims_log\vlims_backend\jl_person\models\jl_translate.py 188, 451, 699
jl.person d:\www\lims_log\vlims_backend\jl_person\models\jl_person.py 85
jl.labour.price d:\www\lims_log\vlims_backend\jl_labour\models\jl_labour_price.py 288
jl.labour.need d:\www\lims_log\vlims_backend\jl_labour\models\jl_labour_need.py 342
jl.entrust d:\www\lims_log\vlims_backend\jl_entrust\models\jl_entrust.py 1500

jl.entrust.button_approve(行 1500-1507)是个特例——它只更新状态而不调用 start_approval,因为委托单的审批由其他流程触发:

def button_approve(self):
    """委托单(最终的审核节点)"""
    self.with_context(**{"approval_callback": True}).write({'state': 'approval'})

而 jl.supervise.button_approve(行 57-59)是标准范式:

def button_approve(self):
    self.with_context(approval_callback=True).write({'state': 'in_approval', 'initiate': True})
    self.start_approval()

2. 审批流程核心模型类

2.1 引擎核心模型(web_approval 模块)

文件 d:\www\lims_log\vlims_backend\web_approval\models\approval_flow.py:

模型类名行号职责
approval.flow ApprovalFlow 89 审批流程定义表。绑定 ir.model(业务模型)、condition(domain 条件)、company_ids(适用公司)、node_ids/action_ids(节点和动作)、commit_run/completed_run/cancel_run(自动运行函数名)、版本管理(approval_flow_num/version/is_release)
approval.flow.node ApprovalFlowNode 1093 流程节点。is_start/is_end、type(group/user/job/leader/department_head/department_parent/expression)、会签 is_all_approval、加签 allow_before/after_increase、转签 allow_turn_to、complete_run
approval.flow.node.action ApprovalFlowNodeAction 1535 节点动作(边)。action_type(accept/refuse)、condition、sorce_node_id→target_node_id,构成有向图
approval.version.max MaxApprovalVersion 58 记录流程版本号,用于版本管理
increase.type IncreaseType 1617 加签类型字典
approval.circulated ApprovalCirculated 1625 传阅记录

文件 d:\www\lims_log\vlims_backend\web_approval\models\approval_flow_instance.py:

模型类名行号职责
approval.flow.instance ApprovalFlowInstance 13 审批流实例(一次审批的运行实例)。state(active/complete/cancel)、str_node_ids(JSON 节点路径)、create_approval_instance_and_node
approval.flow.instance.node ApprovalFlowInstanceNode 393 实例节点(每个节点的一次运行)。state(active/running/complete/turn_to)、加签/转签字段、accept_next_action/refuse_next_action/jump_next_node
approval.copy.for ApprovalCopyFor 1175 审批抄送

文件 d:\www\lims_log\vlims_backend\web_approval\models\approval_state.py:

模型类名行号职责
record.approval.state RecordApprovalState 7 记录审批状态(每条业务单据对应一条)。model/res_id/approval_state/is_commit_approval/commit_user_id。是业务单据与审批实例的桥梁
approval Approval 211 审批动作记录(每次同意/拒绝/转签的日志)。instance_node_id/action_type/say/user_id
wait.approval WaitApproval 254 待审批(待办)。create_wait_approval_for_users_and_instance

文件 d:\www\lims_log\vlims_backend\web_approval\models\approval_form.py:

模型类名行号职责
approval.from (AbstractModel) ApprovalForm 31 业务模型必须继承的抽象基类。提供 approval_code/uuid/approval_company_id/apply_user_id/apply_department_id/parent_department/doc_approval_state/circulated 字段,以及 _compute_doc_approval_state(计算展示状态)。create 时自动通过 approval.from.code 生成编号
approval.from.code ApprovalFormCode 11 流程编码规则(ir.sequence 关联)

文件 d:\www\lims_log\vlims_backend\web_approval\models\approval_flow_record.py:

模型类名行号职责
approval.record ApprovalRecord 89 所有审批记录(SQL视图 _auto=False)
i.proposed.approval.record IProposedApprovalRecord 210 我提交的审批记录(视图)
approval.circulated.summary ApprovalCirculatedSummary 6 传阅汇总(视图)

2.2 引擎扩展模型(jl_base 模块)

文件 d:\www\lims_log\vlims_backend\jl_base\models\approval_flow.py(_inherit 扩展引擎,增加业务特性):

类名行号继承扩展内容
ApprovalFlow 18 approval.flow document(制度文档Html)、customer_node(自定义节点)、start_approval_user_field_id(发起人字段)、get_commit_uid(按字段取发起人)、commit_approval 重写
ApprovalFlowNode 129 approval.flow.node redirect_expression(待办路由表达式)、get_users_for_is_department_parent/head/leader 重写(支持 start_approval_user_field_id)
ApprovalForm 274 approval.from approval_start_time、approval_done_time
ApprovalFlowInstanceNode 282 approval.flow.instance.node redirect_info、is_customer、customer_node_name、approval_ids(One2many)、can_approval_current_node 重写、jump_next_node 重写、run_complete_func
RecordApprovalState 419 record.approval.state approval_code、update_state_ref_record_state(回写业务单据 apply_user_id/approval_code/approval_start_time/approval_done_time)、create/write 时发系统通知
Approval 485 approval redirect_info、create_approval 重写(同时创建 done.approval 已办记录)
WaitApproval 521 wait.approval + common.mixin is_done、approval_type、approval_code、approval_name、send_mail_datetime、wait_send_mail(邮件)、run_wait_approval_expire_notice(超时提醒)
DoneApproval 651 common.mixin(新模型) 已办记录,SQL/related 字段
OwnerApprovalRecord 700 common.mixin(_auto=False 视图) 我发起的所有审批记录
ApprovalFlowCustomerRecord 790 新模型 自定义节点信息,create_customer_approval 发起自定义审批
ApprovalCirculatedExtend 967 approval.circulated + common.mixin 扩展传阅

3. 业务模型接入审批流程的配置

3.1 必须的继承

业务模型必须同时继承两个抽象类:

_inherit = ['mail.thread', 'mail.activity.mixin', 'common.mixin', 'approval.from']
  • approval.from(AbstractModel):提供审批相关字段(approval_code、apply_user_id、doc_approval_state、circulated 等)和 _compute_doc_approval_state 方法。这是接入审批流的标识。
  • common.mixin(即 JlCommonMixin,d:\www\lims_log\vlims_backend\jl_base\models\jl_common_mixin.py):提供所有审批操作方法:start_approval、confirm_approval、refuse_approval、cancel_approval、close_approval、endorsement_transfer、before_increase_sign、after_increase_sign、share_approval、get_approval_info、get_approve_history。

3.2 需要在模型上添加的字段

业务模型通常自己定义一个 state Selection 字段(业务状态)和一个 initiate Boolean(是否已发起审批)。这些不是引擎强制要求,但 button_approve 需要操作它们。approval.from 已提供审批状态展示字段 doc_approval_state。

jl_base 扩展的 RecordApprovalState.update_state_ref_record_state(行 425-442)会在审批状态变化时回写业务单据的 apply_user_id、approval_code、approval_start_time、approval_done_time——所以业务模型若继承了 approval.from(及 jl_base 扩展),会自动获得这些字段的回写。

3.3 注册到审批配置表

业务模型不需要在代码中注册。注册是通过后台在 approval.flow 表中创建配置记录完成的:配置时选择 model_id(ir.model)指向业务模型,设置 condition、company_ids,然后设计节点和动作。

引擎通过 _register_hook(approval_flow.py 行 455-718)动态 patch 所有在 approval.flow 中配置了 active=True 的模型:重写其 create(自动创建 record.approval.state)、write(审批中禁止修改)、unlink(审批中禁止删除)、fields_view_get(注入"提交审核/暂停/恢复/取消/审批/传阅"按钮)、_apply_ir_rules/check_access_rule(跳过权限检查)。

3.4 调用链

发起审批(button_approve → start_approval):

业务模型.button_approve()                          # 业务模型自定义
  → common.mixin.start_approval()                  # jl_common_mixin.py:1163
    → approval.flow.get_approval_flow_and_check_approval()  # approval_flow.py:974
    → approval.flow.commit_approval(model, res_id) # jl_base 扩展版 approval_flow.py:47
      → record.approval.state.change_approval_state_to_active()
      → approval.flow.instance.create_approval_instance_and_node()
      → instance.create_wait_user()                # 创建 wait.approval 待办
      → instance.commit_first_node()                # 激活首个节点

审批同意(confirm_approval):

common.mixin.confirm_approval(instance_node_id, reason)   # jl_common_mixin.py:1466
  → common.mixin._approval(instance_node_id, 'accept', ...)  # jl_common_mixin.py:1422
    → approval.create_approval()                  # 创建审批记录
    → instance_node.accept_next_action(document, jump, multi_approval)  # approval_flow_instance.py:856
      → node.state='complete' + complete_run_fun()
      → set_next_node_state()                     # 激活下一节点
      → 若全部完成 → instance.run_after_the_instance_is_executed()
        → res_state.approval_state='complete'
        → flow_id.completed_run_fun(document)     # 执行配置的"完成后自动运行"函数

button_commit 在此代码库中不存在;它属于 Odoo 标准采购模块(purchase),与本审批引擎无关。


4. 所有接入审批流程的业务模型

通过 _inherit 包含 approval.from 找到全部接入模型:

4.1 business_approval 模块(专门用于给既有模型追加审批能力)

文件 d:\www\lims_log\vlims_backend\business_approval\models\:

文件模型接入方式
sale_order.py jl.sale.order _inherit = ['approval.from', 'jl.sale.order'],仅追加 approval.from,无自定义 button_approve
res_partner.py res.partner _inherit = ['approval.from', 'res.partner']
project_task.py project.task _inherit = ['approval.from', 'project.task']
ir_attachment.py ir.attachment _inherit = ['approval.from', 'ir.attachment']

4.2 各业务模块

模块/文件路径模型名接入方式(关键点)
jl_entrust\models\jl_entrust.py:78 jl.entrust _inherit=[...,'common.mixin','approval.from'];button_approve(1500行)只改 state;button_entrust(1509行)为审批完成回调(配置在 completed_run)
jl_entrust\models\jl_collaboration.py:24 jl.collaboration 同上模式
jl_person\models\jl_supervise.py:11,129,400 jl.supervise 等3个模型 button_approve(57行) 改 state+initiate 并调 start_approval();create_and_approval(62行) 创建后立即审批
jl_person\models\jl_translate.py:13,232,489,739 jl.translate.plan 等4个模型 button_approve(188/451/699行) 改 state+initiate 并调 start_approval()
jl_person\models\jl_person.py:10,110 jl.person 等2个模型 button_approve(85行) 调 start_approval()
jl_labour\models\jl_labour.py:22 jl.labour 继承审批
jl_labour\models\jl_labour_price.py jl.labour.price button_approve(288行) 改 state='waiting' 并 start_approval()
jl_labour\models\jl_labour_need.py jl.labour.need button_approve(342行) 批量发起审批
jl_outsource\models\jl_subcontract.py:40 jl.subcontract 继承审批
jl_report\models\jl_subcontract_report.py:9 委外技术验收单 继承审批
jl_check_accept\models\jl_check_accept.py:32 jl.check.accept 继承审批
jl_check_accept\models\jl_final_statement.py:11 jl.final.statement 继承审批
jl_disassembly_manage\models\jl_disassembly_plan.py:18 jl.disassembly.plan 继承审批
jl_ingredients_manage\models\jl_ingredients_declaration.py:16 jl.ingredients.declaration 行200:write state='approval'+is_commit_approval=True(不走 button_approve,直接发起)
jl_ingredients_manage\models\jl_annual_plan.py:12 jl.annual.plan 行58:同上模式
jl_execute\models\jl_execute_order.py:309 jl.execute.order 继承审批
jl_experimental_assistant_base\models\jl_outline.py:115 jl.outline 行865:write state='approval'+is_commit_approval=True
jl_vlims_extend\models\jl_srlc_contract.py:10 jl.srlc.contract 继承审批
jl_vlims_extend\models\jl_site_order_ext.py:15 jl.site.order(扩展) _inherit=['approval.from','jl.site.order']
jl_plan\models\jl_experimental_plan.py:27 jl.experimental.plan 继承审批
jl_plan\models\jl_experimental_total.py:41 jl.experimental.total 继承审批
jl_plan\models\jl_maintain_exchange.py:15 jl.maintain.exchange 继承审批
jl_base\models\res_partner.py:15 res.partner(扩展) 继承审批

接入方式分两类:

  • 定义 button_approve:显式更新 state 字段后调用 self.start_approval()(最常见范式)
  • 仅继承不定义 button_approve:通过 business_approval 模块或直接 _inherit=['approval.from', <原模型>] 只追加审批能力,审批由其他途径(如控制器 /web/approval/commit_approval 或前端按钮 type=commitApproval)触发

create_and_approval 方法(jl_supervise.py:62、jl_person.py:70):创建记录后立即调用 button_approve,用于"保存并发起审批"场景。


5. 审批节点的数据配置方式

5.1 节点配置是后台手动配置

审批流程、节点、动作全部在后台手动配置,没有 XML/CSV 初始数据。配置入口为菜单 "流程审批 → 流程配置":

  • 菜单定义:d:\www\lims_log\vlims_backend\web_approval\views\approval_flow_view.xml:233
    <menuitem name="流程配置" id="menu_action_approval_flow" action="action_approval_flow"
              groups="group_approval_config" parent="menu_approval_root" sequence="40"/>
    
  • 根菜单"流程审批":d:\www\lims_log\vlims_backend\web_approval\views\menu.xml
  • 表单视图:approval_flow_view.xml:45(view_approval_flow_form),包含"发布流程/升级流程/设计流程"按钮
  • 设计器是前端 JS 组件 approval_diagram(approval_flow.py:164 的 show_flow_designer)

5.2 仅有序列号初始数据

d:\www\lims_log\vlims_backend\web_approval\data\approval_from_code_data.xml 只定义了两个 ir.sequence:

  • approval.from.code(流程编号 S+年月日)
  • approval.flow.instance.code(实例编号 年月日)

以及一条默认 approval.from.code 记录。没有任何预置的审批流配置。

5.3 配置表与配置项

在 approval.flow 表单(approval_flow_view.xml)中配置:

  • model_id:指向业务模型(ir.model)
  • condition:domain 表达式(条件审批,如 [("amount", ">", 10000)])
  • company_ids:适用公司
  • node_ids:节点(开始/结束/中间审批节点,每节点配 type、审批人、会签、加签权限、complete_run)
  • action_ids:节点动作(源节点→目标节点,accept/refuse,可带 condition)
  • commit_run/completed_run/cancel_run:自动运行的业务方法名(逗号分隔)
  • approval_can_run/approval_cannot_run:审批后(不)能执行的方法(如 write/unlink)
  • 版本管理:approval_flow_num/version/is_release,"发布流程"按钮激活

另外 approval.from.code(流程编号规则)在菜单 "流程审批 → 流程编号" 配置(approval_form_code_view.xml:56)。

record.approval.state(记录审批状态)在菜单 "流程审批 → 记录审批状态"(record_approval_state_view.xml:26,仅 group_approval_config 组可见)。


总结:新模型类接入审批流程的配置清单

要让一个新 Odoo 模型接入此审批流程引擎,需要:

  1. 模块依赖:在 __manifest__.py 的 depends 中加入 web_approval 和 jl_base(jl_base 提供 common.mixin 和引擎扩展)。

  2. 模型继承:在模型类中添加继承:

    _inherit = ['mail.thread', 'mail.activity.mixin', 'common.mixin', 'approval.from']
    
    • approval.from 是接入审批的标识(必选)
    • common.mixin 提供所有审批操作方法 start_approval/confirm_approval/refuse_approval/cancel_approval 等(必选)
  3. 定义业务状态字段:模型自定义一个 state Selection 字段和(可选)initiate Boolean,用于业务状态流转展示。approval.from 已提供 doc_approval_state(审批展示状态)、approval_code、apply_user_id 等。

  4. 定义发起审批方法:定义 button_approve 方法,范式:

    def button_approve(self):
        self.with_context(approval_callback=True).write({'state': 'in_approval', 'initiate': True})
        self.start_approval()
    

    approval_callback=True context 用于绕过引擎 patch 的 write 限制(审批中不可修改)。

  5. (可选)定义审批回调方法:在 approval.flow 配置中填写 completed_run/cancel_run 等方法名,这些方法需在本模型上定义,引擎会在审批完成/取消时自动调用(如 jl.entrust.button_entrust)。

  6. 后台配置审批流:进入"流程审批 → 流程配置"菜单,新建 approval.flow 记录:

    • 选择 model_id 为本模型
    • 设置 condition(可选)和 company_ids
    • 用"设计流程"按钮配置节点(开始/审批节点/结束)和节点动作(边)
    • 点击"发布流程"激活
  7. (可选)配置流程编号:在"流程审批 → 流程编号"为模型配置 approval.from.code(ir.sequence),否则用默认序列。

  8. 无需:不需要在代码中注册到任何配置表(_register_hook 会自动扫描 approval.flow 中 active=True 的记录并 patch 对应模型);不需要定义 _get_flow 方法(引擎的 get_approval_flow 会通过 model_id + condition + company 匹配)。

关键文件路径汇总:

  • 引擎核心:d:\www\lims_log\vlims_backend\web_approval\models\approval_flow.py、approval_flow_instance.py、approval_state.py、approval_form.py
  • 引擎扩展与业务 mixin:d:\www\lims_log\vlims_backend\jl_base\models\approval_flow.py、jl_common_mixin.py
  • 控制器:d:\www\lims_log\vlims_backend\web_approval\controllers\main.py
  • 配置视图:d:\www\lims_log\vlims_backend\web_approval\views\approval_flow_view.xml、menu.xml
  • 审批向导:d:\www\lims_log\vlims_backend\web_approval\wizard\approval_wizard.py(button_ok 执行同意/驳回)
 

子代理已完成全库扫描,现在把"其他用到审批的地方"这一项给你补全,并对前面的分析做两处修正补充。


四、所有接入审批流程的业务模型(完整清单)

通过全库扫描 _inherit 包含 approval.from 的模型,接入方式分两类:

A. 标准范式:自定义 button_approve → 改 state → start_approval()

模型文件button_approve 行号备注
jl.person.auth jl_person/models/jl_person.py :85 你选中的这个,有 create_and_approval
jl.supervise 等 3 个 jl_person/models/jl_supervise.py :57 也有 create_and_approval(:62)
jl.translate.plan 等 4 个 jl_person/models/jl_translate.py :188/:451/:699  
jl.labour.price jl_labour/models/jl_labour_price.py :288 state 用 waiting
jl.labour.need jl_labour/models/jl_labour_need.py :342 批量发起

B. 特例:button_approve 只改 state,不调 start_approval()

模型文件行号说明
jl.entrust jl_entrust/models/jl_entrust.py :1500 委托单这里只是"最终审核节点"的回调,真正的发起由 completed_run 里配的 button_entrust(:1509) 等触发。这说明 button_approve 不是引擎约定,只是惯例名——真正引擎入口永远是 start_approval。

C. 不走 button_approve,直接 write + start_approval

模型文件行号
jl.ingredients.declaration jl_ingredients_manage/models/jl_ingredients_declaration.py :200
jl.annual.plan jl_ingredients_manage/models/jl_annual_plan.py :58
jl.outline jl_experimental_assistant_base/models/jl_outline.py :865

D. 仅继承、不写 button:用 business_approval 模块给既有模型"贴"审批能力

business_approval/models/ 下用 _inherit=['approval.from','<原模型>'] 给 jl.sale.order、res.partner、project.task、ir.attachment 追加审批字段,发起动作由前端 type=commitApproval 按钮经控制器 /web/approval/commit_approval 触发。这是接入成本最低的方式:零业务方法,全靠后台配置 + 引擎 patch。


两处修正/补充(相对前面的回答)

  1. button_approve 不是引擎方法,是各业务模型自定义的按钮回调,名字只是惯例。引擎真正入口是 common.mixin.start_approval()(jl_common_mixin.py:1163)和 approval.flow.commit_approval()。

  2. 审批通过的回调路径(之前只讲了发起链,补全同意链):

    common.mixin.confirm_approval(instance_node_id, reason)   # jl_common_mixin.py:1466
      → _approval(node_id, 'accept', ...)
        → approval.create_approval()                          # 写审批日志 + done.approval 已办
        → instance_node.accept_next_action(document, ...)
          → 当前节点 complete + complete_run_fun()
          → set_next_node_state() 激活下一节点
          → 全部完成 → instance.run_after_the_instance_is_executed()
            → record.approval.state 写 'complete'
            → flow.completed_run_fun(document)               # 执行后台配的"完成后自动运行"方法
    

    这就是为什么 jl.entrust 那种"只改 state"的 button_approve 能工作——它其实是被配置在 completed_run / commit_run 里、由引擎在节点流转时回调,而不是前端直接调。

  3. 后台配置入口:菜单 流程审批 → 流程配置(approval_flow_view.xml:233,需 group_approval_config 组权限),点"设计流程"打开前端 JS 流程图编辑器 approval_diagram,配好节点和 accept/refuse 动作后点"发布流程"激活。无任何 CSV/XML 预置流程数据,只有两条 ir.sequence(approval_from_code_data.xml)。


最终:新模型接入审批的完整配置清单

代码侧(你的模型类):

  1. __manifest__.py 的 depends 加 web_approval、jl_base
  2. _inherit = ['mail.thread','mail.activity.mixin','common.mixin','approval.from']
  3. 自定义 state Selection(业务状态)+ initiate Boolean(可选)
  4. 保证有 company_id 字段(审批流配"适用公司"时匹配必需)
  5. 定义发起方法(二选一):
    • 标准:button_approve → with_context(approval_callback=True).write(state=in_approval) → self.start_approval()
    • 零代码:不写方法,靠前端 commitApproval 按钮经控制器触发
  6. 如需自动编号:配 ir.sequence + 在 create 里 next_by_code,并在 approval.from.code 配编码规则(不配则用兜底序列)
  7. 审批完成/取消后要跑的业务方法,定义在本模型上,方法名填到后台 completed_run/cancel_run/commit_run

后台配置侧(运行时数据):

  1. approval.flow 新建一条,model_id 选你的模型、company_ids/condition/commit_approval_group_id 按需
  2. "设计流程"配节点(开始/审批/结束,每节点 type=user/group/job/leader、会签/加签/转签权限)
  3. 至少一条 action_type='accept' 的节点动作(否则报"没有为审批定义审批流程")
  4. active=True 后"发布流程" → 引擎 _register_hook 自动 patch 你的模型 create/write/unlink/fields_view_get 5.(可选)approval.from.code 配流程编号规则

不需要做的:

  • 不用在代码里注册到配置表(_register_hook 自动扫描 approval.flow 中 active=True 记录)
  • 不用写 _get_flow(引擎 get_approval_flow 按 model_id+company+condition 匹配)
  • 不用自己写 create/write 拦截(patch 后的 write 自动拦截审批中修改、自动建 record.approval.state)

这样你的新模型就完整接入了审批流程。

posted @ 2026-09-18 10:32  fpc  阅读(15)  评论(0)    收藏  举报