Odoo定制开发指南:模块扩展与二次开发实战
你是否在使用Odoo时遇到功能无法满足业务需求的困境?是否希望通过定制开发实现个性化业务流程?本文将带你从模块结构解析到实战开发,掌握Odoo二次开发的核心技能,让系统真正为你所用。读完本文,你将能够独立创建自定义模块、扩展现有功能,并理解Odoo开发的最佳实践。
Odoo模块开发基础
Odoo采用模块化架构设计,所有功能都通过模块(Addons)实现。每个模块是一个独立的功能单元,包含业务逻辑、数据模型、视图定义和静态资源。这种设计使系统具有极高的扩展性,用户可以根据需求灵活添加或移除功能。
模块目录结构
标准Odoo模块遵循统一的目录结构,以下是一个基础模块的典型组织:
custom_module/
├── __init__.py # Python包初始化文件
├── __manifest__.py # 模块元数据配置
├── models/ # 数据模型定义目录
│ └── models.py # 业务模型代码
├── views/ # 视图定义目录
│ └── views.xml # 界面视图配置
├── security/ # 权限控制目录
│ └── ir.model.access.csv # 模型访问权限配置
├── static/ # 静态资源目录
│ ├── src/ # 源代码目录
│ │ ├── js/ # JavaScript文件
│ │ └── css/ # 样式表文件
│ └── description/ # 模块描述资源
│ └── icon.png # 模块图标
└── data/ # 初始化数据目录
└── data.xml # 演示数据或配置数据
Odoo官方提供了丰富的模块示例,如addons/account/(会计模块)和addons/sale/(销售模块),这些模块可以作为学习和参考的最佳实践。
模块清单文件
__manifest__.py是模块的核心配置文件,包含模块的基本信息、依赖关系和资源声明。以下是一个基础的清单文件示例:
{
'name': '自定义销售扩展',
'version': '15.0.1.0.0',
'author': 'Your Company',
'website': 'https://www.yourcompany.com',
'category': 'Sales',
'depends': ['base', 'sale'], # 依赖模块
'data': [
'security/ir.model.access.csv',
'views/views.xml',
],
'demo': [
'data/demo.xml', # 演示数据
],
'installable': True,
'auto_install': False,
'application': True,
'icon': '/custom_module/static/description/icon.png',
}
这个文件定义了模块的名称、版本、依赖关系和所需的各种资源文件。其中depends字段非常重要,它指定了当前模块依赖的其他模块,确保在安装当前模块前,所有依赖模块都已正确安装。
数据模型扩展
Odoo使用ORM(对象关系映射)系统来定义和操作数据库模型。通过继承和扩展现有模型,我们可以在不修改原始代码的情况下添加新功能。
模型继承方式
Odoo提供了三种主要的模型继承方式,适用于不同的扩展场景:
- 经典继承:通过
_inherit属性完全继承一个模型,可添加新字段或重写方法 - 委托继承:通过
_inherits属性实现模型组合,允许一个模型"包含"另一个模型的字段 - 原型继承:创建一个全新模型,复制原始模型的所有字段和方法
以下是经典继承的示例,为销售订单添加一个"紧急程度"字段:
from odoo import models, fields
class SaleOrder(models.Model):
_inherit = 'sale.order' # 继承销售订单模型
urgency_level = fields.Selection([
('low', '低'),
('medium', '中'),
('high', '高'),
], string='紧急程度', default='medium')
def action_confirm(self):
# 重写确认方法,添加紧急订单处理逻辑
if self.urgency_level == 'high':
self.env['mail.thread'].message_post(
model='sale.order',
res_id=self.id,
body='此订单标记为紧急,请优先处理!'
)
return super(SaleOrder, self).action_confirm()
字段定义类型
Odoo提供了丰富的字段类型,满足不同业务需求:
| 字段类型 | 描述 | 示例 |
|---|---|---|
| Char | 单行文本 | name = fields.Char('名称', required=True) |
| Text | 多行文本 | description = fields.Text('描述') |
| Integer | 整数 | quantity = fields.Integer('数量', default=1) |
| Float | 浮点数 | price = fields.Float('价格', digits=(10, 2)) |
| Boolean | 布尔值 | is_active = fields.Boolean('激活', default=True) |
| Selection | 下拉选择 | state = fields.Selection([('draft','草稿'),('done','完成')]) |
| Many2one | 多对一关系 | partner_id = fields.Many2one('res.partner', '客户') |
| One2many | 一对多关系 | order_line = fields.One2many('sale.order.line', 'order_id', '订单行') |
| Many2many | 多对多关系 | tag_ids = fields.Many2many('crm.tag', '销售标签') |
| Date | 日期 | date_order = fields.Date('订单日期', default=fields.Date.today) |
| Datetime | 日期时间 | create_date = fields.Datetime('创建时间', readonly=True) |
你可以在addons/sale/models/sale.py中查看销售订单模型的完整定义,了解Odoo如何组织和实现复杂业务模型。
添加新字段示例
以下示例演示如何为合作伙伴(客户)模型添加新字段:
from odoo import models, fields
class ResPartner(models.Model):
_inherit = 'res.partner'
# 添加新字段
customer_rating = fields.Float('客户评分', default=0.0, digits=(3, 2))
is_vip = fields.Boolean('VIP客户', default=False)
credit_limit = fields.Float('信用额度', default=0.0)
# 重写现有方法
def write(self, vals):
# 在保存前执行自定义逻辑
if 'credit_limit' in vals and vals['credit_limit'] > 10000:
self.env['mail.thread'].message_post(
body=f'客户 {self.name} 信用额度调整为 {vals["credit_limit"]}',
message_type='notification'
)
return super(ResPartner, self).write(vals)
添加新字段后,需要在视图中显示它们才能在界面上看到和使用。
视图定制
Odoo的界面完全通过XML视图定义,通过继承和扩展这些视图,我们可以自定义界面布局和展示内容。
视图类型
Odoo提供多种视图类型,用于不同的交互场景:
- Tree View:列表视图,用于显示记录列表
- Form View:表单视图,用于创建和编辑单个记录
- Kanban View:看板视图,用于可视化工作流
- Pivot View:数据透视表,用于数据分析
- Graph View:图表视图,用于数据可视化
- Search View:搜索视图,定义搜索条件和过滤器
继承并修改视图
通过XML文件继承和修改现有视图,以下示例为销售订单表单添加新字段:
<odoo>
<record id="view_sale_order_inherit" model="ir.ui.view">
<field name="name">sale.order.inherit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<!-- 在现有字段后添加新字段 -->
<xpath expr="//field[@name='partner_id']" position="after">
<field name="urgency_level"/>
<field name="is_vip"/>
</xpath>
<!-- 添加新分组 -->
<xpath expr="//group[@name='sale_order']" position="after">
<group string="自定义信息">
<field name="customer_rating"/>
<field name="credit_limit"/>
</group>
</xpath>
</field>
</record>
</odoo>
XPath是Odoo视图继承的强大工具,它允许你精确定位到视图中的任何元素,并进行插入、替换或修改操作。常用的position属性值包括:after、before、inside、replace和attributes。
你可以在addons/sale/views/sale_views.xml中查看销售模块的视图定义,了解Odoo如何组织复杂视图结构。
创建新视图
除了修改现有视图,还可以创建全新的视图。以下示例创建一个自定义的合作伙伴看板视图:
<odoo>
<record id="view_res_partner_kanban_vip" model="ir.ui.view">
<field name="name">res.partner.kanban.vip</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<kanban default_group_by="is_vip">
<field name="name"/>
<field name="customer_rating"/>
<field name="is_vip"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="oe_kanban_box_content">
<h4><field name="name"/></h4>
<div t-if="record.is_vip.raw_value">
<span class="badge bg-success">VIP客户</span>
</div>
<div>评分: <field name="customer_rating"/></div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- 将新视图添加到动作中 -->
<record id="action_partner_form" model="ir.actions.act_window">
<field name="name">Contacts</field>
<field name="res_model">res.partner</field>
<field name="view_mode">kanban,tree,form,pivot,graph</field>
<field name="view_id" ref="view_res_partner_kanban_vip"/>
</record>
</odoo>
业务逻辑扩展
Odoo模块不仅可以扩展数据结构和界面,还可以通过重写和添加业务方法来扩展系统功能。
工作流与业务规则
Odoo使用状态字段和工作流方法来管理业务流程。以下示例扩展销售订单的工作流:
from odoo import models, fields, api
class SaleOrder(models.Model):
_inherit = 'sale.order'
# 添加新状态
state = fields.Selection(
selection_add=[('credit_check', '信用检查'), ('approved', '已批准')],
ondelete={'credit_check': 'set default', 'approved': 'set default'}
)
# 添加新方法
def action_credit_check(self):
"""执行信用检查"""
for order in self:
if order.partner_id.credit_limit > 0 and order.amount_total > order.partner_id.credit_limit:
# 信用额度不足,发送通知
self.env['mail.thread'].message_post(
body=f'销售订单 {order.name} 超出信用额度',
subject='信用检查失败',
message_type='notification'
)
order.state = 'draft'
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': '订单金额超出客户信用额度',
'type': 'danger',
'sticky': True,
}
}
else:
order.state = 'approved'
order.action_confirm()
return True
定时任务
使用Odoo的定时任务功能,可以定期执行自定义业务逻辑:
from odoo import models, fields, api
from odoo.exceptions import UserError
class ScheduledActions(models.Model):
_inherit = 'ir.cron'
@api.model
def scheduled_customer_rating_reminder(self):
"""发送客户评分提醒"""
# 查找30天前创建且未评分的订单
thirty_days_ago = fields.Date.today() - timedelta(days=30)
orders = self.env['sale.order'].search([
('date_order', '<=', thirty_days_ago),
('state', '=', 'sale'),
('partner_id.customer_rating', '=', 0)
])
for order in orders:
# 发送邮件提醒
template = self.env.ref('custom_module.email_template_rating_reminder')
template.send_mail(order.id, force_send=True)
return True
然后在XML中定义定时任务:
<record id="ir_cron_customer_rating_reminder" model="ir.cron">
<field name="name">客户评分提醒</field>
<field name="model_id" ref="model_sale_order"/>
<field name="state">code</field>
<field name="code">model.scheduled_customer_rating_reminder()</field>
<field name="interval_number">7</field>
<field name="interval_type">day</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="user_id" ref="base.user_admin"/>
</record>
报表定制
Odoo允许通过自定义报表模板来生成各种格式的业务文档,如报价单、发票和报表。
QWeb报表
Odoo使用QWeb模板引擎生成HTML和PDF报表。以下示例创建一个自定义销售报表:
<odoo>
<template id="report_sale_order_custom">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.external_layout">
<div class="header">
<h1>自定义销售订单 #<t t-esc="doc.name"/></h1>
<div class="date"><t t-esc="doc.date_order"/></div>
</div>
<div class="client_info">
<h3>客户信息</h3>
<p><t t-esc="doc.partner_id.name"/></p>
<p><t t-esc="doc.partner_id.street"/></p>
<p><t t-esc="doc.partner_id.city"/></p>
</div>
<table class="table table-condensed">
<thead>
<tr>
<th>产品</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<t t-foreach="doc.order_line" t-as="line">
<tr>
<td><t t-esc="line.product_id.name"/></td>
<td><t t-esc="line.product_uom_qty"/></td>
<td><t t-esc="line.price_unit"/></td>
<td><t t-esc="line.price_subtotal"/></td>
</tr>
</t>
</tbody>
</table>
<div class="total">
<p>总计: <t t-esc="doc.amount_total"/></p>
<t t-if="doc.urgency_level == 'high'">
<p class="urgent">紧急订单 - 请优先处理</p>
</t>
</div>
</t>
</t>
</t>
</template>
<!-- 创建报表动作 -->
<record id="action_report_sale_order_custom" model="ir.actions.report">
<field name="name">自定义销售订单报表</field>
<field name="model">sale.order</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">custom_module.report_sale_order_custom</field>
<field name="report_file">custom_module.report_sale_order_custom</field>
<field name="print_report_name">'SO-%s' % (object.name)</field>
</record>
</odoo>
你可以参考addons/sale/report/sale_report.xml中的销售报表定义,了解Odoo如何设计复杂报表。
模块部署与维护
开发完成后,需要将自定义模块部署到生产环境,并进行持续维护和更新。
模块打包与安装
Odoo模块可以打包成zip文件进行分发和安装:
- 将模块目录压缩为zip文件
- 通过Odoo应用商店界面上传并安装
- 或使用命令行安装:
./odoo-bin -i custom_module -d database_name
版本控制与升级
对于模块的版本管理,建议遵循以下实践:
- 使用语义化版本号(主版本.次版本.修订版本)
- 在
__manifest__.py中明确声明版本号 - 数据库迁移使用
odoo-bin -u custom_module -d database_name命令 - 重大更新前备份数据库
Odoo提供了数据库迁移工具,可以帮助你处理版本升级过程中的数据结构变更。
调试与日志
开发和维护过程中,有效的调试和日志记录非常重要:
# 使用Odoo日志系统
import logging
_logger = logging.getLogger(__name__)
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
_logger.info('确认销售订单: %s', self.name)
try:
# 业务逻辑
result = super(SaleOrder, self).action_confirm()
_logger.debug('订单 %s 确认成功', self.name)
return result
except Exception as e:
_logger.error('订单 %s 确认失败: %s', self.name, str(e))
raise
你可以在Odoo配置文件中设置日志级别和日志文件路径,以便更好地跟踪和调试系统问题。
最佳实践与常见问题
开发最佳实践
- 遵循Odoo编码规范:保持代码风格一致,使用有意义的变量和方法名
- 模块化设计:功能分解为独立模块,降低复杂度
- 避免修改核心代码:始终通过继承扩展功能,而非直接修改Odoo核心模块
- 编写单元测试:为自定义功能编写测试用例,确保稳定性
- 文档化代码:为关键方法和复杂逻辑添加文档字符串
Odoo官方提供了详细的开发文档,是学习最佳实践的重要资源。
常见问题与解决方案
-
模块未显示在应用列表:
- 检查
__manifest__.py是否正确配置 - 确保模块目录放置在Odoo能识别的路径中
- 检查是否有语法错误或依赖问题
- 检查
-
继承不生效:
- 确认
_inherit属性设置正确 - 检查依赖模块是否已安装
- 确保没有命名冲突
- 确认
-
视图未更新:
- 执行
odoo-bin -u module_name -d db_name更新模块 - 检查视图继承的XPath表达式是否正确
- 清除浏览器缓存或使用隐私浏览模式测试
- 执行
-
权限问题:
- 检查
ir.model.access.csv文件中的权限配置 - 确保用户组设置正确
- 使用管理员账户测试功能是否正常
- 检查
通过遵循这些最佳实践和解决方案,你可以避免大部分常见的开发问题,提高定制开发的效率和质量。
总结与后续学习
通过本文,你已经了解了Odoo模块开发的基础知识,包括模块结构、数据模型扩展、视图定制、业务逻辑实现和报表设计。这些技能足以让你开始创建简单的自定义模块,满足特定业务需求。
进阶学习资源
- Odoo官方文档:https://www.odoo.com/documentation
- Odoo社区教程:addons/website_slides/
- Odoo开发论坛:https://www.odoo.com/forum
- Odoo企业版API:提供更多高级功能和支持
推荐实践项目
为了巩固所学知识,建议尝试以下实践项目:
- 创建一个简单的任务管理模块,包含任务创建、分配和跟踪功能
- 扩展销售模块,添加客户满意度调查功能
- 开发一个自定义报表,展示销售趋势和预测
Odoo的模块化架构和强大的扩展能力,使得几乎所有业务需求都可以通过定制开发来实现。随着经验的积累,你将能够构建更复杂、更强大的业务系统,充分发挥Odoo的潜力。
最后,记住Odoo是一个活跃的开源社区,定期更新和改进。保持关注最新版本和功能,持续学习和实践,将帮助你成为一名高效的Odoo开发者。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



