第四十六章 高级开发

篇别:第十四篇 高级主题

本章学习目标

  • 多公司 环境下正确配置 记录规则、默认值、报表上下文,并能解释 切换公司search 结果 的影响。
  • 使用 Monetary、汇率、公司币 处理 多货币单据与报表,区分 交易折算与期末重估 的职责边界。
  • 了解 自定义字段(含 Studio 动态字段)生命周期、导出与 Git 协作 风险。
  • 掌握 Studio适用场景与禁区,能将 导出模块 纳入 受控分支
  • 了解 Odoo SpreadsheetPivot/列表数据一致性校验 思路。
  • 能按 官方文档索引 阅读 accountpayment 等标准模块状态机与扩展点
  • 使用 website 模块 完成 路由、QWeb 页面、SEO 元数据最小站点页
  • 了解 移动端浏览器访问 时的 交互优化与条码等能力边界
  • 能在 报表/PDF独立接口 中嵌入 二维码(内容规范、纠错与依赖)。
  • 了解 Odoo Sign(电子签名)模板—请求—签署 链路与 自定义扩展点
  • 掌握 PDF/打印物水印常见实现路径(引擎差异与合规)。
  • 能通过 按钮/动作 触发 Word(.docx)服务端生成与下载(依赖与权限)。

导读:从「会用模块」到「敢改核心」

前各章已覆盖 ORM、视图、安全、前端、API、部署。本章把 多公司/多货币(与 第四十五章 呼应但侧重 产品功能与 Studio/网站)、低代码与电子表格标准模块阅读法网站与移动端 串成 高级主题闭环46.9~46.12 补充 二维码、电子签名、水印、Word 输出交付物常见需求。实施时务必 对照企业版许可与官方文档Studio、Spreadsheet、Sign、部分网站/电商能力 可能 因版本与订阅而异法律效力当地法规与审计要求 为准,技术实现不能替代法务评审


46.1 多公司

46.1.1 知识要点

  • 用户与公司:用户可关联 多个 res.company;界面 当前公司 影响 默认值、company_id 隐式过滤、部分报表范围
  • 记录归属:业务表常见 company_id / company_ids共享主数据(产品、伙伴)常 company_id 为空多公司规则 显式允许。
  • 记录规则ir.rulecompany_ids 元组('company_id', 'in', [...]) 组合;错误规则 可导致 「切换公司后数据消失」串库
  • with_company / sudo:代码中 显式切换 计算上下文;滥用 sudo绕过多公司隔离第四十五章第三十章)。
  • 测试同一用户、不同当前公司 各执行一次 search_count断言差异符合产品说明

46.1.2 案例

记录规则(示意)library.loan 仅本公司

<record id="library_loan_rule_company" model="ir.rule">
    <field name="name">借阅记录:多公司</field>
    <field name="model_id" ref="model_library_loan"/>
    <field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
</record>

model_library_loan XML ID 需与贵模块一致;domain 以业务为准。)

实操对比设置 → 用户 勾选 两家公司切换公司菜单同一列表菜单search_count 截图对比。

46.1.3 截图占位

图 46-1 用户多公司勾选与当前公司切换

46.1.4 本节练习

  1. 实操:编写 library.loan 记录规则:用户仅见本公司 借阅单(测试用户 两家公司 各验一次)。
  2. 简答company_id 为空 的图书记录在 多公司规则 下通常表示什么?
  3. 判断:切换公司 只影响前端显示不改变 env.company。( )

参考答案提示:2. 全局共享待分配(依产品定义)。3. 错;当前公司,影响 默认与部分 domain


46.2 多货币

46.2.1 知识要点

  • 字段Monetary + currency_field 表达 单据币种金额公司本位币 多为 company_id.currency_id
  • 汇率res.currency.rate 按日期历史凭证 必须用 过账日/业务日 对应汇率,勿用今日汇率重算去年账
  • 日常交易:销售/采购 外币价按规则折算 应收账款/应付账款公司币报表列
  • 期末重估未结外币余额期末汇率 调整;与日常折算分工不同,一般由 会计模块 提供 向导/分录
  • 自定义模块避免 手写 浮点乘汇率直接比较相等;优先 复用会计工具方法Decimal 策略(与团队规范一致)。

