Featured image of post 退役 ERPNext 六容器,8090 端口跑起了 Odoo 18 中文账

退役 ERPNext 六容器,8090 端口跑起了 Odoo 18 中文账

上篇用 107 个 AI agent 选定 Odoo 社区版+l10n_cn,这篇执行:ERPNext v15 六容器从 8090 端口整体退役,Odoo 18+PostgreSQL 一个 compose 顶上,config/addons/数据卷三处挂载,建库即铺好小企业会计准则科目表,13%/9%/6% 增值税率官方预置、3% 征收率手动补上,第一张费用单借贷分录自动生成、试算平衡表出数,界面全中文。数电票和金税只能走外部付费层,踩坑一并交代。

结论先讲:8090 端口还是那个端口,里面已经换人。 原来跑在上面的 ERPNext v15 六容器已整套退役,compose 停掉带卷删,镜像删光,实例目录拆走,hosts 里那行解析也摘掉。同一个端口现在顶上来的是 Odoo 18 社区版 + PostgreSQL。建库选中国,小企业会计准则科目表自动铺好,13%/9%/6% 增值税率官方预置,3% 征收率手工补一条,第一张测试费用单按下确认,借贷分录当场生成,试算平衡表出数,界面全中文。

这篇是执行记录。上篇我调了 107 个并行 AI agent,949 次工具调用,20 分钟跑完报告,把三款开源记账底座拆到源代码,结论是 Odoo 社区版 + 官方 l10n_cn,过程见《给公司挑记账开源底座》。调研做完,轮到动手。下面每一步都带验证方法,你照着走,今天就能在自己的服务器上开出一套中文记账系统。

动手前先摆一句反直觉的判断。小公司的记账底座,关键决策是维护路线,软件本身说换就换。我装完这套,Odoo 里没有一个数电票接口,也没有金税四期对接,三家开源全都如此,深度合规从一开始就划在外部付费层。想清楚这句,后面很多纠结根本不会发生。

前置条件

材料清单只有三样。

  • 一台能跑 Docker 的机器,Compose 插件装好,docker compose version 有输出即可;
  • 一个能访问到这台机器 8090 端口的方式,内网直接用 IP,要外网再套一层反代;
  • 上篇最好看过,没看也不影响跟着做,选型的理由都在链接里。

成本提前交代,软件层零元,三块全是社区版或 GPL 系自托管,跑在原本就有的那台服务器上,不新增开支。域名可以先不配,内网 IP 加端口一样能把账开起来,反代等真的要外网访问那天再套。

先把 ERPNext 六容器退干净

8090 上原来跑着 ERPNext v15,域名 erp.lingxu.local,就是上篇里我进过容器翻 erpnext_china 源码的那套实例。退役的账我算过:一个已经停更 8 个多月的中国插件,加一条为了装它才存在的镜像构建流水线,六只容器的日常全是维护成本,换不来任何财税本地化。既然上篇定下了 Odoo,这套留着每天就多一套要盯的东西。

这里专心讲怎么退,四个动作,做完一步验一步。

  1. 在旧实例目录跑 docker compose down -v。六个容器的停服顺序 compose 自己会处理,依赖关系它都认识;-v 把挂着的卷一起删掉,不加的话匿名卷留在磁盘上继续占地方。
  2. 删镜像。docker image ls | grep frappe 把全家桶找出来,docker rmi 挨个删。
  3. 删目录。ERPNext 的数据在 MariaDB 里,先 mysqldump 导出一份,附件目录 rsync 一份,两份都落盘,再 rm -rf 整个实例目录。这一步不可逆,备份是唯一的后悔药。
  4. 清 hosts。把 /etc/hosts 里 erp.lingxu.local 那行解析摘掉,域名从此没有存在的必要。

四步做完,统一验一遍。

1
2
3
4
docker ps -a                             # 看不到任何 frappe 开头的容器
docker image ls | grep frappe            # 空
curl -s http://localhost:8090            # 连接被拒,端口已经让出来
grep erp.lingxu /etc/hosts               # 无结果

验到这一步,8090 才算真正空出来。退役一共就这四条命令加一套验证,比想象中便宜,也比想象中容易拖延。

如果你的旧系统里已经滚着真实历史账,退役前要把科目映射和期初余额导入的方案定好,那属于另一篇文章的活;跟账无关的旧数据,留一份 dump 在备份里就够。这一层的原则,先备份,后删除,顺序不能反。

一个 compose 文件,把 Odoo 18 顶上来

