
在odoo 15企业版中,部分用户可能遇到一个棘手的问题:打印送货单时,生成的pdf有时会错误地显示客户的账单地址,而非实际的送货地址。这种现象看似随机发生,给物流和客户沟通带来了困扰。本文将详细解析这一问题的排查过程、根本原因以及其对odoo数据结构的影响。
问题的核心在于,当为销售订单生成发货单(Delivery Slip)时,PDF报告本应展示正确的送货地址,却意外地显示了与销售订单关联的客户主地址(通常是账单地址)。
初步排查通常从以下几个方面入手:
以下是一个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报告仍可能显示错误的地址。这表明问题可能出在报告模板的渲染逻辑上。
Odoo的送货单报告模板位于 stock 模块的 report_deliveryslip.xml 文件中。仔细分析该文件是解决问题的关键。
在 report_deliveryslip.xml 中,存在一个名为 div_outgoing_address 的 <t t-set="address"> 块,其代码片段如下:
<t t-set="address">
<div name="div_outgoing_address">
<div t-if="o.should_print_delivery_address()">
<span><strong>Delivery Address:</strong></span>
<div t-field="o.move_lines[0].partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
</div>
<div t-elif="o.picking_type_id.code != 'internal' and o.picking_type_id.warehouse_id.partner_id">
<span><strong>Warehouse Address:</strong></span>
<div t-field="o.picking_type_id.warehouse_id.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
</div>
</div>
</t>这个区域虽然包含了 Delivery Address 的字样,但其作用是定义发货方的地址,即仓库或公司自己的地址,而不是收货方的送货地址。这是最初排查时容易产生混淆的地方。
真正用于渲染客户地址(包括送货地址和账单地址)的逻辑位于另一个名为 information_block 的 <t t-set="information_block"> 块中,具体在 div_incoming_address 部分:
<t t-set="information_block">
<div class="row">
<div class="col-7" name="div_incoming_address">
<t t-set="show_partner" t-value="False" />
<div t-if="o.picking_type_id.code=='incoming' and partner">
<span><strong>Vendor Address:</strong></span>
<t t-set="show_partner" t-value="True" />
</div>
<div t-if="o.picking_type_id.code=='outgoing' and partner and partner != partner.commercial_partner_id">
<span><strong>Customer Address:</strong></span>
<t t-set="show_partner" t-value="True" />
</div>
<div t-if="show_partner" name="partner_header">
<div t-field="partner.commercial_partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
<p t-if="partner.sudo().commercial_partner_id.vat"><t t-esc="o.company_id.country_id.vat_label or 'Tax ID'"/>: <span t-field="partner.sudo().commercial_partner_id.vat"/></p>
</div>
</div>
</div>
</t>请注意其中的关键行:
<div t-if="o.picking_type_id.code=='outgoing' and partner and partner != partner.commercial_partner_id">
<span><strong>Customer Address:</strong></span>
<t t-set="show_partner" t-value="True" />
</div>以及后续用于显示地址的:
<div t-if="show_partner" name="partner_header">
<div t-field="partner.commercial_partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
<!-- ... 其他信息 ... -->
</div>这里的问题在于,当 o.picking_type_id.code=='outgoing'(即发货单)时,Odoo会检查 partner != partner.commercial_partner_id 这个条件。如果这个条件为 True,它就会显示 Customer Address,并且最重要的是,它会显示 partner.commercial_partner_id 的地址,而不是 partner 本身的地址。
为了理解 partner != partner.commercial_partner_id 为 True 的含义,我们需要检查 res.partner 模型中的 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)这是一个计算字段,它递归地存储并索引了“商业实体”伙伴。
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这个方法的逻辑是:
结合报告模板和 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 的地址,这实际上是父公司的地址,而不是送货地址本身的地址。父公司的地址通常被用作账单地址,这就解释了为什么送货单上会错误地显示客户的账单地址。
根本原因: 送货地址的 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:
<div t-if="o.picking_type_id.code=='outgoing' and o.partner_id" name="partner_header">
<span><strong>Delivery Address:</strong></span>
<div t-field="o.partner_id"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'/>
<!-- ... 其他信息 ... -->
</div>或者更准确地使用 o.move_lines[0].partner_id,因为它通常代表了具体的送货伙伴。
在实施任何解决方案之前,务必在测试环境中进行充分的验证,以确保不会引入新的问题。理解Odoo的伙伴(res.partner)模型及其 commercial_partner_id 字段的复杂性,对于解决这类报告显示问题至关重要。
以上就是Odoo 15 送货单地址显示错误问题深度解析与排查的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号