46.2.2 案例

报表列「公司币金额」amount_currency(外币)+ balance(公司币)account.move.line 中的分工(阅读标准模块字段说明)。

46.2.3 截图占位

图 46-2 货币与汇率列表(多币别)

46.2.4 本节练习

  1. 简答期末重估日常交易折算 的分工?
  2. 实操:在 测试库 创建 外币价目一张销售单,观察 公司币列汇率日期(若已装 sale)。
  3. 判断所有业务模型 都应自建 amount_company 浮点字段 存公司币。( )

参考答案提示:1. 日常 按交易日汇率;期末 未结余额重估。3. 错;应跟会计/标准模式


46.3 自定义字段

46.3.1 知识要点

  • Studio 动态字段:元数据在 ir.model.fields导出为模块 后变为 普通 XML/Python可入 Git
  • 手写 vs Studio同一模型 混用时需 统一负责人,避免 同义字段重复x_studio_* 与自定义名)。
  • 升级:Studio 导出模块 升级顺序depends生产 上 Studio 直接改 需变更流程禁止无评审直接改生产)。
  • 卸载:删除字段可能 丢数据先做备份与迁移脚本

46.3.2 案例

合并维护策略Studio 仅用于原型导出 studio_customization重命名/重构后续只改 Git 模块关闭生产 Studio 随意改(策略示例)。

46.3.3 截图占位

图 46-3 Studio 添加字段界面

46.3.4 本节练习

  1. 简答Studio 导出模块纯手写模块 如何 合并维护
  2. 判断:删除 Studio 字段后 PostgreSQL 列立刻无痕迹。( )
  3. 实操:在 测试库 添加 自定义 Char 字段导出对比 ir.model.fields 与 XML

参考答案提示:2. 不一定;可能保留列 直至 手动迁移


46.4 Studio

46.4.1 知识要点

  • 适合快速原型、非关键扩展、顾问现场配置
  • 不适合复杂状态机、强合规审计、需高测试覆盖 的核心逻辑——应 下沉淀为 Python 模块
  • 权限Studio 管理员 接近 结构改库能力生产收紧组
  • 导出自定义视图/字段/自动化 打包为模块;diff 审查 后再合并。

46.4.2 案例

清单:导出模块内 views/data/security/ 各打开 一文件,标出 可删的临时 xpath

46.4.3 截图占位

图 46-4 Studio 导出为模块向导

46.4.4 本节练习

  1. 实操:用 Studio 加字段导出 custom_module 纳入 Git测试库)。
  2. 简答两条 不建议用 Studio 做 的事?
  3. 综合:Studio 改的 sale.order 视图手写继承 冲突时 谁优先?如何查?

参考答案提示:3. priority 与加载顺序设置 → 技术 → 视图继承链


46.5 Odoo Spreadsheet(新增)

46.5.1 知识要点

  • 定位自助分析、管理层看板;与 Pivot 视图 共享 部分数据理念以版本文档为准)。
  • 一致性同一 domain、同一公司上下文Spreadsheet 汇总 应与 Pivot 可对账差异 多来自 时区、归档过滤、缓存
  • 权限电子表格内数据 仍受 ORM 权限 约束;分享链接合规评审
  • 扩展数据源插件 / 公式集 随版本变化;定制前读官方 Spreadsheet 开发说明

46.5.2 案例

校验脚本思路导出 Pivot CSVSpreadsheet 同条件单元格 对比允许小额舍入差)。

46.5.3 截图占位

图 46-5 Spreadsheet 与 Pivot 并排对数

46.5.4 本节练习

  1. 案例:用表格做 「按类别图书库存」 并与 Pivot 一致性校验(记录 一条 差异原因假设)。
  2. 简答:Spreadsheet 适合不适合 作为 唯一真相源 的场景?

参考答案提示:2. 探索适合过账与法务业务单据/会计为准