新底座放在 ~/odoo-cn,一个 docker-compose.yml 全搞定。只装官方镜像和官方 l10n_cn,不碰任何第三方中国模块,原因放在后面踩坑一节。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# ~/odoo-cn/docker-compose.yml
services:
  web:
    image: odoo:18
    depends_on:
      - db
    ports:
      - "8090:8069"
    volumes:
      - odoo-data:/var/lib/odoo
      - ./config:/etc/odoo
      - ./addons:/mnt/extra-addons
    environment:
      HOST: db
      USER: odoo
      PASSWORD: odoo
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: odoo
      POSTGRES_PASSWORD: odoo
      POSTGRES_DB: postgres
    volumes:
      - pg-data:/var/lib/postgresql/data

volumes:
  odoo-data:
  pg-data:

compose 里那句 8090:8069 的意思是,外部仍用 8090,进容器后映射到 Odoo 默认的 8069,以后想换入口只改左边那个数,容器内部不用动。

三处挂载各管一事。数据卷 odoo-data 存账套附件和会话文件,pg-data 是 PostgreSQL 的数据本体,这两个丢了等于账没了;./config 放 odoo.conf,调参数不用进容器;./addons 留给以后的自定义模块。目录长下面这个样。

1
2
3
4
~/odoo-cn
├── docker-compose.yml
├── config/odoo.conf
└── addons/

数据只住两个地方,pg-data 和 odoo-data,备份出口就是 pg_dump 加上数据卷的拷贝,不给账套留第三个家。单机部署里,这是最不容易出错的结构。

起服务只有两条命令。第一次 up 要拉 odoo:18 和 postgres:16 两个镜像,拉完自动替你把容器带起来。

1
2
cd ~/odoo-cn
docker compose up -d

起不来的时候,docker compose logs -f web 拉日志看,常见就两类,db 还没就绪 web 抢跑(重启 web 容器就好),或者 8090 被旧进程占着(换左边端口)。

验证: docker compose ps 里 web 和 db 两个容器都是 Up;浏览器打开 http://<服务器IP>:8090,看到的是一页中文的数据库管理页,底座就成了。

建库选中国,科目表和税率自己长出来

点创建数据库。Master Password 填 admin,这是 odoo 官方镜像的默认值,上生产前记得改掉。库名随意,语言选中文(简体),国家选中国,邮箱密码填自己的。

装会计应用的时候,Odoo 按创建时选的国家自动挂上 l10n_cn 财务本地化,然后问你要哪套科目表。官方模板两套,大企业会计准则和小企业会计准则,大企业版科目更细,小企业版对我们够用。我们公司杭州凌序科技,2025-09-19 成立,小规模纳税人,选小企业会计准则,下一步,几分钟后科目表铺完。

应用我一起装了会计、销售、采购、库存四个,网站和营销先留着。社区版里开多少应用都不另外收费,后面要用哪块再点开就行,没有"开模块要钱"这回事。

l10n_cn 的全部家当,上篇核过 manifest,科目类型、科目表模板、增值税结构、省份中文化、凭证格式打印模板。前四样建库时就生效,最后一样在打印凭证的时候用,出来的凭证格式是中国的样子。

税基本不用自己建。会计 → 配置 → 税,官方模板预置 13%、9%、6% 三档,物料和费用行上按税率模板自动挂。我们小规模纳税人开票要按 3% 征收率走,这条税率是我手动加的,拆分录的逻辑跟预置档一样。哪天转了一般纳税人,预置的三档直接能用。

建完库补两件小事。config/odoo.conf 里的 admin_passwd 改掉,重启 web 生效;数据库的 pg_dump 挂上 crontab,一晚一份,滚动留七份,这是整套系统最便宜的一份保险。

验证: 会计 → 配置 → 科目表,第一个科目是 1001 库存现金,往下 1002 银行存款,费用在 5602 管理费用,收入在 5001 主营业务收入,全部中文。菜单、按钮和报表标题,没有一个英文残留。

设置页也过一眼,会计 → 配置 → 设置,财务本地化一栏写着中国,币种自动落在人民币。这两页看完,本地化验收就算完成,后面所有单据的科目和税率都从这两处往下带。

第一张费用单:分录自动写,试算平衡当场平

验收这套系统,就看一件硬事,它敢不敢替你写分录。我建了个测试供应商,开一张供应商账单,行上选管理费用下面的办公费,选上适用的增值税率,填不含税金额,保存、确认一步不跳。点确认后,再去 会计 → 会计 → 分录,同一条单号下面躺着三条。

1
2
3
借 管理费用(办公费)
借 应交税费-应交增值税(进项税额)
贷 应付账款

三条的方向和金额系统自动配平,分录表单一个字没填。科目表对着小企业会计准则长出来的好处就在这里,科目名全是标准中文,税率那行也是系统拆出来的,账单行上的税列选上 3% 或 6%,金额不动,税额列自己算。

