企业版审批文档

Odoo 企业版 Approvals + Documents 为什么不是“审批单挂个附件”:documents_approvals 的默认文件夹、标签、可见性与回退路径讲透

很多人把 Odoo 企业版审批附件理解成“approval.request 上多了个附件列表”。但 `documents_approvals` 真正做的是把审批材料接进 Documents 的治理体系:默认审批目录、公司级标签、文档 mixin 自动建档、按审批参与者收敛访问权,以及关闭开关后的动作回退。

企业 协同办公
进阶 开发者 3 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

很多团队第一次看到 Odoo 企业版的 Approvals + Documents,往往会把它想得很轻:

  • 发起一张审批单;
  • 上传几个附件;
  • 批准人点开看看;
  • 审批结束后材料留在记录上。

这个理解不能说错,但它忽略了 documents_approvals 真正要解决的问题。

如果你只把审批附件留在 approval.request 上,它更像“挂在业务单据上的文件”,而不是“进入企业文档治理体系的材料”。可官方在 /home/ubuntu/odoo-temp/enterprise/documents_approvals 里做的,明显不是简单加个附件按钮,而是把审批材料重新放进 Documents 的统一规则里。

它主要处理四件事:

  1. 审批附件是不是自动变成 documents.document
  2. 这些文档默认进哪个目录、打哪些标签
  3. 谁天然能看、谁不能看
  4. 如果公司关闭这套桥接,界面和行为怎么回退

所以 documents_approvals 的重点,不是“审批单也能传附件”,而是“审批材料如何以最小暴露面进入 Documents,并保留审批上下文”。


一、先看模块定位:它不是重做审批,而是把 approval.request 接进 documents.mixin

documents_approvals/models/approval_request.py 最关键的一行是:

_inherit = ['approval.request', 'documents.mixin']

这意味着官方没有重写一套“审批附件模型”,而是直接让审批请求继承 Documents 的自动建档机制。

/home/ubuntu/odoo-temp/enterprise/documents/models/documents_mixin.py 已经把这套机制定义得很清楚:

  • ir.attachment 挂到继承了 documents.mixin 的记录上时;
  • 系统可以自动创建 documents.document
  • 并通过若干可重写方法,决定默认 owner / folder / tags / partner / access rights / members

这说明 documents_approvals 的设计思路非常克制:

  • 审批还是审批;
  • 文档还是文档;
  • 两者通过 mixin 桥接,而不是互相污染模型职责。

这类写法在 Odoo 企业源码里很典型:

不另起一套附件逻辑,而是把业务对象接入平台级文档规范。


二、审批文档默认落到哪:不是当前用户随手传到哪,而是公司级 approvals_folder_id

documents_approvals/models/res_company.py 给公司加了三项关键配置:

  • documents_approvals_settings
  • approvals_folder_id
  • approvals_tag_ids

其中 approvals_folder_id 是存储在公司上的默认审批目录,并且:

  • 要求 type='folder'
  • 不能是 shortcut
  • _compute_approvals_folder_id() 里会跟随开关自动恢复默认值

documents_approvals/__init__.pypost_init_hook 还补了一刀:

  • 如果某些公司还没有设置审批目录;
  • 安装模块后,会统一把它们指向 documents_approvals.document_approvals_folder

再看 data/documents_folder_data.xml,这个默认目录初始就是:

  • 名称为 Approvals
  • access_via_link = none
  • access_internal = none

这三个细节放在一起就能看懂官方态度:

1)审批材料不是“跟着上传入口漂流”

目录是公司级配置,不是某个用户上传时临时决定。

2)审批材料默认不是公开共享的

无论外链访问还是内部默认可见,初始都是 none

3)审批目录是个稳定容器,不是随记录即时拼装

官方宁可先给你一层固定审批目录,再通过每张审批单自己的文档权限去细化,而不是把附件直接散落在各种位置。

审批文档的第一原则是“先归位”,不是“先上传”。


三、标签怎么进来:不是上传人自己打,而是公司级 approvals_tag_ids