46.6 标准模块参考(新增)

46.6.1 知识要点

  • 阅读法模型 _name → 状态字段 → def action_* / button_* → 报表与约束
  • account.movestatedraft/posted/cancel 等)、过账方法account.move.line 关系
  • payment支付提供商、token、回调第三十八章 衔接。
  • 文档官方每个应用一篇 Application/Accounting源码 addons/account最终真相

46.6.2 案例

笔记模板模型名|关键状态|不可逆操作|常用扩展点(_inherit 钩子)

46.6.3 截图占位

图 46-6 开发者模式下 account.move 表单与状态

46.6.4 本节练习

  1. 拓展:阅读 account.movestate 流转 相关 方法名列表笔记 5 条)。
  2. 简答:为何扩展 支付应优先读 payment provider 架构 而非 只 copy 控制器

参考答案提示:2. 安全、回调验签、多提供商抽象 已在核心中处理。


46.7 网站功能开发(新增)

46.7.1 知识要点

  • 模块website;页面多为 QWeb 模板 + Controller 路由
  • 路由@http.route type='http' website=TrueSEO slug多语言 hreflang(进阶)。
  • SEO<meta name="description">、标题、canonicalsitemap网站配置 生成(以版本为准)。
  • 电商website_sale 扩展点多;与库存、定价、多公司 组合需 整体验收

46.7.2 案例

from odoo import http
from odoo.http import request

class LibraryWebsite(http.Controller):
    @http.route("/library/hours", type="http", auth="public", website=True)
    def hours_page(self, **kw):
        return request.render("my_library.library_opening_hours", {})

QWeb 页 内设置 <t t-call="website.layout">meta以官方网站开发文档为准)。

46.7.3 截图占位

图 46-7 网站页「推广 → 元数据」

46.7.4 本节练习

  1. 实操:发布 「图书馆开放时间」 静态信息页并设 slug
  2. 简答Portal纯 public 页面auth 上的典型差异?
  3. 判断:网站页 无需 考虑 多公司路由。( )

参考答案提示:3. 错;定价、库存、可见分类 可能 跟公司


46.8 移动端开发(新增)

46.8.1 知识要点

  • 形态响应式 Web 为主;专用 App 多为 WebView / 品牌包装以 Odoo 移动策略为准)。
  • 交互大触控目标、减少横向滚动、表单分步列表虚拟滚动 由 web 模块处理,自定义勿破坏。
  • 条码部分浏览器 APIHTTPS硬件扫描枪模拟键盘输入,与 IoT 不同。
  • 离线有限关键业务 勿假设离线可用

46.8.2 案例

条码(思路)<input> 聚焦 + 快速回车提交 适配 激光枪摄像头扫码浏览器 Media API需权限与兼容矩阵)。

46.8.3 截图占位

图 46-8 手机浏览器访问 Odoo 列表

46.8.4 本节练习

  1. 简答:移动端优先优化哪 三类 交互?
  2. 实操:用 Chrome 设备模拟表单「保存」按钮 是否 拇指可达(记录 一条 改进)。
  3. 拓展Mobile JavaScript API(若文档提供)与 桌面 web client差异 一句话。

参考答案提示:1. 触控、网络弱、可视区域小


46.9 二维码生成(说明与实现)

46.9.1 知识要点

  • 用途URL、单据号、访客登记、支付参数快速录入;与 一维条码(EAN、Code128)分工不同——二维码信息密度高,适合 短 URL 或结构化字符串
  • Odoo 中的位置QWeb 报表 内嵌 PNG/SVG;或 Controller/字段 返回 data:image/png;base64,...库存条码 多与 stock/硬件 联动,QR自管生成逻辑
  • 依赖:常用 qrcode + Pillowrequirements.txt 声明);未固定版本 的生产环境 隐式依赖
  • 安全:QR 内容若为 URL,须 HTTPS防短链劫持敏感令牌 不宜 长期印在 公开报表可撤销、短时有效内部扫码)。
  • 打印质量纠错级别(如 ERROR_CORRECT_M)、模块大小打印机 DPI 匹配,避免 现场扫不出