分录是系统代写的,顺带的效果是,应付账款挂进了那个测试供应商的对账单,账龄从这一单开始滚。上篇提过的应收应付账龄开箱即用,指的就是这里。

最后开 会计 → 报表 → 试算平衡表,选好期间。表里三列,期初、本期发生和期末,每一列的借方合计都等于贷方合计。这层平衡的意思是,期间内所有科目的借方发生额之和等于贷方发生额之和,分录录错一笔,平衡就破。

资产负债表和利润表也顺手点开看一眼,标题全中文。这两张的数字和试算平衡同一个底子,分录对了它们就跟着对,也是季度申报要用的原料。到此,底座的关键部件全部验过一遍。

  • 科目表 小企业会计准则,全中文
  • 分录 费用单自动生成借贷
  • 试算平衡 借方合计等于贷方合计

这次踩过的三个坑

坑没在这轮现踩,上篇靠 107 个 agent 和一次实跑提前挖出来了,这里按执行版重列。

  1. 容器不可变,自定义 app 必须进镜像。 ERPNext 时代装 erpnext_china 这类自定义 app,必须把它构建进自定义镜像再推 registry,运行中的容器里装 app,官方不支持,为了一个不用的插件养一条构建流水线,对一家刚成立的小公司纯属浪费。这条对 Odoo 同样成立,所以 ./addons 单独挂出来,模块丢进去重启容器就行,别进容器手改,容器一重启手改的东西全没,这就是不可变的直接代价。
  2. erpnext_china 停更 8 个多月。 最后一次实质提交停在 2026 年 1 月,社区还在拿它当"ERPNext 中国化方案"推荐,实际是个 CRM/HRM 本地化插件,跟财税没有关系,上篇进容器翻过它的模块清单。把记账底座押在它上面,等于押给一个已经不维护的个人项目。
  3. Odoo 第三方中国模块会注广告。 商店里免费的科目表模块捆绑 app_common 和 app_odoo_customize 两个依赖,装上会改官方设置,从 odooapp.cn 同步内容,顺手注入厂商广告,还有用户报告装完即崩。所以这次只装官方 l10n_cn,第三方模块一律先看 manifest 依赖再决定。

终态:一块底座,两块周边,全部开源自托管

底座立起来,周边按上篇的定位补上。现在整套系统落成三块。

干什么方案许可证
账、库存、邮件营销Odoo 18 社区版LGPL-3
自动化旅程MauticGPL
社媒排期PostizAGPL-3.0

三块各管各的,靠 webhook 和 API 对客户名单,加起来零订阅费,没有法律灰色地带。关键全在自托管,账务数据、客户名单和发布排期,都躺在自己服务器上。

拿 Mautic 和 Postiz 补周边,是上篇算过的账。Odoo 企业版里营销相关的专属就两块,Marketing Automation 和 Social Marketing,前者是拖拽式自动化(客户注册三天没下单,自动发邮件),后者是看板排期发多平台。社区版自带的邮件营销、UTM 追踪和活动报名这些都在,日常够用,不值得为那两块上企业版。Mautic 的 drip 序列和分群比企业版做得还细,Postiz 的多平台定时发布就是 Social Marketing 的开源平替。

有一条上篇反复讲、这里再钉一次的边界。数电票、金税四期这类深度合规接口,三家开源方案里一家都没有,换成哪款开源软件都补不上,只能走外部付费层。这是整个开源生态的天花板,不用归到 Odoo 头上。

回到开头那句话,现在的维护路线就几个词,官方镜像、官方模块,加上 Mautic 和 Postiz 两个一直在更新的开源项目,每一环后面都站着持续更新的社区。摊开看,这套组合最脆弱的环节仍然是人,季度申报、数电票回填,都得自己记着。

下一步

我们是小规模纳税人,按季度申报,账单、分录和试算平衡正好对应季报要的数。接下来排了四件事。数电票开票和接收走外部付费服务,季度申报前得先定下一家;账单表单上发票号、发票日期那些字段还空着,等外部的数电票服务回填;库存模块用起来,让进销存跟着订单动,不只当个总账用;Mautic 和 Postiz 各接一个真实场景,不为了装而装,装的东西必须有人用。

现在打开 http://localhost:8090,出来的是 Odoo 18 的中文登录页,账套里躺着第一张测试费用单和一份借贷持平的试算平衡表。ERPNext 那六只容器,连镜像带目录,都不在了。

本文为部署执行记录,不构成税务或法律意见,记账与申报合规请咨询持证税务师或代理记账机构。