企业 现场服务

Odoo 企业版现场服务为什么不是“签个字就算交付留痕”:worksheet 可用条件、portal 签署与报告附件回写讲透

基于 industry_fsm 源码,讲清现场服务报告何时可生成、为什么没工时时不能直接签署、portal 客户签字后又如何把 PDF 回帖到任务消息流。

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

很多人把 Odoo 企业版现场服务里的“签字”理解成一个很轻的动作:客户在平板上写个名字,系统存一张图片,事情就结束了。源码里的设计要严格得多。

在企业版 FSM 里,签字不是孤立动作,而是依赖于报告是否可生成、任务是否具备最基本的服务内容、portal 是否有合法 access token、签完以后是否回写 PDF 留痕。也就是说,它处理的不是“收集一张签名图片”,而是把客户确认、现场报告和消息流审计串成一条链

关键源码主要在:

  • enterprise/industry_fsm/models/project_task.py
  • enterprise/industry_fsm/models/ir_actions_report.py
  • enterprise/industry_fsm/controllers/portal.py
  • enterprise/industry_fsm/views/project_portal_templates.xml

一、为什么不是所有 FSM 任务都能直接点“Sign Report”

project.task 里最关键的两个判断方法是:

  • _is_fsm_report_available()
  • _has_to_be_signed()

其中 _is_fsm_report_available() 在当前实现里非常克制:它要求任务至少有 timesheet_ids。随后 _has_to_be_signed() 又在此基础上要求:

  • 这份 FSM 报告本身可用;
  • 当前任务还没有 worksheet_signature

所以系统的真实口径是:

不是“只要是 FSM task 就能签”,而是“这张 task 至少已经形成了一份可被称为服务报告的内容,而且还没签过”。

这比很多团队想象的严格,因为它避免了“什么都没做,只留一张签名图”的伪交付。

二、为什么没内容时连 PDF 都不让你生成

ir.actions.report._render_qweb_pdf()industry_fsm.worksheet_custom 做了专门拦截。

它会先过滤 display_satisfied_conditions_count,只有满足条件的任务才允许渲染 PDF;否则直接抛 ValidationError

所选任务没有工时、产品或 worksheet,现场服务报告不可用。

虽然 project.task._is_fsm_report_available() 当前最直接只看 timesheet_ids,但报告引擎这一层又多加了一道更广的安全阀。它强调的并不是“有没有签名按钮”,而是能不能生成一份像样的现场服务报告

这就形成了双层边界:

  1. 业务对象层:task 是否具备可签署条件;
  2. 报告渲染层:PDF 是否真的有内容可出。

三、portal 里的签字并不是随便 POST 一下图片

controllers/portal.pyportal_worksheet_sign() 是真正的签署入口。它做了完整校验:

  1. 先从 query string 或 json 参数里取 access_token
  2. _document_check_access() 校验该 portal 访问是否合法;
  3. 再确认 task_sudo._has_to_be_signed() 仍然成立;
  4. 如果没传 signature,直接返回错误;
  5. 尝试写入 worksheet_signatureworksheet_signed_by
  6. 若签名数据非法,则返回 Invalid signature data

这说明企业版并没有把客户签字做成一个前端装饰组件,而是当作一条需要权限、状态和数据格式同时成立的公开入口来处理。

也正因此,旧路径 /my/task/... 还会被强制重定向到 /my/tasks/...,避免 portal 链路混乱。

四、为什么签完以后还要重新生成 PDF 并回帖消息流

portal_worksheet_sign() 在签字成功后,并不会只保存二进制签名字段。它还会:

  • 调用 ir.actions.report 渲染 industry_fsm.task_custom_report 的 PDF;
  • 在任务 chatter 里 message_post()
  • 把“Field Service Report - 任务名 - 客户名.pdf”作为附件一起贴回去。

这个设计特别像企业系统会做的事:

  • 签名图只是原始证据;
  • PDF 报告是可交付文档;
  • chatter 附件是审计留痕和内部协作证据。

因此真正被保存下来的不是“客户签过字”这一个事实,而是:

某个有 access token 的客户,在某个可签状态下,对某张任务报告完成了签署,并生成了一份可回看的 PDF 文档。

五、为什么“Send Report”和“Sign Report”是两条不同状态链

project.task 里还有两条经常被混淆的字段:

  • fsm_is_sent
  • worksheet_signature

_compute_show_customer_preview() 明确写了:只要任务已发送过报告,或者已经存在签名,就可以展示客户预览。

action_send_report() / _get_send_report_action() 做的是:

  • 订阅客户 partner;
  • 打开邮件发送向导;
  • 使用 mail_template_data_task_report 模板;
  • 通过上下文 fsm_mark_as_sent 在发信后把 fsm_is_sent 标记为 True。

这意味着:

  • 已发送 不等于 已签署
  • 已签署 也不一定意味着是通过同一次发送动作进入的;
  • 客户预览资格则是两者的并集之一。

这组拆分很实用,因为现实业务里经常出现:

  • 技术员先现场让客户直接签;
  • 或者先发报告给客户,之后客户再远程签;
  • 或者已发但客户迟迟未签。

六、portal 模板里真正暴露给客户的是什么

project_portal_templates.xml 里,签字按钮和弹窗都不是无条件渲染:

  • portal_task_sign_button 只有在 task._has_to_be_signed() 时才显示;
  • portal_task_sign_modal 会把 task.idaccess_token、默认签名名和调用 URL 一并塞给前端;
  • 如果任务已经有 worksheet_signature,门户页面还会直接展示签名图片和 worksheet_signed_by

所以客户看到的 portal 并不是“一个随时可签的空白板”,而是一个受 task 状态严格控制的交付页面。

七、实战建议

  • 如果客户反馈“为什么我的任务没有 Sign 按钮”,先检查 task 是否真的已有工时/worksheet 内容,而不是先怀疑 portal 权限。
  • 想把 FSM 签字做成合规留痕时,不要只保存签名图片,应该同时保留 chatter PDF 附件与访问链路。
  • 自定义“无工时也能签”的逻辑时,要同时考虑 _has_to_be_signed() 与报告渲染拦截,不然容易出现按钮可见但 PDF 生成失败。
  • 培训一线团队时,要明确区分“发送报告给客户”和“客户已经签字确认”。

八、结论

Odoo 企业版 FSM 的签字流程,本质不是“收一张电子签名图片”,而是任务内容校验、portal 访问控制、PDF 报告生成与 chatter 审计留痕的组合。也正因为如此,它比表面看上去更重,但这套重量恰恰是现场服务交付证据可信的来源。

DISCUSSION

评论区

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