企业项目深度

Odoo 项目文档为什么不是“项目下建个文件夹”就完了:documents_project 的自动建档、权限同步与上传边界讲透

很多人以为 Odoo 企业版项目文档只是给 project 加一个 documents 文件夹。可从 `documents_project` 源码看,官方真正做的是一套“项目 -> 文件夹 -> 文档默认归属 -> 对外上传入口”的联动机制。本文把自动建档、重命名、换文件夹、公司约束与分享边界一次讲清。

企业 项目
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

很多人第一次看到 Odoo 企业版的项目文档,会把它理解成:

  • 项目上多了一个 documents_folder_id
  • 用户往里传文件
  • 项目页顺手多了个 Documents 按钮

但如果你去看 /home/ubuntu/odoo-temp/enterprise/documents_project/,会发现官方的设计重点根本不只是“挂一个文件夹”,而是下面这条完整链路:

  • 公司先定义“项目根文件夹” documents_project_folder_id
  • 新建项目时自动生成专属子文件夹
  • 项目改名、换可见性、换公司时,文件夹要跟着校正
  • 上传到项目文件夹的文档,会自动继承项目客户 partner_id
  • 即使不共享整个项目,也可以单独共享文档上传入口
  • 删除、归档项目相关文件夹时,还有专门保护逻辑

一句话说:

Odoo 企业版项目文档的真正目标,不是“项目里能放文件”,而是“项目协作产生的文件,如何带着正确的归属、权限和生命周期进入 Documents 体系”。


第一层:项目文件夹为什么会自动创建

documents_project/models/project_project.py 里,project.project 增加了:

  • documents_folder_id
  • document_count
  • document_ids

最关键的不是字段本身,而是 create() 里会调用 _create_missing_folders()

也就是说,只要项目启用了这套能力,Odoo 在创建项目时就会自动:

  • 以公司级 documents_project_folder_id 作为父级目录
  • 新建一个和项目同名的子文件夹
  • 根据项目可见性初始化内部权限

这里的默认权限特别值得注意:

  • privacy_visibilityemployees / portal 时,文件夹内部权限给到 edit
  • 否则给 none

这说明源码的思路是:

项目文档权限,不是 Documents 自己凭空决定,而是先从项目的协作可见性出发。

所以它从第一步就不是“自由建个文件夹”,而是“项目配置驱动文档空间初始化”。


第二层:Documents 按钮为什么只是入口,不是核心

源码里既有 stat button,也有 embedded action:

  • action_view_documents_project()
  • 项目表单 / 仪表盘里的 Documents 嵌入动作
  • 项目 kanban 菜单里的 Documents 入口

从界面上看,这像是在项目里补一个快捷方式。

action_view_documents_project() 真正做的事,是把 Documents 视图的上下文切换到:

  • 当前项目
  • 当前项目文档根目录
  • searchpanel_default_user_folder_id
  • no_documents_unique_folder_id

也就是说,这个按钮的意义不是“打开一个普通列表”,而是:

  • 让用户以项目视角进入 Documents
  • 默认聚焦到该项目目录树
  • 避免用户误以为自己在全局 Documents 里乱翻

所以 Documents 按钮只是呈现层;真正重要的是后面的文件夹归属和继承规则。


第三层:为什么上传到项目文件夹的文档会自动带客户

documents_project/models/documents_document.py 里最值得讲的,是 _prepare_create_values()_get_link_to_project_values()

官方做法不是让每个上传动作都手写“把客户带上”,而是:

  1. 创建文档前先看目标 folder_id
  2. 如果这是普通文件而不是 folder,且不是已有业务记录附件
  3. 找这个目录最近的、且只绑定了一个项目的祖先目录
  4. 把该项目的 partner_id 自动塞进文档值里

这意味着什么?

这不是“项目文件夹能放文件”

而是:

文档一旦进入项目目录,就会尽可能继承项目业务上下文。

这个上下文当前最关键的是客户 partner_id