46.9.2 案例(服务端生成 + 报表展示)

模型方法(示意:为图书记录生成 借阅查询链接 的 QR 图):

import base64
import io

import qrcode

def _library_qr_png_b64(self, url: str) -> str:
    buf = io.BytesIO()
    qrcode.make(url, box_size=4, border=2).save(buf, format="PNG")
    return base64.b64encode(buf.getvalue()).decode()

QWeb(片段):<img t-att-src="'data:image/png;base64,' + doc.qr_b64"/>qr_b64 用计算字段或 report 上下文传入)。

表单按钮ir.actions.server 或 object 方法 打开 带 QR 的报表 ir.actions.report(与 第十三章 QWeb 报表衔接)。

46.9.3 截图占位

图 46-9 报表或表单中的二维码预览与实扫测试

46.9.4 本节练习

  1. 简答二维码内容内部 API 密钥至少两条 风险?
  2. 实操:在 测试模块一条业务记录 生成 https:// 测试页 的 QR 报表 PDF
  3. 判断所有条码场景 都应使用 QR。( )

参考答案提示:1. 泄露、不可吊销、打印扩散。3. 错;一维码零售扫描 仍常见。


46.10 电子签名(Sign)功能与实现思路

46.10.1 知识要点

  • 产品sign 应用(多为 企业版)提供 PDF 模板、签名字段、签署顺序、邮件邀请、证书/审计轨迹以版本文档为准)。
  • 数据流sign.template 定义版式 → sign.request 发起 → 签署人 Portal/邮件链接状态机signed/refused/canceled 等)→ 归档 PDF
  • 扩展自定义字段类型与业务单据关联res_model/res_idsign.request 上增加 Many2one)、签署完成后 message_post / 回写业务 state@api.model_create_multi / write 约束 防绕过)。
  • 合规电子签名法 因地而异;技术要点 包括 身份验证、意愿表达、防篡改、可追溯——需法务与实施共同定稿勿仅依赖「勾选项」
  • 自研替代:无 Sign 许可时,外接 DocuSign/法大大Webhook + 附件回写 是常见模式(与 第三十八章 Webhook 衔接)。

46.10.2 案例(签署完成回写业务)

思路:继承 sign.request(或 监听消息/自动化),在 state == signed字段名以源码为准)时 record.sudo().write({'signed': True}) ——sudo 须再校验 发起人权限单号匹配

46.10.3 截图占位

图 46-10 Sign 模板与签署请求关联业务单据

46.10.4 本节练习

  1. 简答Sign 模板普通 QWeb PDF 报表本质差异一条)?
  2. 判断电子签名 等价于 用户在表单点了「确认」按钮。( )
  3. 综合:列出 签署完成后 应写入 chatter两条 审计信息。

参考答案提示:1. Sign 含 法律意义上的签署流程与证据链产品化)。2. 错。


46.11 水印功能(说明与实现)

46.11.1 知识要点

  • 目的「草稿」「机密」「样张」视觉提示不能 单独作为 防泄密 手段(PDF 仍可被提取文本)。
  • 实现路径(择一或组合)
    • wkhtmltopdf / Chromium PDFCSS background-image / @page 水印层矢量文字 可用 绝对定位 opacity)。
    • 后处理PyPDF / pypdf 合并「仅水印页」或 每页叠加 透明 PNG注意性能与大 PDF)。
    • ReportLabCanvas 旋转平铺 drawString适合 完全代码生成的 PDF)。
  • Odoo 接入点:继承 ir.actions.report._render_qweb_pdf(或 版本对应的 hook)在 返回 bytes水印函数仅对特定报表 report_name 判断,避免 全站变慢
  • 多语言/多公司:水印文案 翻译company_id 标识 常需 动态传入
  • 性能大批量打印后处理 可能 倍增耗时优先渲染阶段 带水印 或异步 job

46.11.2 案例(伪代码:后处理叠水印)

