资讯动态

Odoo 16 实施应用:从零搭建小型企业 ERP 的落地路径

发布时间:2026/10/15 9:26:31 来源:尧图企业网站定制
简介这份资源是面向Odoo16初学者与实施人员的入门指南围绕开源智造理念下的GoodERPOdoo中国版展开帮助读者理解从基础资料配置到复杂生产、部署优化的完整实施路径。压缩包内仅含1个PDF文件大小约9.01MB内容以图文文档形式系统梳理实施要点便于随时查阅与对照操作。目前已有276人学习适合刚接触Odoo、希望快速上手企业ERP实施的技术人员与业务顾问。文档目录覆盖基础资料配置、采购与销售流程、仓库管理、现金处理、简单组装与委外生产、复杂工序与MRP分析、外贸多币种实现、PostgreSQL数据库管理以及Windows与Ubuntu环境下的安装、升级、Nginx性能优化、多核与预读调优等高级部署功能并附有权限设置、洞察模块、数据导入导出等实用技巧。读者可借此建立Odoo实施的整体框架掌握常见业务场景的处理思路与排错方法为后续项目落地打下基础。1. Odoo 16 实施应用从零搭出一套能跑的小型企业 ERP很多开发者第一次接触 Odoo 16 实施应用都是被一句“开源 ERP模块化装完就能用”带进来的。真到动手才发现装完只是开始能不能把采购、销售、库存、会计这几条线串起来跑通才是分水岭。我见过不少团队Demo 阶段演示得漂漂亮亮一上真实单据就翻车库存对不上、科目乱跳、权限一放开就出事。这篇笔记不讲空泛概念只讲一条能复现的落地路径——从环境准备、模块选型、基础数据配置到权限、自动化、常见坑全部按一线实施顺序展开。适合两类人刚接手 Odoo 16 实施、需要一份可抄作业清单的工程师以及已经装过但卡在“跑不通业务闭环”的开发者。读完你应该能独立搭出一套能录单、能过账、能查报表的最小可用系统。2. 环境与模块选型先把地基打对再谈功能Odoo 16 的部署方式直接决定后面实施顺不顺。选错部署方式后面调个字段都要重启半天模块装多了升级时数据库迁移能让你加班到天亮。这一章先把环境、版本、模块三件事定下来。2.1 部署方式怎么选源码、Docker 还是包管理常见做法有三种我一般按团队规模和维护能力来定。第一种是源码部署适合需要改核心代码或做深度二开的团队。用 Python 虚拟环境隔离依赖PostgreSQL 单独装。优点是可控缺点是升级麻烦每次大版本都要手动处理依赖冲突。第二种是 Docker Compose适合中小团队快速起环境。官方镜像把 Odoo 和 PostgreSQL 的依赖都封好了一条命令拉起。缺点是镜像内文件系统不持久自定义模块要挂载卷否则容器一删代码就没了。第三种是系统包管理安装适合只做配置、不改代码的实施场景。优点是省心缺点是 Python 依赖被系统锁死装第三方库容易和系统包打架。我一般推荐要二开就源码纯实施就 Docker别混着来。下面给一个 Docker Compose 的最小可用配置这是我在多个模拟项目里验证过的起手式。# docker-compose.yml version: 3.8 services: db: image: postgres:15 environment: POSTGRES_DB: odoo16 POSTGRES_USER: odoo POSTGRES_PASSWORD: odoo volumes: - db_data:/var/lib/postgresql/data restart: always odoo: image: odoo:16.0 depends_on: - db ports: - 8069:8069 environment: HOST: db USER: odoo PASSWORD: odoo volumes: # 自定义模块挂载容器重建不丢代码 - ./addons:/mnt/extra-addons # 配置文件挂载方便调参数 - ./odoo.conf:/etc/odoo/odoo.conf restart: always volumes: db_data:逻辑说明db 服务用 PostgreSQL 15这是 Odoo 16 官方支持的版本区间。odoo 服务依赖 db启动顺序有保证。关键在 volumes 两行./addons挂到/mnt/extra-addons这是 Odoo 默认扫描自定义模块的路径./odoo.conf挂配置文件改参数不用进容器。参数说明POSTGRES_PASSWORD别用默认值生产环境必须换。ports映射 8069 是 Odoo 默认端口如果本机被占用改成8070:8069。restart: always保证宿主机重启后服务自动拉起省得半夜被叫起来。配置文件odoo.conf我一般这么写[options] addons_path /mnt/extra-addons,/usr/lib/python3/dist-packages/odoo/addons data_dir /var/lib/odoo db_host db db_port 5432 db_user odoo db_password odoo admin_passwd change_this_master_password logfile /var/log/odoo/odoo.log log_level info workers 2addons_path顺序有讲究自定义模块放前面官方模块放后面同名模块优先加载自定义的。workers按 CPU 核数算一般核数*21小团队 2 到 4 够用。admin_passwd是数据库管理密码不是登录密码别搞混。2.2 模块选型先装哪几个后装哪几个Odoo 16 官方模块几百个全装是灾难。我一般按业务闭环来选先装“能录单、能过账、能查数”的最小集合跑通再加。核心必装base基础、web界面、mail消息和活动、contacts联系人。这四个是所有业务的底座。业务主线按场景选做贸易就装sale_management、purchase、stock、account做生产加mrp做零售加point_of_sale做项目加project、timesheet。这里有个血泪经验account模块一旦启用并录入凭证再想改会计科目表结构就非常麻烦。所以会计科目、税率、币种这些基础数据必须在录第一笔单之前定好。我见过有人先录了三个月单据才发现科目表建错最后只能导数据重建后悔药没处买。模块安装顺序也有讲究。先装base和依赖再装业务模块。如果一次勾选多个模块安装Odoo 会自动解析依赖但遇到循环依赖或版本冲突时日志里报错信息很隐晦。稳妥做法是分批装装完一批看日志确认无报错再装下一批。# 查看 Odoo 容器日志确认模块安装状态 docker compose logs -f odoo | grep -i module # 如果模块安装卡住进容器手动更新模块列表 docker compose exec odoo odoo -d odoo16 -u all --stop-after-init-u all是更新所有模块--stop-after-init表示更新完就退出不常驻。这个命令在模块升级后必跑否则界面看不到新字段。2.3 数据库初始化别在演示库上做实施新手最容易犯的错是在 Odoo 自带的演示数据上直接做实施。演示数据里有假客户、假产品、假凭证你配的真实数据混在里面查报表时根本分不清哪些是测试哪些是真实。正确做法新建数据库时取消勾选“加载演示数据”。如果已经建了带演示数据的库可以新建一个空库用 Odoo 的数据库管理界面复制结构或者直接重新初始化。# 创建不带演示数据的数据库 docker compose exec odoo odoo -d odoo16_prod --db_hostdb --db_userodoo --db_passwordodoo -i base --without-demoall --stop-after-init--without-demoall是关键参数表示不加载任何演示数据。-i base只装基础模块业务模块后面按需装。这样建出来的库是干净的适合做真实实施。数据库命名我一般用项目名_环境的格式比如odoo16_prod、odoo16_test。别用odoo这种默认名多项目并行时容易搞混。3. 基础数据配置把业务语言翻译成系统字段环境搭好、模块装完下一步是配基础数据。这一步做扎实后面录单才顺。基础数据包括公司信息、用户权限、会计科目、税率、产品、合作伙伴。这一章按配置顺序展开每步给可抄作业的操作。3.1 公司、币种与会计科目会计模块的地基公司信息在“设置 → 用户与公司 → 公司”里配。关键字段公司名称、地址、税号、币种。币种一旦有交易记录就不能改所以第一笔单之前必须确认。会计科目是account模块的核心。Odoo 16 会根据你选的国家和会计科目表模板自动生成一套科目。常见做法是先选一个最接近的模板再手动调整。我一般按这个顺序检查科目表第一确认应收、应付、库存、收入、成本这几个关键科目存在且类型正确。应收和应付必须是“应收账款”和“应付账款”类型库存必须是“当前资产”类型收入和成本必须是“收入”和“费用”类型。第二检查税率。默认税率可能不符合当地要求需要手动改。税率在“会计 → 配置 → 税”里配注意区分销售税和采购税。第三设置默认科目。在“会计 → 配置 → 设置”里把默认的应收、应付、收入、成本、库存科目指到刚才确认的科目上。这一步不做录单时系统会报“未找到默认科目”。# 用 Odoo shell 批量检查科目类型是否正确 # 进入 shelldocker compose exec odoo odoo shell -d odoo16_prod accounts env[account.account].search([]) for acc in accounts: # 打印科目代码、名称、类型 print(f{acc.code} | {acc.name} | {acc.account_type})逻辑说明这段代码在 Odoo shell 里跑遍历所有科目打印代码、名称、类型。account_type是 Odoo 16 的科目类型字段值如asset_receivable应收、liability_payable应付、asset_current当前资产、income收入、expense费用。参数说明search([])里的空列表表示不加过滤条件查全部。如果科目多可以加 domain比如[(account_type, , asset_receivable)]只查应收科目。3.2 用户、权限组与多公司别等出事再补权限Odoo 16 的权限体系分三层用户、权限组、记录规则。用户属于一个或多个权限组权限组决定能访问哪些菜单和模型记录规则决定能看哪些数据。常见做法是先建权限组再把用户挂上去。别直接给用户单独配权限后期维护会疯。权限组在“设置 → 用户与公司 → 组”里建。我一般按角色建组销售员、采购员、仓管、会计、经理。每个组勾选对应的菜单和模型权限。这里有个坑Odoo 16 的权限组有继承关系。比如“销售员”组可能继承了“内部用户”组的权限。你改子组权限时父组权限也会影响。所以建组时先看清楚继承链别把不该给的权限通过父组漏出去。多公司场景下记录规则要配“公司”字段。比如销售订单默认规则是只能看自己公司的单。如果跨公司调拨需要额外配规则。我一般建议多公司实施时先在一家公司跑通全流程再复制配置到其他公司别一上来就多公司并行。!-- 自定义权限组示例只读销售订单 -- record idgroup_sale_readonly modelres.groups field namename销售只读/field field namecategory_id refbase.module_category_sales/ field nameimplied_ids eval[(4, ref(sales_team.group_sale_salesman))]/ /record !-- 给该组配只读权限 -- record idrule_sale_readonly modelir.rule field namename销售订单只读规则/field field namemodel_id refsale.model_sale_order/ field namegroups eval[(4, ref(group_sale_readonly))]/ field nameperm_read evalTrue/ field nameperm_write evalFalse/ field nameperm_create evalFalse/ field nameperm_unlink evalFalse/ /record逻辑说明第一段建组implied_ids表示继承“销售员”组这样新组自动有销售员的基础权限。第二段建记录规则perm_read为 True其他为 False实现只读。参数说明eval[(4, ref(...))]是 Odoo 的 x2many 操作语法4 表示添加关联。model_id指向要控制的模型groups指向生效的组。记录规则不配 domain 时默认对所有记录生效如果要限制范围加domain_force字段。3.3 产品、合作伙伴与价格表录单前的最后准备产品和合作伙伴是录单时最常选的两类数据。产品要配名称、类型可库存/服务/消耗品、销售价、采购价、税、单位。合作伙伴要配名称、类型客户/供应商/两者、地址、税号、付款条件。价格表是销售模块的进阶功能。Odoo 16 的价格表支持基于数量、时间、客户组的阶梯价。我一般建议先不用价格表用产品上的默认销售价跑通流程等业务稳定再加价格表。价格表配复杂了调试时很难定位是哪条规则生效。产品类型选错是常见翻车点。可库存产品会走库存移动服务产品不走库存消耗品不走库存但计入成本。如果选错库存报表和成本核算都会错。我一般建议实物商品选“可库存”服务选“服务”办公用品这类选“消耗品”。# 批量创建产品示例 products [ {name: 产品A, type: product, list_price: 100.0, standard_price: 60.0}, {name: 服务B, type: service, list_price: 500.0, standard_price: 0.0}, ] for p in products: env[product.template].create(p) env.cr.commit() # 提交事务否则 shell 退出后数据丢失逻辑说明product.template是产品模板模型type字段区分产品类型list_price是销售价standard_price是成本价。循环创建后必须commit否则 shell 退出时事务回滚数据不落库。参数说明type可选值product可库存、service服务、consu消耗品。list_price和standard_price是浮点数单位是公司本位币。4. 业务流程跑通从报价到收款的最小闭环基础数据配好接下来跑业务闭环。这一章按“销售 → 采购 → 库存 → 会计”的顺序把一条完整链路走通。每步给操作路径和关键参数。4.1 销售流程报价单到销售订单的转换逻辑销售流程起点是报价单Quotation确认后变成销售订单Sales Order。Odoo 16 里这两个状态在同一个模型sale.order上用state字段区分。操作路径销售 → 订单 → 报价单 → 新建。填客户、产品、数量、价格保存后点“确认”。确认时系统做几件事检查库存、生成交货单、生成发票草稿如果产品是“按订单开票”。关键参数在“销售 → 配置 → 设置”里默认开票策略可选“按订单开票”或“按交货开票”。按订单开票是确认订单就生成发票按交货开票是发货后才生成。我一般建议实物商品用“按交货开票”服务用“按订单开票”。# 用代码创建并确认销售订单 partner env[res.partner].search([(name, , 某客户)], limit1) product env[product.product].search([(name, , 产品A)], limit1) order env[sale.order].create({ partner_id: partner.id, order_line: [(0, 0, { product_id: product.id, product_uom_qty: 10, price_unit: 100.0, })], }) order.action_confirm() # 确认订单 env.cr.commit()逻辑说明order_line是订单行(0, 0, {...})表示新建一条记录。action_confirm()是确认方法会触发库存检查、交货单生成等逻辑。参数说明product_uom_qty是数量price_unit是单价。如果产品有税税会自动带出。确认后order.state从draft变成sale。4.2 采购与库存入库单和出库单的联动采购流程采购 → 订单 → 新建。填供应商、产品、数量、价格确认后生成采购订单。收货时点“接收”生成入库单。入库单确认后库存增加。库存模块的核心概念是“移动”Stock Move。每次入库、出库、调拨都生成一条移动记录。移动有源位置和目标位置比如供应商 → 库存库存 → 客户。常见坑库存位置配错。Odoo 16 默认有“供应商”“客户”“库存”“生产”等位置。如果入库单的目标位置不是“库存”库存报表不会增加。我一般建议先检查“库存 → 配置 → 位置”里的位置层级确认默认位置正确。# 查看某产品的库存数量 product env[product.product].search([(name, , 产品A)], limit1) qty product.qty_available # 可用数量 print(f产品A 可用库存{qty}) # 查看库存移动记录 moves env[stock.move].search([(product_id, , product.id)], limit10) for m in moves: print(f{m.reference} | {m.location_id.name} - {m.location_dest_id.name} | {m.quantity})逻辑说明qty_available是计算字段实时算可用库存。stock.move查移动记录location_id是源位置location_dest_id是目标位置quantity是数量。参数说明qty_available受“预留”影响如果库存被其他订单预留可用数量会减少。要看实际在库数量用qty_on_hand。4.3 会计过账发票、付款与对账会计流程发票确认后生成会计分录付款后核销应收/应付。Odoo 16 的发票模型是account.move类型out_invoice是销售发票in_invoice是采购发票。操作路径会计 → 客户 → 发票 → 新建。填客户、产品、数量、价格确认后过账。过账时系统按产品上的收入和税科目生成分录。付款在发票上点“登记付款”填金额、日期、付款方式。确认后系统生成付款分录并核销发票。对账如果付款金额和发票金额不一致需要手动对账。在“会计 → 会计 → 对账”里选科目勾选要核销的行点“对账”。# 创建并过账销售发票 partner env[res.partner].search([(name, , 某客户)], limit1) invoice env[account.move].create({ move_type: out_invoice, partner_id: partner.id, invoice_line_ids: [(0, 0, { product_id: env[product.product].search([(name, , 产品A)], limit1).id, quantity: 10, price_unit: 100.0, })], }) invoice.action_post() # 过账 env.cr.commit()逻辑说明move_type指定发票类型invoice_line_ids是发票行。action_post()过账生成会计分录。参数说明move_type可选out_invoice销售、in_invoice采购、out_refund销售退款、in_refund采购退款。过账后invoice.state从draft变成posted。5. 避坑与排查实施中最容易翻车的 5 个点这一章列 5 个我在实施中反复遇到的坑每个按“现象 → 原因 → 解决”写。这些坑不挑场景新手老手都可能踩。5.1 模块升级后界面不显示新字段现象改了模块代码重启容器界面还是旧字段。原因Odoo 有缓存模块升级后需要更新模块列表并升级模块否则数据库里的模型定义没变。解决进容器跑odoo -d 数据库名 -u 模块名 --stop-after-init。如果还不行检查addons_path是否包含模块路径以及模块的__manifest__.py里版本号是否变了。版本号不变时 Odoo 可能跳过升级。5.2 库存数量对不上现象系统显示有库存实际没货或者反过来。原因常见有三种。一是库存位置配错入库入到了“供应商”位置而不是“库存”。二是单位换算错误采购用“箱”库存用“个”换算比例没配。三是并发操作两个人同时出库库存扣重了。解决先查stock.move记录看每笔移动的源位置和目标位置。再查产品单位换算在“库存 → 配置 → 单位”里确认。并发问题需要开 Odoo 的锁机制或者用SELECT FOR UPDATE在代码里加锁。5.3 会计科目默认值丢失现象录发票时报“未找到默认科目”。原因会计设置里的默认科目没配或者配的科目被归档了。解决去“会计 → 配置 → 设置”里重新指定默认科目。如果科目被归档取消归档或换一个。检查科目类型是否正确应收必须是asset_receivable应付必须是liability_payable。5.4 权限放开后数据泄露现象普通用户能看到其他销售员的订单。原因记录规则没配或者配了但domain_force写错。解决在“设置 → 技术 → 安全 → 记录规则”里检查。销售订单的默认规则是“只能看自己的”如果被改成“看全部”就会泄露。domain_force里用user.id限制比如[(user_id, , user.id)]。5.5 定时任务不执行现象配了自动动作到点没跑。原因Odoo 的定时任务依赖ir.cron模型需要 Odoo 服务常驻运行。如果服务停了任务不跑。另外任务的“下次执行时间”如果配错可能永远不触发。解决检查 Odoo 服务是否在跑docker compose ps看状态。在“设置 → 技术 → 自动化 → 定时动作”里看任务的“下次执行时间”和“执行频率”。手动点“运行”测试看日志报什么错。6. 进阶技巧用自动化动作和服务器动作减少手工操作跑通基础流程后下一步是减少手工操作。Odoo 16 的自动化动作Automated Action和服务器动作Server Action是两个利器。这一章给一个具体场景销售订单确认后自动发邮件通知仓管并自动创建拣货单。6.1 自动化动作的触发条件与执行逻辑自动化动作在“设置 → 技术 → 自动化 → 自动化动作”里配。关键字段模型、触发条件、执行动作。触发条件有三种创建时、更新时、创建和更新时。更新时可以加过滤条件比如“状态变成已确认时触发”。执行动作可以选“服务器动作”或“Python 代码”。我一般建议简单逻辑用服务器动作复杂逻辑用 Python 代码。Python 代码在 Odoo 的沙箱里跑不能直接访问文件系统但能访问env。# 自动化动作里的 Python 代码示例 # 触发条件sale.order 更新时state 变成 sale # 执行逻辑给仓管发消息并创建拣货单 # 获取当前订单 order env[sale.order].browse(record.id) # 找仓管用户 warehouse_user env[res.users].search([(name, , 某仓管)], limit1) # 给仓管发消息 order.message_post( bodyf订单 {order.name} 已确认请安排拣货。, partner_ids[warehouse_user.partner_id.id], ) # 创建拣货单如果还没创建 if not order.picking_ids: order._create_picking()逻辑说明record是自动化动作的内置变量指向触发的记录。message_post发消息partner_ids是接收人。_create_picking()是销售订单的方法生成拣货单。参数说明body是消息内容支持 HTML。partner_ids是合作伙伴 ID 列表不是用户 ID。_create_picking()是私有方法Odoo 16 里可用但升级时要注意兼容性。6.2 服务器动作的配置与调试服务器动作在“设置 → 技术 → 自动化 → 服务器动作”里配。关键字段模型、动作类型、执行代码。动作类型可选“执行 Python 代码”“创建记录”“更新记录”等。我一般用“执行 Python 代码”灵活度高。调试服务器动作时日志是关键。Odoo 的日志在odoo.conf里配的logfile路径。如果没配日志打到标准输出用docker compose logs -f odoo看。# 实时看 Odoo 日志过滤服务器动作相关 docker compose logs -f odoo | grep -i server action\|automation如果日志里报NameError或AttributeError说明代码里用了未定义的变量或方法。Odoo 16 的服务器动作沙箱里可用变量有env、model、record、records、time、datetime、dateutil、timezone。别用import沙箱不支持。6.3 验证自动化是否生效的三种方法配完自动化动作怎么确认它真的跑了我一般用三种方法交叉验证。第一种看日志。自动化动作执行时会打日志搜automation或动作名。第二种看消息。如果动作里发了消息去订单的“消息”标签页看有没有新消息。第三种看关联记录。如果动作创建了拣货单去“库存 → 操作 → 拣货”里看有没有新单。如果三种方法都看不到先检查自动化动作的“激活”开关是否打开。Odoo 16 的自动化动作默认是激活的但手动建的可能默认关闭。再检查触发条件比如“更新时”触发但你的操作是“创建”就不会触发。我自己的习惯是每配一个自动化动作先手动触发一次看日志和结果确认无误再交给业务用。别一次配一堆出了问题根本不知道是哪个动作导致的。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