它解决了一个特别现实的问题:

  • 顾问、客户、项目经理都可能把文件扔进项目目录
  • 但 Documents 后续搜索、过滤、协作和外发,经常又依赖 partner 归属

如果项目目录里的文件没有业务对象上下文,文档系统就会退化成网盘。

Odoo 显然不想要这个结果。


第四层:为什么“最近的唯一项目祖先目录”这个判断很重要

_get_project_from_closest_ancestor() 的逻辑相当讲究。

它不是简单看“这个目录是不是某项目目录”,而是顺着 parent_path 往上找:

  • 哪个祖先目录绑定了项目
  • 并且这个目录只绑定了 一个 项目
  • 取最近的那个祖先目录

这套设计的含义很强:

1. 允许项目目录下面继续分子目录

比如你在项目文件夹下再建:

  • 合同
  • 交付物
  • 会议纪要
  • 验收资料

这些子目录里的文档,仍然可以继承项目上下文。

2. 避免共享目录导致归属混乱

如果一个目录被多个项目共用,源码就不会再武断地把文档自动归到某一个项目客户。

换句话说:

只有归属足够明确时,系统才自动补业务上下文;一旦有歧义,就宁可不乱猜。

这是很典型的 Odoo 设计风格。


第五层:项目改名、换目录、换公司时,为什么文档空间也要跟着变

项目改名

project_project.write() 里,如果满足两个条件:

  • 当前文件夹只绑定这一个项目
  • 文件夹名称本来就和项目名一致

那么项目改名时,文件夹也会跟着改名。

这说明 Odoo 不想把项目文档目录变成很快失真的“历史名字壳”。

更换 documents_folder_id

如果项目被改到另一个文件夹,源码会把原项目根目录下的子文档重新挂到新目录下。

重点是:

  • 不是只改项目上的 Many2one
  • 而是连项目根目录下的文档组织关系一起迁移

所以这里改的是“文档空间归属”,不是“显示入口”。

更换公司

这个部分约束更严。

源码会检查:

  • 项目文件夹若属于某公司,项目公司必须一致,或者文件夹公司为空
  • 如果一个文件夹同时被多个项目使用,而这些项目还在别的公司,就禁止你随便改公司
  • 公司级 documents_project_folder_id 也不能指向别家公司的文件夹

这背后的原则很清晰:

项目文档可以共享目录,但不能跨公司乱共享到破坏公司边界。


第六层:为什么删除 / 归档项目文件夹会被保护

documents_project 对删除和归档做了不少保护,很多人平时不会注意,但它们非常关键。

1. 不能删 Projects 基础目录

documents_project.document_project_folder 是受保护的基础目录。

不仅它自己不能删,连它的祖先目录也不能删。测试里专门覆盖了这个场景。

2. 不能删正在被项目使用的目录

如果某个目录或其上级还被项目拿来当 documents_folder_id,删除时会直接报错。

3. 不能归档项目基础目录

基础目录被归档同样会触发校验失败。

4. 项目删除后,相关目录会被自动归档,而不是粗暴物理删除

_archive_folder_on_projects_unlinked() 的逻辑是:

  • 如果一个目录关联的项目都被删掉了
  • 则把该目录归档

这个做法非常合理,因为项目文档往往带审计价值:

  • 你不一定想继续在界面里活跃显示
  • 但也不应该直接硬删掉

所以这里是“生命周期退场”,不是“数据彻底抹掉”。


第七层:附件为什么会自动建议放进项目文件夹

documents_project/models/ir_attachment.py 还补了一层体验优化。

当附件来自:

  • project.project
  • project.task

get_documents_operation_add_destination() 会自动把 Documents 的目标目录建议成对应项目的 documents_folder_id

这看起来像个小细节,其实非常重要。

因为真实使用里,很多文件最初不是从 Documents 页面上传的,而是:

  • 在项目 chatter 里上传
  • 在任务里补附件
  • 从活动上传入口提交文件

如果这些附件默认不知道该落哪里,项目文档体系就会断链。