approval.request 里重写了:

def _get_document_tags(self):
    return self.company_id.approvals_tag_ids

这说明审批材料的标签来源,不是当前请求临时输入,也不是随上传动作漂移,而是公司级统一配置。

这件事很重要,因为审批材料通常是企业里最容易越积越乱的一类文档:

  • 报销凭证
  • 采购申请附件
  • 用章申请材料
  • 合同审批底稿
  • 费用证明与补充说明

如果没有统一标签入口,最后你在 Documents 里只能靠:

  • 文件名猜测
  • 上传人记忆
  • 审批单号搜索

那就退化成网盘了。

官方用 approvals_tag_ids 的意思很明确:

审批材料进入 Documents 之后,首先要变成可归类、可筛选、可治理的文档。


四、访问权为什么这么保守:默认不开放内部访问,但给请求拥有者保留可见性

approval_request.py 里最值得讲的是 _get_document_vals_access_rights()

return {
    'access_via_link': 'view',
    'access_internal': 'none',
    'is_access_via_link_hidden': False,
}

documents.mixin 的默认实现本来是更保守的:

  • access_via_link = none
  • access_internal = none
  • is_access_via_link_hidden = True

documents_approvals 在此基础上只放开了一点:

  • 外链层面可以 view
  • 内部默认仍然 none
  • 链接可见,不再隐藏

单看这段很容易误解成“审批文档对外更开放了”,但别忘了同一个模型里还重写了 _get_document_access_ids()

def _get_document_access_ids(self):
    return [(self.request_owner_id.partner_id, ('view', False))]

再配合 tests/test_documents_approvals.py,官方验证的是:

  • 请求 owner 会拿到 view
  • 对审批目录本来有权限的审批用户,也能访问
  • 不相关内部用户读不到

也就是说,源码真正做的是两层组合:

第一层:目录本身极保守

审批根目录默认 none/none,避免审批材料天然暴露给整个内部组织。

第二层:具体文档再补最小必要权限

至少让审批请求拥有者能看,同时允许目录级已授权的审批参与者继续工作。

这套思路比“内部所有人可见”稳得多。

documents_approvals 的权限策略不是广撒网,而是“默认封闭 + 业务参与者补洞”。


五、为什么文档 owner 不是请求 owner,而是当前操作用户

approval.request_get_document_owner() 的实现是:

def _get_document_owner(self):
    return self.env.user

这一点很容易被忽略。

它没有把 owner 固定成:

  • request_owner_id
  • create_uid
  • 审批类别管理员

而是直接取 当前环境用户

在测试里也验证了:如果由 approval_user 这个审批参与者实际创建附件,那么生成的文档 owner_id 就是这个用户。

为什么官方会这么做?

因为 Documents 里的 owner 语义更接近“当前文档的直接维护者/控制者”,而不总是“业务发起人”。

审批场景里这两者可能不是同一个人:

  • 发起人是报销员工;
  • 上传附件的是审批专员;
  • 审批材料归档时,真正处理文档权限的是审批角色;
  • 但请求 owner 仍要保留查看权。

所以官方把这两层拆开:

  • owner:给当前执行动作的人
  • 业务可见性:再通过 access_ids 单独补给请求 owner

这比把 owner 一把梭给业务发起人更灵活。


六、action_get_attachment_view() 为什么要做“开关式回退”

approval_request.py 还有一个很关键但很容易漏看的方法:

def action_get_attachment_view(self):
    if not self.company_id.documents_approvals_settings:
        return super().action_get_attachment_view()

如果公司没开 documents_approvals_settings,系统直接回退父类逻辑。

如果开了,则返回一个专门指向 documents.document 的动作,并带上:

  • res_model = approval.request
  • res_id = 当前审批单
  • searchpanel_default_user_folder_id = 审批默认目录
  • 域里同时包含文件夹与挂到该审批单上的文档

这说明官方不只是切换“显示哪个按钮”,而是在切换两套完整的附件查看语义:

关闭时

  • 审批单就按原始 approvals 逻辑看附件;
  • 不强依赖 Documents 工作台。

开启时

  • 附件视图直接转向 Documents;
  • 用户进入的是审批文档工作语境,而不是普通 chatter 附件列表。

这类“功能启用即切换动作语义,关闭即无损回退”的设计,在 Odoo 企业版里非常成熟。

开关不是装饰,而是决定审批附件究竟属于“普通附件模式”还是“文档治理模式”。


七、为什么 _check_create_documents() 还要再拦一次

documents.mixin 默认只要有 folder,就可能自动建档。

documents_approvals 还是额外加了一层:

def _check_create_documents(self):
    return self.company_id.documents_approvals_settings and super()._check_create_documents()

也就是:

  • 公司必须明确开启 documents_approvals_settings
  • 同时还得满足 folder 等基础条件
  • 才允许审批附件自动生成文档

这在架构上很关键。

因为它避免了一个很常见的企业系统坑:

  • 模块安装了;
  • mixin 已经继承了;
  • 用户并不想接入 Documents;
  • 结果附件仍悄悄被文档化。

官方这里等于在模型层再次声明:

审批附件变文档,不是“装了模块就必然发生”,而是公司显式启用的一项治理策略。


八、documents_count 与专用视图动作,说明它想让审批材料变成“可运营对象”

documents_count 不是简单数附件,而是专门统计:

  • res_model = approval.request
  • res_id in self.ids
  • active = True

也就是统计真正活跃的 documents.document 记录。

这意味着审批页上的文档数量,不再只是“记录上挂了多少二进制文件”,而是:

  • 已进入 Documents 的正式文档数
  • 能进入目录、标签、权限、搜索体系的对象数

这和传统“附件数”是两种完全不同的产品信号。

当官方再配上 action_get_attachment_view() 的 Documents 动作时,传达出的信息就很明确:

审批材料在企业版里不是零散附件,而是可以被统一治理、统一检索、统一授权的文档对象。


九、最容易误解的 5 个点

误区 1:这只是 approvals 多了个 Documents 按钮

不是。

它核心是把 approval.request 接入 documents.mixin,让附件可自动转文档。

误区 2:审批文档默认对内部都可见

不是。

默认目录与默认文档访问都很保守,真正依赖的是最小必要授权。

误区 3:请求 owner 就一定是文档 owner

也不是。

源码把 owner 给当前环境用户,而查看权再补给请求 owner。

误区 4:装上模块后所有审批附件都会自动建档

不对。

还要经过 documents_approvals_settings 开关与 folder 条件双重校验。

误区 5:关闭开关只是隐藏一下界面

不止。

附件查看动作和自动建档逻辑都会回退到原始 approvals 行为。


十、二开最值得抄的 4 条经验

如果你要自己做“业务审批对象 + 文档治理”的桥接,documents_approvals 最值得抄的是这四条:

  1. 用 mixin 接文档平台,不要再造一套附件逻辑。
  2. 默认目录与默认标签做成公司级配置,而不是上传时临时决定。
  3. 权限策略先保守,再对业务参与者补最小必要访问。
  4. 配置关闭时必须允许动作与自动化逻辑一起回退。

这四条都很朴素,但特别适合企业系统长期维护。


结语

documents_approvals 的价值,不是让 Approvals“也能看文档”,而是把审批材料从普通附件提升成 带目录、带标签、带权限边界、还能无损回退 的正式文档对象。

所以 Odoo 企业版这条链路真正解决的,不是“审批单怎么挂文件”,而是“审批材料如何以最低暴露面进入企业文档治理体系”。

理解了这一点,你以后再做请款单附件、采购审批附件、用章材料、合同审批底稿时,就不会只想着“用户在哪上传”,而会先问:

  • 默认落哪一类目录?
  • 谁天然能看?
  • 谁不该被默认放开?
  • 配置关闭后还能不能退回原模式?

这些,才是 documents_approvals 真正教你的东西。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。