
Odoo 15 送货单地址显示错误问题排查与分析
在odoo 15企业版中,部分用户可能遇到一个棘手的问题:打印送货单时,生成的pdf有时会错误地显示客户的账单地址,而非实际的送货地址。这种现象看似随机发生,给物流和客户沟通带来了困扰。本文将详细解析这一问题的排查过程、根本原因以及其对odoo数据结构的影响。
1. 问题描述与初步排查
问题的核心在于,当为销售订单生成发货单(Delivery Slip)时,PDF报告本应展示正确的送货地址,却意外地显示了与销售订单关联的客户主地址(通常是账单地址)。
初步排查通常从以下几个方面入手:
- 检查销售订单数据: 确认销售订单(sale.order)上的 partner_id(客户主地址)和 partner_shipping_id(送货地址)是否正确设置。
- 检查发货单数据: 确认与销售订单关联的发货单(stock.picking)上的 partner_id 是否指向正确的送货地址。
- Odoo Shell 验证: 利用Odoo Shell进行实时数据查询,验证相关记录的地址信息。
以下是一个Odoo Shell的验证示例,用于检查特定销售订单(S12345)及其相关发货单的地址数据:
# 假设self.env已在Odoo Shell中可用
# 查找销售订单
so = self.env['sale.order'].search([('name', '=', 'S12345')])
# 销售订单的客户主地址
print(f"SO Partner ID: {so.partner_id}")
# 销售订单的送货地址
print(f"SO Shipping Partner ID: {so.partner_shipping_id}")
# 查找与销售订单相关的发货单
pick = self.env['stock.picking'].search([('origin', '=', so.name)])
# 发货单的关联伙伴ID(通常应是送货地址)
print(f"Picking Partner ID: {pick.partner_id}")
# 发货单移动行中的伙伴ID(通常应是送货地址)
print(f"Picking Move Line Partner ID: {pick.move_lines[0].partner_id}")
# 打印发货单移动行伙伴的完整地址
print(f"Picking Move Line Partner Address:\n{pick.move_lines[0].partner_id._display_address()}")
# 检查是否应该打印送货地址(Odoo内部逻辑)
print(f"Should print delivery address: {pick.should_print_delivery_address()}")在上述示例中,尽管 pick.partner_id 和 pick.move_lines[0].partner_id 都正确指向了送货地址(YYYYYY),且 pick.should_print_delivery_address() 返回 True,但生成的PDF报告仍可能显示错误的地址。这表明问题可能出在报告模板的渲染逻辑上。
2. 深入分析报告模板:report_deliveryslip.xml
Odoo的送货单报告模板位于 stock 模块的 report_deliveryslip.xml 文件中。仔细分析该文件是解决问题的关键。
2.1 误导性的“发货地址”区域
在 report_deliveryslip.xml 中,存在一个名为 div_outgoing_address 的
Delivery Address:Warehouse Address:
这个区域虽然包含了 Delivery Address 的字样,但其作用是定义发货方的地址,即仓库或公司自己的地址,而不是收货方的送货地址。这是最初排查时容易产生混淆的地方。
2.2 真正的客户地址渲染逻辑
真正用于渲染客户地址(包括送货地址和账单地址)的逻辑位于另一个名为 information_block 的
Vendor Address: Customer Address:
:
请注意其中的关键行:
Customer Address:
以及后续用于显示地址的:
这里的问题在于,当 o.picking_type_id.code=='outgoing'(即发货单)时,Odoo会检查 partner != partner.commercial_partner_id 这个条件。如果这个条件为 True,它就会显示 Customer Address,并且最重要的是,它会显示 partner.commercial_partner_id 的地址,而不是 partner 本身的地址。
3. 根本原因:res.partner 的 commercial_partner_id 字段
为了理解 partner != partner.commercial_partner_id 为 True 的含义,我们需要检查 res.partner 模型中的 commercial_partner_id 字段定义及其计算方法。
3.1 commercial_partner_id 字段定义
commercial_partner_id 字段定义如下:
commercial_partner_id = fields.Many2one('res.partner', string='Commercial Entity',
compute='_compute_commercial_partner', recursive=True,
store=True, index=True)这是一个计算字段,它递归地存储并索引了“商业实体”伙伴。
3.2 _compute_commercial_partner 计算方法
commercial_partner_id 的计算逻辑在 _compute_commercial_partner 方法中:
@api.depends('is_company', 'parent_id.commercial_partner_id')
def _compute_commercial_partner(self):
for partner in self:
if partner.is_company or not partner.parent_id:
partner.commercial_partner_id = partner
else:
partner.commercial_partner_id = partner.parent_id.commercial_partner_id这个方法的逻辑是:
- 如果一个 res.partner 记录是公司(is_company 为 True)或者它没有父级(parent_id 为 False),那么它的 commercial_partner_id 就是它自己。
- 否则(如果它不是公司且有父级),它的 commercial_partner_id 将是其父级伙伴的 commercial_partner_id。
3.3 问题症结
结合报告模板和 commercial_partner_id 的逻辑,问题的原因浮出水面:
当送货地址的 res.partner 记录被设置为“个人”类型(is_company 为 False),并且它有一个父公司(parent_id 已设置)时,根据 _compute_commercial_partner 方法,该送货地址记录的 commercial_partner_id 将会指向其父公司的 commercial_partner_id(通常是父公司本身)。
在这种情况下,对于送货地址 partner 而言,partner != partner.commercial_partner_id 条件将为 True。报告模板因此会渲染 partner.commercial_partner_id 的地址,这实际上是父公司的地址,而不是送货地址本身的地址。父公司的地址通常被用作账单地址,这就解释了为什么送货单上会错误地显示客户的账单地址。
4. 结论与潜在解决方案
根本原因: 送货地址的 res.partner 记录被配置为“个人”类型,并且拥有一个父公司,导致其 commercial_partner_id 字段指向了父公司(通常是账单地址)。Odoo发货单报告在特定条件下会优先显示 commercial_partner_id 的地址,从而引发了地址显示错误。
挑战: 尽管根本原因已查明,但如何优雅地解决这一问题仍需进一步探讨。直接将送货地址的 res.partner 记录类型修改为“公司”可能会破坏现有的联系人层级结构,或者在未来引发其他潜在问题。同时,修改父公司的地址也可能影响到其他业务流程。
潜在解决方案方向:
数据结构优化: 重新评估和调整 res.partner 记录的层级和类型设置,确保送货地址的 commercial_partner_id 始终指向自身或一个真正代表送货点的实体。这可能需要对现有客户数据进行清理和迁移。
-
报告定制: 如果无法修改底层数据结构,可以考虑定制 report_deliveryslip.xml 报告模板。修改 div_incoming_address 部分的逻辑,使其在发货单(outgoing)类型下,无论 partner.commercial_partner_id 是否与 partner 相同,都强制显示 partner(即 o.partner_id 或 o.move_lines[0].partner_id)的地址,而不是 partner.commercial_partner_id 的地址。
例如,可以修改相关部分,直接引用发货单上的 partner_id:
Delivery Address:或者更准确地使用 o.move_lines[0].partner_id,因为它通常代表了具体的送货伙伴。
在实施任何解决方案之前,务必在测试环境中进行充分的验证,以确保不会引入新的问题。理解Odoo的伙伴(res.partner)模型及其 commercial_partner_id 字段的复杂性,对于解决这类报告显示问题至关重要。