官方这里等于是在补一句:

只要这份附件属于项目或项目任务,它就应该优先被纳入项目文档空间。


第八层:为什么“共享项目”和“共享项目文件上传入口”不是一回事

tests/test_routes.py 里有一个特别值得写进实施经验的测试:

  • 不共享整个项目给 portal 用户
  • 只给项目目录的文档权限
  • portal 用户依然可以通过 /documents/upload/<token> 上传文件
  • 文件会正确进入项目目录

这说明一件很重要的事:

项目协作权限,和项目文档上传权限,在 Odoo 企业版里是可以拆开的。

也就是说,客户可能:

  • 看不到完整项目后台或共享项目界面
  • 但仍然可以通过受控上传口,把交付材料、签字文件或反馈资料送进项目目录

这正是很多实施场景真正需要的能力。

它比“给客户开项目共享”更保守,也更好控边界。


第九层:活动上传为什么不会凭空多出两份文档

测试里还覆盖了一个很细的点:

  • 任务上安排一个“上传文件”类型 activity
  • 该 activity 绑定项目文件夹
  • 上传时先有一个临时文档
  • 完成 activity 后,最终只有一份真实文档留在项目目录

这个设计在防什么?

它防的是典型协作系统常见问题:

  • 活动有一份占位记录
  • 附件上传又新建一份文件
  • 最后用户看到重复文档

Odoo 这里明确保证:

  • 流程上可以先有临时文档承接上传动作
  • 但结果上不制造重复垃圾记录

所以它不是“能传就行”,而是把协作动作和文档落地的边界也处理了。


这套设计到底解决了什么问题

如果没有 documents_project,项目文档通常会落进三种混乱状态:

1. 文件是有了,但没有项目上下文

最后只能靠文件名、群聊和人脑找资料。

2. 文件在项目里能看见,但 Documents 里不可管理

这样文档系统和项目系统是断开的。

3. 外部人要交资料时,只能粗暴开项目权限

这会把协作边界放得过大。

而 Odoo 企业版这套实现,把问题拆成了三段:

  • 归属:项目自动建目录,目录继承项目上下文
  • 治理:改名、迁移、公司边界、删除归档都有规则
  • 协作:附件、活动、外链上传都能落进同一项目文档空间

这才是它比“项目下放附件”更高级的地方。


新手最容易误解的 5 件事

1. 以为项目文档就是项目字段指向一个目录

不对,后面还有自动建档、权限映射、文档值继承和生命周期保护。

2. 以为只有项目本身共享了,客户才能上传项目资料

不对,项目目录可以单独授权上传入口。

3. 以为项目改名只影响项目,不影响文档空间

不对,满足条件时文件夹会同步改名。

4. 以为目录改了只是 UI 入口变了

不对,项目根目录下的文档归属也会一起迁移。

5. 以为项目删除后文档目录就该直接删掉

不对,源码更倾向于归档,保留审计和追溯价值。


实战里最该注意什么

1. 实施时先设计公司级“项目根目录”

不要等项目跑起来后,再临时调整 documents_project_folder_id,否则很容易碰到迁移和公司约束问题。

2. 如果客户说“上传的文件没有自动带客户”,先查目录归属是否唯一

尤其要确认:

  • 文档是不是传到了项目目录或其子目录
  • 上游祖先目录是否只绑定一个项目
  • 文档是否本来就是已挂业务记录的附件

3. 如果业务只需要客户交付资料,不一定要开整个项目共享

很多时候给受控的文档上传入口,更符合最小权限原则。

4. 多公司环境下,不要图省事让不同公司的项目共用一个带公司属性的目录

源码已经把这类情况视为高风险边界,会在写入时卡住你。


一句话记忆法

Odoo 企业版项目文档不是“项目下面一个文件夹”,而是“项目自动生成文档空间,再把权限、客户归属、附件落点和外部上传入口一起接进 Documents 体系”的完整设计。

DISCUSSION

评论区

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