会计锁定

Odoo Fiscal Year Lock 为什么不只是“月末关账”:global/tax/sale/purchase/hard lock 与 lock exception 的层级讲透

很多人把 Odoo 的锁定日期理解成一个总开关,但官方源码其实做了分层:global、tax、sale、purchase、hard lock,再加上可审计的 lock exception。本文把这套设计讲清楚。

Odoo 开发 会计
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

结论先行

在 Odoo 里,Fiscal Year Lock 并不是一个“这个日期前统统不准动”的粗暴开关

更准确地说,它是一套分层保护体系:

  • fiscalyear_lock_date:通用会计期间保护
  • tax_lock_date:税务申报保护
  • sale_lock_date:销售类锁定
  • purchase_lock_date:采购类锁定
  • hard_lock_date:更强、不可回退的硬锁定
  • account.lock_exception:带审计痕迹的临时例外机制

所以官方设计的重点不是“关不关”,而是:

谁在什么边界内还能做什么动作。


为什么 Odoo 不做成一个总锁

真实业务里,“不能改”其实有很多原因:

  • 财务期间已结账
  • 税报已经申报
  • 销售或采购模块希望先局部锁定
  • 某些历史期间甚至要求永久不可回退

如果只放一个 lock date,系统就会有两个问题:

  1. 太粗:不同责任边界被一刀切
  2. 不够准:用户不知道自己到底踩了哪条线

/home/ubuntu/odoo-temp/addons/account/models/company.py_get_lock_date_violations()_get_user_fiscal_lock_date() 就是在做这件事:

  • 按不同 field 分别判断是否违规
  • 再结合 journal 类型决定 sale / purchase lock 要不要参与
  • 最后把 hard lock 一起纳入

这说明“锁定”在 Odoo 里是组合判断,不是单字段判断。


Hard Lock 为什么比普通 Lock 更重

源码里对 hard_lock_date 的态度很强硬:

  • 不能删除
  • 新的 hard lock 不能比旧的更早
  • 如果锁定期间内还有 draft entries,会直接阻止

这意味着 hard lock 的语义不是“方便管理”,而是更接近:

一旦走到这一步,系统默认你是在建立不可逆的法定边界。

所以 hard lock 不是给“试试看”的。


Lock Exception 真正解决了什么问题

很多系统一旦锁账,就只能“放开锁再改”。

Odoo 没这么做。

/home/ubuntu/odoo-temp/addons/account/models/account_lock_exception.py 里,account.lock_exception 被设计成一个独立模型,它会记录:

  • 针对哪个公司
  • 给谁开的例外(甚至可对 everyone)
  • 改的是哪种 lock date
  • 原始公司 lock date 是多少
  • 例外有效到什么时候
  • 理由是什么

更重要的是,它会把变更记到 chatter,并且状态分成:

  • active
  • revoked
  • expired

这说明官方不是鼓励“偷偷开锁”,而是在提供:

可控、可追踪、可过期的关账例外。

这套设计很成熟。


为什么“用户看到的 lock date”不一定等于公司字段值

company.py 里的 _get_user_lock_date() 很关键。

它不是直接返回公司上的 lock date,而是:

  • 先看公司及父公司
  • 再看当前用户是否命中了 exception
  • 最后算出“这个用户实际有效的 lock date”

所以两个用户面对同一家公司、同一期间,可能看到的是不同可操作边界。

这也是很多人排查 lock 问题时会忽略的一层:

  • 配置没错
  • 日期也没错
  • 但当前用户正好被 exception 放行或没被放行

新手最容易误解的 4 件事

1. 以为 fiscal lock date 就是唯一锁定

不是。tax、sale、purchase、hard lock 都可能一起参与。

2. 以为 lock exception 就是偷偷绕过控制

不是。它是带审计信息的正式例外机制。

3. 以为 hard lock 只是更晚一个日期

不对。它在规则上更硬,也更不可逆。

4. 以为查公司字段就等于查当前用户实际边界

不对。用户级 exception 会改变“有效锁定日期”。


实战排查顺序

如果用户说“这个日期为什么不能改 / 别人能改我不能改”,建议这样查:

  1. 公司上的各类 lock date 分别是多少
  2. 当前 journal 属于 sale / purchase / general 哪一类
  3. 是否存在 hard lock
  4. 当前用户是否命中了 account.lock_exception
  5. 例外是否已经 revoked 或 expired

一句话记忆法

Odoo 的关账不是一个锁,而是一组分层边界;lock exception 也不是偷开后门,而是带审计痕迹的临时通行证。

DISCUSSION

评论区

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