通过Odoo销售源代码分析Odoo销售业务

        源代码版本:Odoo社区版V18

        源代码位置:odoo\addons\sale\models\sale_order.py

        sale_order.py这段代码是 Odoo 销售模块的核心,实现了从报价创建、确认、开票到支付的全流程管理,支持多公司、门户集成、财务对账和营销跟踪等功能。关键逻辑包括:

  • 状态机管理:订单状态(state)和发票状态(invoice_status)的自动更新。
  • 金额与税收计算:结合价格表、税收规则和支付条款动态计算。
  • 合作伙伴与地址管理:自动同步客户信息,支持多地址。
  • 支付与发票集成:通过payment.transactionaccount.move实现闭环。

         下面我们来分节讲解下sale_order.py中的重点代码。

一、代码结构与继承关系

模型定义
class SaleOrder(models.Model):
    _name = 'sale.order'
    _inherit = [
        'portal.mixin', 'product.catalog.mixin', 'mail.thread', 
        'mail.activity.mixin', 'utm.mixin'
    ]
    _description = "Sales Order"
    _order = 'date_order desc, id desc'
    _check_company_auto = True

  • 继承关系(_inherit)
    • portal.mixin:支持门户访问功能(如客户查看订单)。
    • mail.thread/mail.activity.mixin:支持消息线程和活动管理(如邮件通知、任务提醒)。
    • product.catalog.mixin:集成产品目录功能(如从目录添加产品到订单)。
    • utm.mixin:支持 UTM 跟踪(营销渠道分析)。

二、核心字段解析

状态与流程字段

我们来看看在代码中,定义的核心字段的意义。

state = fields.Selection(
    selection=SALE_ORDER_STATE,  # ['draft', 'sent', 'sale', 'cancel']
    string="Status",
    readonly=True, 
    tracking=3,
    default='draft'
)
invoice_status = fields.Selection(
    selection=INVOICE_STATUS,  # ['upselling', 'invoiced', 'to invoice', 'no']
    string="Invoice Status",
    compute='_compute_invoice_status',
    store=True
)

  • state:订单状态,从草稿(draft)到已确认(sale)、取消(cancel)等。
  • invoice_status:发票状态,根据行项目的开票情况计算,如 “待开票”(to invoice)、“已完全开票”(invoiced)。
财务与金额字段

amount_untaxed = fields.Monetary(compute='_compute_amounts')
amount_tax = fields.Monetary(compute='_compute_amounts')
amount_total = fields.Monetary(compute='_compute_amounts')
amount_paid = fields.Float(compute='_compute_amount_paid', compute_sudo=True)

  • 通过_compute_amounts方法计算订单金额(未税、税费、总额),结合税收规则和支付条款。
  • amount_paid汇总关联的支付交易金额,涉及支付模块的集成。
合作伙伴与地址字段
partner_id = fields.Many2one('res.partner', string="Customer", required=True)
partner_invoice_id = fields.Many2one(
    'res.partner', string="Invoice Address", 
    compute='_compute_partner_invoice_id', store=True
)
partner_shipping_id = fields.Many2one(
    'res.partner', string="Delivery Address", 
    compute='_compute_partner_shipping_id', store=True
)

  • 自动根据客户信息获取发票和送货地址,支持多公司和地址验证。

三、关键方法解析

计算方法(Compute Methods)
  1. _compute_invoice_status
    根据订单行的开票状态(invoice_status)计算整单的发票状态,逻辑包括:

    • 若存在 “待开票” 行,则订单状态为to invoice
    • 若所有行已开票,则状态为invoiced
    • 若存在追加销售机会(upselling),则状态为upselling
  2. _compute_amounts
    调用account.tax计算税收和总额,处理支付条款中的提前付款折扣(_add_base_lines_for_early_payment_discount)。

  3. _compute_pricelist_id
    根据客户的默认价格表(property_product_pricelist)自动设置订单价格表,支持多公司场景。

业务逻辑方法
  1. action_confirm
    确认订单,触发以下操作:

    • 验证订单行(如产品是否存在、分析账户分配)。
    • 发送确认邮件(通过_send_order_confirmation_mail)。
    • 根据配置自动锁定订单(action_lock)。
  2. _create_invoices
    生成发票,支持按分组(grouped)或按订单生成,处理预付款(Down Payment)和最终发票(final)。

  3. _get_portal_last_transaction
    获取门户用户的最后一次支付交易,用于支付状态跟踪和重定向。

约束与验证方法
@api.constrains('company_id', 'order_line')
def _check_order_line_company_id(self):
    # 验证订单行产品所属公司是否与订单公司兼容
    invalid_companies = order.order_line.product_id.company_id.filtered(
        lambda c: order.company_id not in c._accessible_branches()
    )
    if invalid_companies:
        raise ValidationError(...)

  • 确保订单行产品的公司与订单公司在权限范围内(多公司场景)。

四、门户与支付集成

门户访问

def _compute_access_url(self):
    super()._compute_access_url()
    for order in self:
        order.access_url = f'/my/orders/{order.id}'

  • 生成客户门户访问链接(如/my/orders/1),支持在线签名和支付。
支付流程
require_payment = fields.Boolean(compute='_compute_require_payment')
prepayment_percent = fields.Float(compute='_compute_prepayment_percent')
amount_paid = fields.Float(compute='_compute_amount_paid', compute_sudo=True)

  • require_payment:根据公司配置决定是否需要在线支付确认订单。
  • prepayment_percent:预付款比例(如支付 30% 确认订单)。
  • amount_paid:通过payment.transaction关联支付记录,支持授权(authorized)和完成(done)状态。

五、发票与财务集成

发票生成
def _prepare_invoice(self):
    # 准备发票数据,包括客户、货币、支付条款、交易记录等
    values = {
        'move_type': 'out_invoice',
        'partner_id': self.partner_invoice_id.id,
        'fiscal_position_id': self.fiscal_position_id.id,
        'transaction_ids': [Command.set(txs_to_be_linked.ids)],
        # ...其他字段
    }
    return values

  • 关联订单与发票,处理税收规则(fiscal_position_id)和支付交易(transaction_ids)。
开票状态跟踪
def _get_invoiced(self):
    # 通过订单行的发票行关联发票记录
    invoices = order.order_line.invoice_lines.move_id.filtered(
        lambda r: r.move_type in ('out_invoice', 'out_refund')
    )
    order.invoice_ids = invoices

  • 支持正向发票(out_invoice)和退款(out_refund)的跟踪。

六、多公司与权限管理

company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company)
@api.constrains('company_id', 'order_line')
def _check_order_line_company_id(self):
    # 验证产品公司是否属于订单公司的可访问分支
    if invalid_companies:
        raise ValidationError(...)

  • 强制订单必须属于某个公司,并验证产品公司与订单公司的兼容性(通过_accessible_branches)。

七、扩展与本地化支持

可扩展点
  • _get_order_edi_decoder:支持 EDI 文件解析(如 XML/CSV 订单导入)。
  • _filter_product_documents:根据订单状态(报价 / 销售订单)过滤附件。
本地化修改
  • 通过继承_get_name_portal_content_view_get_name_tax_totals_view调整门户页面和税收显示。

总结

理解sale_order.py代码中上面这些关键逻辑,才能有助于开发人员在Odoo上针对销售业务进行二次开发(如定制化状态、扩展支付方式、修改开票规则等),Odoo的二次开发要建立在现有业务逻辑的基础上才能真正发挥价值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值