资讯动态

Odoo 19序列号配置全解:从界面字段到代码定义,彻底搞定单据自动编号

发布时间:2026/10/13 8:23:13 来源:尧图企业网站定制
序列号这功能看着不起眼却是很多人刚上手 Odoo 时最容易踩坑的地方。业务员要开销售订单仓库要打出入库单财务要对发票编号全都在跟它打交道。你要是不知道序列号到底怎么配、怎么用、怎么改系统上线之后光是改单号就能让人崩溃。我在多个 Odoo 项目中折腾过这套东西从最基础的自动编号到复杂的重置规则都试过一遍。今天就把 Odoo 19 里创建序列号这件事讲透从配置界面的每个字段到用代码定义序列再到各种看不出原因的奇葩问题一次性说清楚保证你读完能直接上手操作。不要小看序列号这个功能。它不只是“自动帮你排个号”这么简单它决定了单据编号的规范性、内外账目的一致性和团队协作的流畅度。适合正在实施 Odoo、准备自定义单据编号或者已经上线但想优化编号规则的朋友参考。1. 先搞清楚 Odoo 里的序列号到底是个什么东西1.1 序列号与日常编号规则的关系很多人一听说“序列号”第一反应是“产品上的序列号就是每个实物唯一的那个号”。但在 Odoo 里Sequence 这个功能的含义更宽它指的是所有单据和记录在创建时自动生成的唯一编号规则。销售订单、采购订单、发票、库存调拨、生产工单它们的单号本质上都来自同一种机制。你可以把 Sequence 理解成一个“自动发号机”。你给它设定好编号格式和递增规则它就在后台负责给每一条新记录发号。比如销售订单的编号是 SO00001、SO00002、SO00003每来一个新订单它就自动往下走一个号。这里有个很容易混淆的点Odoo 的序列号机制生成的是“记录的内部编号”而不是某个字段的值本身。这个编号可以显示在表单上、打印在单据上也可以被其他模块引用。搞清楚它的本质之后后面的配置就不容易乱。1.2 Odoo 19 里序列号能解决什么问题我常说序列号是“建模的底座”。没有它很多业务流程根本无法规范化运转。具体来说它解决这么几类问题保证编号唯一系统自动控制不会重复人工手动编号再怎么小心也做不到这一点。生成可识别的业务编号比如给销售订单加部门代号、加年月信息一眼就能看出这张单是谁、在什么时候开的。支持内外编号分离有些公司内部有自己的编码习惯客户那边又希望看到格式统一的编号序列号机制可以分别配置满足不同场景。支撑审计和追溯财务凭证、出入库记录全链路追踪都依赖编号的连续性。我在实施项目中发现90% 的客户对 Odoo 的初始需求里都带着“怎么改单号”这件事尤其是从 Excel 台账转过来的团队对编号的重视程度远超想象。所以把序列号这关过了后面实施会顺很多。1.3 先分清两种序列号这里必须先强调一个重要的区分因为很多教程都不说害得不少人在产品模块里翻遍了也找不到序列号配置入口。Odoo 里的序列号体系其实分成两套单据编号的 Sequence在开发者模式下通过“技术 序列”菜单进入控制销售订单、发票等单据的编号这个是接下来主要讲的内容。产品序列号跟踪Lot/Serial这个是库存模块里的功能用于给具体产品做批次或单品追溯配置入口在产品表单的“追踪”字段选项是“按序列号”或“按批次”。你要创建的是哪种序列号决定了后面走哪条路。这篇文章聚焦第一种也就是技术层面的 Sequence 配置。如果你想做产品级别的追踪那是在“库存 配置 设置”里开启“批次和序列号”功能不要跟这边的 Sequence 混在一起。2. 在界面上创建一个序列号的核心步骤与字段解析2.1 打开序列号配置入口序列号的配置入口默认是隐藏的。不是 Odoo 故意藏起来而是因为它属于技术类配置普通业务人员一辈子都用不着所以在界面里不显示。你必须先进入“开发者模式”才能看到这套设置。进入开发者模式的方法在设置页面找到“激活开发者模式”选项。有更快的办法直接在地址栏追加?debug1然后回车页面刷新后你就处于开发者模式。我建议新手先在设置界面里把开发者模式永久打开省得每次都要在地址栏操作一遍也方便同时查看很多其他技术字段。开启开发者模式后顶部菜单出现“技术”选项展开后找到“序列”点击就进入序列号的管理界面。这个列表页会显示系统里所有的 Sequence 记录差不多有上百条包括内部各种模块自带的。不要慌它们都是系统从模块安装时创建的正常情况下不要去动那些系统自带的出问题的风险很高。2.2 字段详解与配置逻辑创建新序列号时要填的字段不少。我按填写顺序一个字段一个字段讲清楚它们的功能和背后的设计逻辑。名称给这条序列规则起个名字一般用业务含义来命名比如“销售订单编号”。这个名字只影响列表里的显示不影响实际生成出来的单号格式。代码用于代码或外部标识引用的短代码我习惯用有意义的名字比如sale.order这样别人看代码时能立刻知道它对应哪个模块。这个字段可以留空但对做开发的人来说养成填写的习惯很重要。因为 Odoo 的模型里面经常通过代码来查找对应的 Sequence。前缀和后缀这是最核心的两个字段决定生成的编号长什么样。前缀是编号开头的内容后缀是编号末尾的内容。它们支持占位符比如%(year)s表示当前年份、%(month)s表示月份、%(day)s表示日期也可以输入固定的字符串。举个例子前缀填SO/%(year)s/生成的编号就是SO/2025/00001这种形式。下一个编号设定下一个要使用的具体数值。比如填 1那么下一条记录会从 00001 开始如果你填 50就直接从 00050 开始。已经生成的编号不会受影响系统上线初期一般填 1 就行。步进每次递增的数值默认为 1。绝大多数场景你都用不到其他数值除非有特殊需求比如想每次跳 2 个号。序列长度编号里数字部分最少几位。填 5 的话数字 1 就会显示成 00001数字 100 显示为 00100。实际动过这个字段的人都知道中途修改序列长度要谨慎因为已经生成的记录里长编号已经存在新编号变短会导致排序混乱看起来很不整齐。数字填充控制数字前面的填充方式默认按 0 填充一般不用改。每循环重置设置编号的循环周期比如每年、每月、每季度重置计数。公司里最常见的做法是按年重置因为跨年之后编号从 00001 重来很多财务单据都要求跟年度对应上。2.3 核心操作从零创建销售订单编号序列我带你完整走一遍创建流程。以“销售订单自动编号”为例子假设公司的编号规则是前缀SO/2025/数字长度为 5 位一年一重置。在序列号列表页点“新建”开始填写字段。名称填“销售订单自动编号”代码填sale.order。前缀填写SO/%(year)s/注意年、月、日占位符外面要加圆括号下面再加一个百分号这是 Python 格式化语法写错了系统不会因为保存报错但实际生成出来的编号就会显示%(year)四个字符而不是 2025很多人栽在这上面。真正使用时还有一个容易被忽略的细节前缀本身不要求以斜杠结尾但为了编号可读性建议带了占位符之后再加一个/否则系统生成的编号会挤在一起比如SO202500001。看起来也不是不行但长时间下来维护和识别很费劲。后继字段这样填下一个编号填1步进填1序列长度为5每循环重置选“每年”。保存之后这条序列规则就生效了。想验证效果新建一个客户、新建一张销售订单保存后订单号应该显示成SO/2025/00001这样的格式。如果显示的不是这样去查一下你的前缀格式是不是少了%的定位符或者数字长度填错位。2.4 使用中容易踩的界面操作坑界面操作看起来简单但我在实际项目里看到过不少同事在这些看似不起眼的小地方翻车第一个坑在“技术 序列”里改了编号规则但没有在“设置”页面里做任何关联。有些模块比如收款单的代码是独立写死的必须去对应的模型里找到company_id下的那个 Sequence 引用否则你的改动不会生效。第二个坑企业销售订单用的 Sequence 其实是模型里通过一个专门的字段引用的不能只靠“技术 序列”里的代码确定。如果你发现改了序列号之后销售订单还是原来的编号格式多半是你创建了一条新规则但销售订单模型里还指向老规则。第三个坑修改了前缀后缀格式之前已经产生的单据编号不会跟着变化。这是好事因为历史单据编号不能变是最基本的要求别指望改了规则能“追溯”回去把旧的也改了。3. 深入了解序列号背后的运作机制很多教程只告诉你怎么点鼠标不告诉你系统内部是怎么跑的。结果就是你照着配了一个序列号发现某些地方“不按预期工作”也不知道往哪里查。这一章我讲清楚 Odoo 的序列机制内部是怎么工作的。3.1 模型与序列的关联方式Sequence 在 Odoo 里的实现是一个独立的模型叫做ir.sequence专门负责存储编号规则。而每个业务单据如销售订单、发票在创建时会通过代码里的create()方法去调用这个序列规则生成编号然后把生成的编号写回自己的某个字段里。这个“写回”动作不是一个固定的内置行为。不同模块、不同版本的写法不一样有的是在模型里直接用字段指定的default函数有的用api.model_create_multi的create方法里主动取号然后在函数内部给字段赋值。所以安排配置时要注意改了技术菜单里的 Sequence 并不意味着所有单据都会自动响应。以销售订单为例在新版本的 Odoo 模型中实际上是通过一个特殊函数在创建记录时调用某个标准函数来拿编号。如果你在某个自定义模块里重写了create()并且忘记调用该标准函数那你的新订单可能完全没有自动编号或者全部编号都变成同一个值这是一个很经典的二次开发坑。3.2 为何需要把序列配置成“按年重置”我说一个更贴实际的例子。你的公司产品是电商类型订单量不断增长几年下来订单号可能已经几百万位录入也不方便。按照每年重置发票和订单编号都只在一个年度里连续这能与财务报告期一一对应审计统计时非常方便。另外有一些客户说“我的编号不要每年重置要永久连续”这也是允许的。只需要把“每循环重置”留空或者选择“不重置”系统就会一直往上递增。不要觉得这里不填就报错留空是合法的。3.3 “下一个编号”与“编号长度”的关系这里要解释一个经典误区下一个编号等于“当前序列号的值”而序列长度只是“显示宽度”它决定数字转成字符串的时候用几个位置来显示。系统内部保存的下个编号是一个纯整数生成字符串时补零。比如下个编号为 12长度为 4则生成0012下个编号为 12345长度为 4生成的字符串就是12345因为位数已经超过长度此时补零逻辑不再生效。所以你要是改了序列长度不用太担心破坏内部逻辑它不影响计数本身。唯一要注意的是某些打印模板或对接外部系统时如果要求“固定长度的编号”并用来做排序不同长度的编号可能在 Excel 里按文本排序时出现 10、2、1 这样的乱序这点要提醒业务团队在筛选时注意。3.4 多公司环境的特殊行为Odoo 的旧版本对多公司的支持比较弱但在新版本里每个公司可以有自己的序列。因为逻辑是不同公司的销售订单应该各有一套编号避免重号。配置的时候你会注意到界面上有一个“公司”字段这个字段决定这条序列生效的法人实体。假如你的环境里有 A、B 两个公司创建序列号时一定要明确公司归属否则全公司都会拿同一套序号。这也是我在实施多公司项目时第一次上线后发现的典型问题所有公司的单号挤在一起票据乱套。所以要单独给每个公司建一条序列规则并通过公司字段隔离。4. 实操案例按月重置的采购单编号销售订单整年重置的例子比较常见但实际业务里也会遇到“按月份重置”的需求尤其是采购部门他们习惯每月一份汇总台账。我完整演示一下如何在 Odoo 19 里做一套按月重置的采购单编号。4.1 业务需求与格式设计需求描述采购部门要求采购订单编号格式为PO/2025/05/0001对应 2025 年 5 月的第 1 张采购单。每月自动重置不跨月连续。这种编号在管理层那里很直观看单号就知道是哪个月的采购信息量大且容易统计。4.2 序列规则配置分解名称填“采购订单月度编号”代码填purchase.order。前缀部分要写出年和月所以填写PO/%(year)s/%(month)s/其中%m是月份如果月份 1 月会显示1还是01这里必须注意Odoo 的日期占位符有一个固定的格式它遵循 Python 的日期格式化语法%m产生的是补零后的两位数比如 5 月会显示为05。所以你不用额外处理“单位数月份要补零”的问题直接写%(month)s就行。序列长度填 4重置周期选择“每月”下一个编号填 1。这样 2025 年 5 月新增第一张采购单编号就是PO/2025/05/0001第二张为...0002如此递增。到了 6 月计数重置第一张采购单编号变成PO/2025/06/0001。4.3 关联到采购订单模型配置好 Sequence 之后要在采购订单里验证新建一个供应商、新建一张采购订单保存观察编号。如果发现编号还是系统原始的P00001说明采购订单模型没有走你新建的序列规则此时需要排查模型中的引用关系。在标准实现里采购订单的编号字段直接关联一个默认的 Sequence名称大概与“采购单”相关。我通常直接在技术菜单中通过“外部标识”反查当前采购订单模型使用的序列 ID然后把新序列的 ID 替换进去。完成之后再做一次采购订单的新建测试编号格式就对了。如果你不想改得这么深也可以直接修改系统原有的那条采购序列规则把前缀改成你的格式。两者效果差不多但各自适用场景不同修改冗余的序列规则更简单创建新序列再替换引用则更干净就看你怕不怕动模型。4.4 日期占位符完整清单做自定义编号绕不开日期占位符这里列一份常见对照表你直接抄走用占位符含义示例%(year)s四位数年份2025%(month)s补零的两位月份01、05、12%(day)s补零的两位日期03、15、31%(y)s两位数年份25%(j)s年内第几天001、123占位符写的时候一定注意最后面的s别漏掉模板里的括号也不能少。发现生成的编号里出现了字面量%(year)s而不是 2025马上检查是不是漏掉百分号或括号。5. 用代码定义序列号界面操作覆盖了绝大多数需求但如果你做自定义开发、需要打包成模块分发到其他环境那么用代码定义序列号是必须掌握的。我自己的项目里几乎很少手动在界面上点序列配置因为每次新建数据库都要重新点一遍太麻烦。直接写进模块里安装即生效。5.1 XML 定义序列的完整示例在自定义模块的data目录下建一个 XML 文件比如data/sequence_data.xml内容大概长这样?xml version1.0 encodingutf-8? odoo data noupdate1 record idseq_my_module_custom_order modelir.sequence field namename自定义单据自动编号/field field namecodemy_module.custom.order/field field nameprefixCO/%(year)s/%(month)s//field field namesuffix/field field namepadding4/field field namenumber_increment1/field field namenumber_next1/field field namecompany_id refbase.main_company/ /record /data /odoo这里noupdate1的含义是模块升级时不覆盖已有记录。如果你后期改了序列规则不想让系统强制覆盖回去就必须带上。但如果第一次安装模块时要执行创建noupdate不影响初次创建它只是避免后续模块升级时重置。字段名注意padding对应界面上的“序列长度”number_increment对应“步进”number_next对应“下一个编号”。在 XML 里这些字段名与界面显示名不同写错了会直接报错。5.2 如何把序列号关联到自定义模型有了序列规则还得让模型用起来。如果是一个新的自定义模型有一个最简单的方法直接在字段定义里指定默认方法。例如from odoo import models, fields, api class CustomOrder(models.Model): _name my_module.custom.order _description 自定义单据 name fields.Char(string编号, requiredTrue, copyFalse, readonlyTrue, indexTrue, defaultlambda self: self.env[ir.sequence].next_by_code(my_module.custom.order))这里next_by_code传入的是你定义 Sequence 时填写的 “代码” 字段值。每次创建记录时系统自动调用这个方法取下一个号然后填充到name字段里边。这种方法写起来最简单但也有局限若你要拿号之后还做其他逻辑比如根据某个字段去动态改前缀就不能在字段默认值里搞定了必须在create方法里手动处理api.model_create_multi def create(self, vals_list): for vals in vals_list: if not vals.get(name): vals[name] self.env[ir.sequence].next_by_code(my_module.custom.order) return super().create(vals_list)优先推荐第二种写法因为它能在多记录创建的场景下保证每条记录都取到正确且唯一的号码。5.3 修改已有模型引用的序列如果你想更换已有模型比如 sale.order的默认序列不打算动那套标准逻辑推荐用 XML 的方式去修改对应模型的一个字段定义。但这种方式有时候比较绕。更直接的方案是在 XML 里用field namesequence_id ref.../对对应的记录做替换。不过这要看对应模型中暴露出来的字段名是什么。我平时更常用另外一条路径直接在ir_sequence表中查出原 Sequence 的 ID然后更新它的名称、前缀等字段。这样不用改模型也不影响调用逻辑只改变生成结果def _update_sequence(self): seq self.env[ir.sequence].search([(code, , sale.order)], limit1) if seq: seq.write({ prefix: SO/%(year)s/, padding: 5, number_next: 1, })这个脚本很适合放在模块的post_init_hook里或者做成一个一次性迁移脚本上线时跑一遍即可。6. 常见问题与排查技巧实录序列号这种低层配置报错的时候往往不是直接红色的错误提示而是“看起来能用但编号不对”。这里把我踩过和排查过的高频问题整理出来供你遇到类似情况时照着排查。6.1 新配置的序列号始终不生效表现你在“技术 序列”里创建了一个新序列但新建订单时单号还是老格式或者空白。排查方向先判断是模型没有引用你的序列还是引用了但该字段被其他模块覆盖了。到表单上把鼠标移到编号字段上看有没有默认值来源的提示开发模式下常见。也可直接看数据库ir_sequence的code哪条记录最匹配你的模型。另外不要忘记针对多公司环境确认下模型的“公司”字段是否与序列公司匹配。一条比较实用的经验序列生效与否取决于模型的取号代码不一定取决于名称和代码。所以最稳妥的办法是修改原序列或动态替换引用而不是“新建一条记录然后期望它生效”。6.2 编号出现重复这种问题比较严重通常是多人并发时取的同一个编号造成的。Odoo 的设计中next_by_code会锁定序列记录相当于加行锁正常情况下不会重号。但如果你写的是循环内多次取值且用了相同的context或者没有提交事务后重新取号就可能导致重复。另外一个非常隐蔽的坑是有人为了“看了下测试数据”直接在数据库里手动改number_next字段改小了。结果已生成单据用了后面的编号新单序号倒回前头必然冲突。我的建议是任何时候不要手动 UPDATE 这个表里的number_next想改就通过界面让代码和缓存保持同步。6.3 编号跳号且跳的幅度很大编号跳号可能有三种原因被取消的单据已经占用了序号。Odoo 默认在创建记录时取号而不是在确认或保存草稿时取号。用户在界面上点击“新建”时只要记录被创建即使是草稿编号就已经分配出去了。以后这条草稿被删掉号也不会还回去最终结果就是跳号。有外部集成程序调用了next_by_code但不创建单据也会消耗编号。这种场景通常出现在错误的重试机制中次数多了跳号肉眼可见。多人同时建单其中某些人在多公司记录中来回切换导致对每个公司分别访问序列产生跨公司跳号假象。针对第一类跳号这是 Odoo 的默认和预期行为不建议为了补齐跳号去改号否则可能引发审计问题。6.4 按年重置但第一张单不是 00001重置选项在界面上选的是“每年”正常情况下每年 1 月 1 日之后新建单据从 00001 重新开始。但如果之前系统已经生成了部分编号并跨越了年份系统按重置逻辑仍然会用当前的编号计数器。也就是说你在 2025 年 6 月把重置选项从“不重置”改成“每年”并不等于 2026 年 1 月 1 日之前会先归零而是从下一自然年起才归零。还有种情况你只改了序列规则但单据数据里已经有 2025 年的编号到 8000改成年重置后2025 年剩余时间的编号会继续从 8001 往下走然后到了 2026 年重置回 00001。很多人看到“我已经选了每年重置怎么现在还是 8000 多”实际上这是完全正确的表现。6.5 前缀或后缀里想加固定字符串却显示不对如果前缀里想加固定缩写比如PRJ-直接填PRJ-就行。但如果你同时用了%占位符就要小心转义问题。例如要生成PRJ-2025-50%这种奇葩格式%符号在 Python 格式化里是特殊字符需要写成%%它才会输出一个%。不过 Odoo 的序列前缀处理方式与原生 Python 字符串格式化略有差异如果你的%与占位符混搭总是出错建议直接避开这种超特殊字符改用其他符合业务习惯的写法。7. 一些基于项目实践的建议我在项目里带新人时经常遇到“把序列配置做得过于复杂”的情况。前缀后缀塞了一堆缩写和特殊符号布局看起来很有条理实际维护起来很痛苦。这里分享几条我在实际项目里验证过的原则。7.1 命名规范和代码规范每条序列的名称必须能让人一眼看懂比如“销售订单编号规则”“采购申请单编号规则”。代码部分尽量与模型名保持一致的风格比如sale.order、purchase.order。这样万一出问题按代码搜索链路时才不会绕。7.2 能用界面就不用代码这个说法容易引起误会。我的意思是如果只是改一个格式直接界面操作最简单不要因此去开发一个额外模块。代码定义适合安装包分发、多环境同步、版本管理也适合要写自动化测试的场景。但纯配置上线的项目所有操作都走界面然后导出一份配置清单留给运维存档反而更可靠。7.3 做一个序列号交接文档很多项目上线半年后出了问题因为团队里已经没人知道这个序列是谁配的、为什么这样配。我现在的习惯是每个项目最后都会用一张表记录序列号配置列明名称、代码、前缀、后缀、长度、是否重置、生效模型、关联公司。交付的时候交给客户运维的时候照着排查节省大量时间。序列号这个看似简单的功能一旦形成清晰的文档资产后期无论转手几次都不会黑盒化。8. 最后分享一点实操心得序列号在 Odoo 里属于“平时没人注意出了问题所有人都来找你”的功能。我自己的经验是越早把编号规则想清楚越好。开工之前先拉上业务、财务、仓库的人把公司现有的全部单据编号格式理一遍整理成一份规则清单再动手去配。格式一旦确定尽量保持稳定频繁改规则虽然技术上不难但会造成历史数据和新数据混排审计和查询的负担都会上来。代码写的next_by_code虽然好用但我在生产环境里更偏好在create方法中取值而不是在字段默认值里取因为前者对批量创建、异常回滚的控制更细腻。对于跨国或多公司的项目去仔细确认每条序列的company_id归属比什么都重要这是我踩过最深的一个坑。希望在 Odoo 19 里倒腾序列号的你能从这篇整理里找到可以直接照搬的答案。遇到其他奇奇怪怪的编号问题都可以试试往回推看看你动过的前缀、长度、重置周期或公司字段——大概率答案就在这几项里。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