[IMP] sale_management: no client reload when adding optional product

Purpose
=======

Avoid a complete reload of the web client when adding a optional
product, which loses searches, breadcrumb, etc.

This is achieved by adding a non-stored 'is_present' computed field
which is computed depending on the presence of a SO line linked to the
option line - that way its is dynamically adapted when the option is
added to the SO. Since we wanted a clear feedback when the option was
added to the quote, green colouring and the disappearance of the 'add
to quote' button have been added.

Note that this field *cannot* be stored - since the line_id field on the
option bypasses the ORM (`set null` for the on_delete attribute), it
would not recompute properly upon deletion of the linked SO line.

Task#1950410

closes odoo/odoo#34929

Signed-off-by: Damien Bouvy (dbo) <dbo@odoo.com>
This commit is contained in:
Damien Bouvy
2019-07-17 05:12:39 +00:00
parent 81a2ad4e05
commit d629f90eb5
3 changed files with 25 additions and 7 deletions
+17 -2
View File
@@ -153,8 +153,12 @@ class SaleOrderOption(models.Model):
_description = "Sale Options"
_order = 'sequence, id'
is_present = fields.Boolean(string="Present on Quotation",
help="This field will be checked if the option line's product is "
"already present in the quotation.",
compute="_compute_is_present", search="_search_is_present")
order_id = fields.Many2one('sale.order', 'Sales Order Reference', ondelete='cascade', index=True)
line_id = fields.Many2one('sale.order.line', ondelete="set null")
line_id = fields.Many2one('sale.order.line', ondelete="set null", copy=False)
name = fields.Text('Description', required=True)
product_id = fields.Many2one('product.product', 'Product', required=True, domain=[('sale_ok', '=', True)])
price_unit = fields.Float('Unit Price', required=True, digits='Product Price')
@@ -163,6 +167,18 @@ class SaleOrderOption(models.Model):
quantity = fields.Float('Quantity', required=True, digits='Product UoS', default=1)
sequence = fields.Integer('Sequence', help="Gives the sequence order when displaying a list of optional products.")
@api.depends('line_id', 'order_id.order_line', 'product_id')
def _compute_is_present(self):
# NOTE: this field cannot be stored as the line_id is usually removed
# through cascade deletion, which means the compute would be false
for option in self:
option.is_present = bool(option.order_id.order_line.filtered(lambda l: l.product_id == option.product_id))
def _search_is_present(self, operator, value):
if (operator, value) in [('=', True), ('!=', False)]:
return [('line_id', '=', False)]
return [('line_id', '!=', False)]
@api.onchange('product_id', 'uom_id')
def _onchange_product_id(self):
if not self.product_id:
@@ -181,7 +197,6 @@ class SaleOrderOption(models.Model):
@api.multi
def button_add_to_order(self):
self.add_option_to_order()
return {'type': 'ir.actions.client', 'tag': 'reload'}
@api.multi
def add_option_to_order(self):
@@ -17,6 +17,7 @@
<field name="uom_id" groups="uom.group_uom"/>
<field name="price_unit"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="is_present" />
</group>
</form>
<kanban class="o_kanban_mobile">
@@ -24,6 +25,7 @@
<field name="quantity"/>
<field name="uom_id" groups="uom.group_uom"/>
<field name="price_unit"/>
<field name="is_present" />
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
@@ -36,7 +38,7 @@
</strong>
</div>
<div class="col-2">
<button name="button_add_to_order" class="btn btn-link oe_link fa fa-shopping-cart" string="Add to order lines" type="object"/>
<button name="button_add_to_order" class="btn btn-link oe_link fa fa-shopping-cart" title="Add to order lines" type="object" attrs="{'invisible': [('is_present', '=', True)]}"/>
</div>
</div>
<div class="row">
@@ -60,7 +62,7 @@
</t>
</templates>
</kanban>
<tree string="Sales Quotation Template Lines" editable="bottom">
<tree string="Sales Quotation Template Lines" editable="bottom" decoration-success="is_present == True">
<control>
<create name="add_product_control" string="Add a product"/>
</control>
@@ -71,7 +73,8 @@
<field name="uom_id" string="UoM" groups="uom.group_uom" optional="show"/>
<field name="price_unit"/>
<field name="discount" string="Disc.%" groups="sale.group_discount_per_so_line" optional="show"/>
<button name="button_add_to_order" class="oe_link" icon="fa-shopping-cart" string="Add to order lines" type="object"/>
<field name="is_present" invisible="1" />
<button name="button_add_to_order" class="oe_link" icon="fa-shopping-cart" title="Add to order lines" type="object" attrs="{'invisible': [('is_present', '=', True)]}"/>
</tree>
</field>
</page>
@@ -28,7 +28,7 @@
</xpath>
<xpath expr="//section[@id='signature']" position="after">
<t t-if="any([(not option.line_id) for option in sale_order.sale_order_option_ids])">
<t t-if="any([(not option.is_present) for option in sale_order.sale_order_option_ids])">
<section>
<h3>Options</h3>
<t t-set="display_discount" t-value="True in [option.discount > 0 for option in sale_order.sale_order_option_ids]"/>
@@ -44,7 +44,7 @@
</thead>
<tbody>
<tr t-foreach="sale_order.sale_order_option_ids" t-as="option">
<t t-if="not option.line_id">
<t t-if="not option.is_present">
<td>
<div t-field="option.name"/>
</td>