def _apply_watermark_pdf(pdf_bytes: bytes, text: str) -> bytes:
    # 使用 pypdf 等将带透明度的水印 Form XObject 合并到每一页(示意)
    # 具体 API 以所选库文档为准
    return pdf_bytes

调用点自定义 report_render_qweb_pdf 末尾 return self._apply_watermark_pdf(super()._render_qweb_pdf(...), _("草稿"))

46.11.3 截图占位

图 46-11 带「草稿」斜纹水印的 PDF 预览

46.11.4 本节练习

  1. 简答:为何 水印 不能 当作 唯一保密措施
  2. 实操:选定 一种 PDF 栈(wkhtmltopdf 或 pypdf),在 测试报表叠一层半透明文字
  3. 判断:所有报表 统一 加水印 不影响 性能。( )

参考答案提示:1. 可复制文本/截图。3. 错;CPU/IO体积 可能上升。


46.12 点击自动生成 Word(.docx)实现

46.12.1 知识要点

  • 依赖python-docxApache 2.0)常见;复杂版式 可用 模板 .docx + docxtpl(Jinja)额外依赖)。
  • 权限下载ir.actions.act_urlHTTP Content-Disposition: attachmentauth='user'check_access 业务记录。
  • 流程按钮object 方法 生成 BytesIO .docxbase64.b64encodeTransientModel 向导 字段 直接 request.make_responseController 更利于 大文件流式)。
  • 与报表分工QWeb → PDF 适合 版式固定、打印Word 适合 客户再编辑、法务改稿——docx 冒充 正式归档 PDF除非流程规定)。
  • 安全动态段落 若含 用户 HTML,须 strip_tags / 白名单,防 XXE解析 XML 时 使用 安全解析器配置)。

46.12.2 案例(按钮 + 临时下载)

import io

from docx import Document
from odoo import models

class LibraryBook(models.Model):
    _inherit = "library.book"

    def action_export_docx(self):
        self.ensure_one()
        doc = Document()
        doc.add_heading(self.name, level=0)
        doc.add_paragraph(self.description or "")
        buf = io.BytesIO()
        doc.save(buf)
        att = self.env["ir.attachment"].create({
            "name": "%s.docx" % (self.name or "export"),
            "type": "binary",
            "raw": buf.getvalue(),
            "res_model": self._name,
            "res_id": self.id,
        })
        return {
            "type": "ir.actions.act_url",
            "url": "/web/content/%s?download=true" % att.id,
            "target": "new",
        }

说明:需保证 调用者对 ir.attachment 与业务记录create/write 权限;否则 单独 ACL系统用户代建 + 受限 token 下载。大文件可改 Controller + stream定期清理 临时附件可用 计划任务向导 TransientModel 承载

向导模式TransientModelbinary 字段form 上「下载」按钮 attrs 仅当 file 非空

46.12.3 截图占位

图 46-12 表单按钮导出 Word 与打开效果

46.12.4 本节练习

  1. 简答PDF 报表Word 导出 各适合 一种 业务场景?
  2. 判断python-docx 生成的文件 天然带 与 Odoo 相同打印按钮版式。( )
  3. 实操:为 自定义模型 增加 action_export_docx经附件或 Controller 下载 可打开.docx

参考答案提示:1. 固定版式/归档→PDF可编辑交付→Word。2. 错;版式独立设计


本章综合练习

  1. 大作业:设计完整 「图书借阅」 交付:模型、权限、视图、报表、网站公告页、REST 查询接口、测试用例(可 分阶段 提交)。
  2. 答辩题多公司 + 网站电商 同时启用时,库存与定价一致性 可能 哪里翻车?(不少于 3 点
  3. 综合Studio 导出模块手写 sale 扩展 并存时,Code Review 重点 5 条
  4. 阅读:列出 第四十六章第四十五章分工各一句:何时读哪章)。
  5. 判断:高级开发等于 可以任意 sudo 写多公司数据。( )

参考答案提示:5. 错。


本章对应白皮书目录:第四十六章 高级开发。下一章 第四十七章 常见应用场景 将多公司、索引、库存追溯、context 与批量写入等收束为场景化自查。