Odoo如何根据选择器切换实现固定金额/百分比折扣适配显示
Odoo 折扣模式切换控件适配方案
问题背景
Odoo开发中需要实现双模式折扣功能,通过选择器切换固定货币金额折扣、百分比折扣两种计算与展示逻辑。
原有后端计算代码(.py)
@api.depends('discount_type', 'discount_rate', 'amount_total') def _compute_amount_discount(self): for act in self: amount_untaxed = amount_discount = discount_rate = 0.0 for line in act.reconciliation_act_line_ids: amount_untaxed += line.price_subtotal if self.discount_type == 'percent': amount_discount = amount_untaxed * self.discount_rate / 100 else: amount_discount = self.discount_rate act.update({ 'amount_total_with_discount': amount_untaxed - amount_discount, 'amount_discount': - amount_discount, 'amount_total': amount_untaxed, })
原有前端视图代码(.xml)
<div class="container"> <div class="row justify-content-between"> <div class="col-4"> <group name="discount" attrs="{'invisible': [('apply_discount', '=', False)]}"> <field name="discount_type"/> <field name="discount_rate"/> </group> </div> <div class="col-4"> <group> <field name="amount_total" nolabel="0" widget='monetary' options="{'currency_field': 'currency_id'}"/> <field name="amount_discount" widget='monetary' attrs="{'invisible': [('apply_discount', '=', False)]}"/> <field name="amount_total_with_discount" widget='monetary' attrs="{'invisible': [('apply_discount', '=', False)]}"/> <field name="amount_total_executors" nolabel="0" widget='monetary' options="{'currency_field': 'currency_id'}"/> </group> </div> </div> </div>
异常表现
固定金额折扣模式运行正常,切换到百分比折扣模式时,discount_rate字段绑定的monetary货币控件会强制显示货币符号,无法适配百分比数值的展示要求,异常效果如下:

修复方案
不要尝试用单个字段动态切换widget,Odoo原生视图框架不支持运行时直接修改字段绑定的控件类型,最稳定的实现方式是拆分字段+动态显隐:
- 模型层调整:新增两个独立字段分别承载两类折扣值,同步修改计算逻辑
# 新增字段定义 discount_fixed = fields.Monetary(string='折扣金额', currency_field='currency_id') discount_percent = fields.Float(string='折扣比例(%)') # 调整后的计算方法 @api.depends('discount_type', 'discount_fixed', 'discount_percent', 'reconciliation_act_line_ids.price_subtotal') def _compute_amount_discount(self): for act in self: amount_untaxed = sum(act.reconciliation_act_line_ids.mapped('price_subtotal')) if act.discount_type == 'percent': amount_discount = amount_untaxed * act.discount_percent / 100 else: amount_discount = act.discount_fixed act.write({ 'amount_total': amount_untaxed, 'amount_discount': -amount_discount, 'amount_total_with_discount': amount_untaxed - amount_discount, }) - 视图层调整:替换原有单个
discount_rate字段,通过attrs属性根据折扣类型控制两个字段的显隐,分别绑定对应适配的控件<group name="discount" attrs="{'invisible': [('apply_discount', '=', False)]}"> <field name="discount_type"/> <!-- 固定金额模式:显示货币控件 --> <field name="discount_fixed" widget="monetary" options="{'currency_field': 'currency_id'}" attrs="{'required': [('discount_type', '=', 'fixed')], 'invisible': [('discount_type', '!=', 'fixed')]}"/> <!-- 百分比模式:显示浮点数百分比控件 --> <field name="discount_percent" widget="float" options="{'type': 'percent'}" attrs="{'required': [('discount_type', '=', 'percent')], 'invisible': [('discount_type', '!=', 'percent')]}"/> </group>
该方案无需编写自定义JS组件,兼容Odoo所有主流版本(12.0~17.0),表单加载、编辑、保存时不会出现控件渲染异常,是Odoo生态内同类需求的标准实现方式。
内容的提问来源于stack exchange,提问作者user14213623
相关产品推荐
相关产品推荐

