summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorRomainLibert <rli@odoo.com>2019-08-07 12:51:03 +0000
committerYannick Tivisse <yti@odoo.com>2019-10-18 12:37:52 +0000
commit69319bba3e9ba20d14f6b30bd555f6d2d7478f0a (patch)
tree311c8572d0ab8ea6f4278321ac455ca5dc8b908c /addons
parentace7f03bab4bdb5c56e027b810141d20e5b08430 (diff)
[IMP] lunch: get rid of the lunch_order_temp wizard
Replace lunch.order.temp wizard by lunch.order (fp request) closes odoo/odoo#35585 Taskid: 2048667 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Diffstat (limited to 'addons')
-rw-r--r--addons/lunch/__init__.py1
-rw-r--r--addons/lunch/__manifest__.py1
-rw-r--r--addons/lunch/models/lunch_order.py131
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_controller.js22
-rw-r--r--addons/lunch/static/tests/lunch_kanban_tests.js3
-rw-r--r--addons/lunch/views/lunch_orders_views.xml73
-rw-r--r--addons/lunch/wizard/__init__.py4
-rw-r--r--addons/lunch/wizard/lunch_line_wizard.py138
-rw-r--r--addons/lunch/wizard/lunch_line_wizard_views.xml77
9 files changed, 220 insertions, 230 deletions
diff --git a/addons/lunch/__init__.py b/addons/lunch/__init__.py
index 4e9b019bd7a..5905766d867 100644
--- a/addons/lunch/__init__.py
+++ b/addons/lunch/__init__.py
@@ -4,4 +4,3 @@
from . import controllers
from . import models
from . import report
-from . import wizard
diff --git a/addons/lunch/__manifest__.py b/addons/lunch/__manifest__.py
index 7dc5b687fd3..0bf568e7550 100644
--- a/addons/lunch/__manifest__.py
+++ b/addons/lunch/__manifest__.py
@@ -26,7 +26,6 @@ If you want to save your employees' time and avoid them to always have coins in
'security/ir.model.access.csv',
'report/lunch_cashmove_report_views.xml',
'views/lunch_templates.xml',
- 'wizard/lunch_line_wizard_views.xml',
'views/lunch_alert_views.xml',
'views/lunch_cashmove_views.xml',
'views/lunch_location_views.xml',
diff --git a/addons/lunch/models/lunch_order.py b/addons/lunch/models/lunch_order.py
index 640e730d528..ccd8af09e41 100644
--- a/addons/lunch/models/lunch_order.py
+++ b/addons/lunch/models/lunch_order.py
@@ -41,10 +41,130 @@ class LunchOrder(models.Model):
display_toppings = fields.Text('Extras', compute='_compute_display_toppings', store=True)
+ product_description = fields.Text('Description', related='product_id.description')
+ topping_label_1 = fields.Char(related='product_id.category_id.topping_label_1')
+ topping_label_2 = fields.Char(related='product_id.category_id.topping_label_2')
+ topping_label_3 = fields.Char(related='product_id.category_id.topping_label_3')
+ topping_quantity_1 = fields.Selection(related='product_id.category_id.topping_quantity_1')
+ topping_quantity_2 = fields.Selection(related='product_id.category_id.topping_quantity_2')
+ topping_quantity_3 = fields.Selection(related='product_id.category_id.topping_quantity_3')
+ image_1920 = fields.Image(related='product_id.image_1920')
+ image_128 = fields.Image(related='product_id.image_128')
+
+ available_toppings_1 = fields.Boolean(help='Are extras available for this product', compute='_compute_available_toppings')
+ available_toppings_2 = fields.Boolean(help='Are extras available for this product', compute='_compute_available_toppings')
+ available_toppings_3 = fields.Boolean(help='Are extras available for this product', compute='_compute_available_toppings')
+
+ # YTI TODO Try to remove this brol
+ edit = fields.Boolean('Currently in edit mode', compute='_compute_edit')
+
+ def _compute_edit(self):
+ value = bool(self._context.get('active_id', False))
+ for line in self:
+ line.edit = value
+
+ @api.depends('product_id')
+ def _compute_available_toppings(self):
+ for line in self:
+ line.available_toppings_1 = bool(line.env['lunch.topping'].search_count([('category_id', '=', line.category_id.id), ('topping_category', '=', 1)]))
+ line.available_toppings_2 = bool(line.env['lunch.topping'].search_count([('category_id', '=', line.category_id.id), ('topping_category', '=', 2)]))
+ line.available_toppings_3 = bool(line.env['lunch.topping'].search_count([('category_id', '=', line.category_id.id), ('topping_category', '=', 3)]))
+
def init(self):
self._cr.execute("""CREATE INDEX IF NOT EXISTS lunch_order_user_product_date ON %s (user_id, product_id, date)"""
% self._table)
+
+ def _extract_toppings(self, values):
+ """
+ If called in api.multi then it will pop topping_ids_1,2,3 from values
+ """
+ if self.ids:
+ # TODO This is not taking into account all the toppings for each individual order, this is usually not a problem
+ # since in the interface you usually don't update more than one order at a time but this is a bug nonetheless
+ topping_1 = values.pop('topping_ids_1')[0][2] if 'topping_ids_1' in values else self[:1].topping_ids_1.ids
+ topping_2 = values.pop('topping_ids_2')[0][2] if 'topping_ids_2' in values else self[:1].topping_ids_2.ids
+ topping_3 = values.pop('topping_ids_3')[0][2] if 'topping_ids_3' in values else self[:1].topping_ids_3.ids
+ else:
+ topping_1 = values['topping_ids_1'][0][2] if 'topping_ids_1' in values else []
+ topping_2 = values['topping_ids_2'][0][2] if 'topping_ids_2' in values else []
+ topping_3 = values['topping_ids_3'][0][2] if 'topping_ids_3' in values else []
+
+ return topping_1 + topping_2 + topping_3
+
+ @api.constrains('topping_ids_1', 'topping_ids_2', 'topping_ids_3')
+ def _check_topping_quantity(self):
+ errors = {
+ '1_more': _('You should order at least one %s'),
+ '1': _('You have to order one and only one %s'),
+ }
+ for line in self:
+ for index in range(1, 4):
+ availability = line['available_toppings_%s' % index]
+ quantity = line['topping_quantity_%s' % index]
+ toppings = line['topping_ids_%s' % index].filtered(lambda x: x.topping_category == index)
+ label = line['topping_label_%s' % index]
+
+ if availability and quantity != '0_more':
+ check = bool(len(toppings) == 1 if quantity == '1' else toppings)
+ if not check:
+ raise ValidationError(errors[quantity] % label)
+
+ @api.model
+ def create(self, values):
+ lines = self._find_matching_lines({
+ **values,
+ 'toppings': self._extract_toppings(values),
+ })
+ if lines:
+ # YTI FIXME This will update multiple lines in the case there are multiple
+ # matching lines which should not happen through the interface
+ lines.update_quantity(1)
+ return lines[:1]
+ return super().create(values)
+
+ def write(self, values):
+ merge_needed = 'note' in values or 'topping_ids_1' in values or 'topping_ids_2' in values or 'topping_ids_3' in values
+
+ # Only write on topping_ids_1 because they all share the same table
+ # and we don't want to remove all the records
+ # _extract_toppings will pop topping_ids_1, topping_ids_2 and topping_ids_3 from values
+ # This also forces us to invalidate the cache for topping_ids_2 and topping_ids_3 that
+ # could have changed through topping_ids_1 without the cache knowing about it
+ toppings = self._extract_toppings(values)
+ values['topping_ids_1'] = [(6, 0, toppings)]
+ self.invalidate_cache(['topping_ids_2', 'topping_ids_3'])
+
+ if merge_needed:
+ lines_to_deactivate = self.env['lunch.order']
+ for line in self:
+ matching_lines = self._find_matching_lines({
+ 'user_id': values.get('user_id', line.user_id.id),
+ 'product_id': values.get('product_id', line.product_id.id),
+ 'note': values.get('note', line.note or False),
+ 'toppings': toppings,
+ })
+ if matching_lines:
+ lines_to_deactivate |= line
+ # YTI TODO Try to batch it, be careful there might be multiple matching
+ # lines for the same order hence quantity should not always be
+ # line.quantity, but rather a sum
+ matching_lines.update_quantity(line.quantity)
+ lines_to_deactivate.write({'active': False})
+ return super(LunchOrder, self - lines_to_deactivate).write(values)
+ return super().write(values)
+
+ @api.model
+ def _find_matching_lines(self, values):
+ domain = [
+ ('user_id', '=', values.get('user_id', self.default_get(['user_id'])['user_id'])),
+ ('product_id', '=', values.get('product_id', False)),
+ ('date', '=', fields.Date.today()),
+ ('note', '=', values.get('note', False)),
+ ]
+ toppings = values.get('toppings', [])
+ return self.search(domain).filtered(lambda line: (line.topping_ids_1 | line.topping_ids_2 | line.topping_ids_3).ids == toppings)
+
@api.depends('topping_ids_1', 'topping_ids_2', 'topping_ids_3', 'product_id', 'quantity')
def _compute_total_price(self):
for line in self:
@@ -65,12 +185,19 @@ class LunchOrder(models.Model):
line.quantity += increment
self._check_wallet()
+ def add_to_cart(self):
+ """
+ This method currently does nothing, we currently need it in order to
+ be able to reuse this model in place of a wizard
+ """
+ # YTI FIXME: Find a way to drop this.
+ return True
+
def _check_wallet(self):
self.flush()
for line in self:
if self.env['lunch.cashmove'].get_wallet_balance(line.user_id) < 0:
- raise ValidationError(_('Your wallet does not contain enough money to order that.'
- 'To add some money to your wallet, please contact your lunch manager.'))
+ raise ValidationError(_('Your wallet does not contain enough money to order that. To add some money to your wallet, please contact your lunch manager.'))
def action_order(self):
self.write({'state': 'ordered'})
diff --git a/addons/lunch/static/src/js/lunch_kanban_controller.js b/addons/lunch/static/src/js/lunch_kanban_controller.js
index 5a9de5ac8db..7713c192bb3 100644
--- a/addons/lunch/static/src/js/lunch_kanban_controller.js
+++ b/addons/lunch/static/src/js/lunch_kanban_controller.js
@@ -174,14 +174,26 @@ var LunchKanbanController = KanbanController.extend({
},
};
- this.do_action({
- res_model: 'lunch.order.temp',
- name: 'Configure Your Order',
+ // YTI TODO Maybe don't always pass the default_product_id
+ var action = {
+ res_model: 'lunch.order',
+ name: _t('Configure Your Order'),
type: 'ir.actions.act_window',
views: [[false, 'form']],
target: 'new',
- context: _.extend(ctx, {default_product_id: ev.data.productId, line_id: ev.data.lineId || false}),
- }, options);
+ context: _.extend(ctx, {default_product_id: ev.data.productId}),
+ };
+
+ if (ev.data.lineId) {
+ action = _.extend(action, {
+ res_id: ev.data.lineId,
+ context: _.extend(action.context, {
+ active_id: ev.data.lineId,
+ }),
+ });
+ }
+
+ this.do_action(action, options);
},
_onOrderNow: function (ev) {
var self = this;
diff --git a/addons/lunch/static/tests/lunch_kanban_tests.js b/addons/lunch/static/tests/lunch_kanban_tests.js
index b16dee08915..6dfb524ef9e 100644
--- a/addons/lunch/static/tests/lunch_kanban_tests.js
+++ b/addons/lunch/static/tests/lunch_kanban_tests.js
@@ -691,13 +691,12 @@ QUnit.module('LunchKanbanView', {
do_action: function (ev) {
assert.deepEqual(ev.data.action, {
name: "Configure Your Order",
- res_model: 'lunch.order.temp',
+ res_model: 'lunch.order',
type: 'ir.actions.act_window',
views: [[false, 'form']],
target: 'new',
context: {
default_product_id: 1,
- line_id: false,
},
},
"should open the wizard");
diff --git a/addons/lunch/views/lunch_orders_views.xml b/addons/lunch/views/lunch_orders_views.xml
index d291b60ed32..cdfcdb3a464 100644
--- a/addons/lunch/views/lunch_orders_views.xml
+++ b/addons/lunch/views/lunch_orders_views.xml
@@ -175,4 +175,77 @@
</field>
</record>
+ <record id="lunch_order_view_form" model="ir.ui.view">
+ <field name="name">lunch.order.view.form</field>
+ <field name="model">lunch.order</field>
+ <field name="arch" type="xml">
+ <form>
+ <field name="currency_id" invisible="1"/>
+ <field name="quantity" invisible="1"/>
+ <field name="product_id" invisible="1"/>
+ <field name="category_id" invisible="1"/>
+ <field name="available_toppings_1" invisible="1"/>
+ <field name="available_toppings_2" invisible="1"/>
+ <field name="available_toppings_3" invisible="1"/>
+ <field name="edit" invisible="1"/>
+ <div class="d-flex">
+ <div class="flex-grow-0 pr-5">
+ <field name="image_1920" widget="image" class="o_lunch_image" options="{'image_preview': 'image_128'}"/>
+ </div>
+ <div class="flex-grow-1 pr-5">
+ <h2><field name="name"/></h2>
+ <h3 class="pt-3"><field name="price" widget="monetary"/></h3>
+ </div>
+ </div>
+ <div class="o_lunch_wizard">
+ <div class="row">
+ <div class="col-2">
+ <field name="topping_label_1" nolabel="1" attrs="{'invisible': [('available_toppings_1', '=', False)]}" class="font-weight-bold"/>
+ </div>
+ <div class="col-10">
+ <field name="topping_ids_1" attrs="{'invisible': [('available_toppings_1', '=', False)]}" widget="many2many_checkboxes" nolabel="1"/>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-2">
+ <field name="topping_label_2" nolabel="1" attrs="{'invisible': [('available_toppings_2', '=', False)]}" class="font-weight-bold"/>
+ </div>
+ <div class="col-10">
+ <field name="topping_ids_2" attrs="{'invisible': [('available_toppings_2', '=', False)]}" widget="many2many_checkboxes" nolabel="1"/>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-2">
+ <field name="topping_label_3" nolabel="1" attrs="{'invisible': [('available_toppings_3', '=', False)]}" class="font-weight-bold"/>
+ </div>
+ <div class="col-10">
+ <field name="topping_ids_3" attrs="{'invisible': [('available_toppings_3', '=', False)]}" widget="many2many_checkboxes" nolabel="1"/>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-2">
+ <label for="product_description" class="font-weight-bold"/>
+ </div>
+ <div class="col-10">
+ <field name="product_description" nolabel="1"/>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-2">
+ <label for="note" class="font-weight-bold" />
+ </div>
+ <div class="col-10">
+ <field name="note" nolabel="1" placeholder="Informations, allergens, ..." />
+ </div>
+ </div>
+ </div>
+ <footer>
+ <button string="Save" special="save" class="oe_highlight" attrs="{'invisible': [('edit', '=', False)]}"/>
+ <button string="Add To Cart" name="add_to_cart" type="object" class="oe_highlight" attrs="{'invisible': [('edit', '=', True)]}"/>
+ <button string="Discard" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+ </record>
+
</odoo>
diff --git a/addons/lunch/wizard/__init__.py b/addons/lunch/wizard/__init__.py
deleted file mode 100644
index 9fa7ccafa50..00000000000
--- a/addons/lunch/wizard/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import lunch_line_wizard
diff --git a/addons/lunch/wizard/lunch_line_wizard.py b/addons/lunch/wizard/lunch_line_wizard.py
deleted file mode 100644
index 29dc6510e5b..00000000000
--- a/addons/lunch/wizard/lunch_line_wizard.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models, _
-
-from odoo.exceptions import ValidationError
-
-
-class LunchOrderWizard(models.TransientModel):
- _name = 'lunch.order.temp'
- _description = 'Lunch Order Temp'
-
- def _default_order_line(self):
- line_id = self.env.context.get('line_id')
-
- if line_id:
- last_time_ordered = self.env['lunch.order'].browse(line_id)
- else:
- last_time_ordered = self.env['lunch.order'].search([('product_id', '=', self.env.context.get('default_product_id', 0)),
- ('user_id', '=', self.env.context.get('default_user_id', self.env.user.id))],
- order="date desc, id desc", limit=1)
- return last_time_ordered
-
- currency_id = fields.Many2one('res.currency', default=lambda self: self.env.company.currency_id)
-
- product_id = fields.Many2one('lunch.product', string='Product')
- product_description = fields.Text('Description', related='product_id.description')
- product_name = fields.Char('Product Name', related='product_id.name')
- product_category = fields.Many2one('lunch.product.category', related='product_id.category_id')
- topping_label_1 = fields.Char(related='product_id.category_id.topping_label_1')
- topping_label_2 = fields.Char(related='product_id.category_id.topping_label_2')
- topping_label_3 = fields.Char(related='product_id.category_id.topping_label_3')
- topping_quantity_1 = fields.Selection(related='product_id.category_id.topping_quantity_1')
- topping_quantity_2 = fields.Selection(related='product_id.category_id.topping_quantity_2')
- topping_quantity_3 = fields.Selection(related='product_id.category_id.topping_quantity_3')
- topping_ids_1 = fields.Many2many('lunch.topping', 'lunch_order_temp_topping', 'order_id', 'topping_id', string="Extra Garniture",
- domain="[('category_id', '=', product_category), ('topping_category', '=', 1)]",
- default=lambda self: self._default_order_line().topping_ids_1)
- topping_ids_2 = fields.Many2many('lunch.topping', 'lunch_order_temp_topping', 'order_id', 'topping_id', string="Extra Garniture 2",
- domain="[('category_id', '=', product_category), ('topping_category', '=', 2)]",
- default=lambda self: self._default_order_line().topping_ids_2)
- topping_ids_3 = fields.Many2many('lunch.topping', 'lunch_order_temp_topping', 'order_id', 'topping_id', string="Extra Garniture 3",
- domain="[('category_id', '=', product_category), ('topping_category', '=', 3)]",
- default=lambda self: self._default_order_line().topping_ids_3)
-
- available_toppings_1 = fields.Boolean(help='Are extras available for this product', compute='_compute_available_toppings')
- available_toppings_2 = fields.Boolean(help='Are extras available for this product', compute='_compute_available_toppings')
- available_toppings_3 = fields.Boolean(help='Are extras available for this product', compute='_compute_available_toppings')
-
- image_1920 = fields.Image(related='product_id.image_1920')
- image_128 = fields.Image(related='product_id.image_128')
-
- quantity = fields.Float('Quantity', default=1)
- price_total = fields.Float('Total Price', compute='_compute_price_total')
- note = fields.Text('Special Instructions', default=lambda self: self._default_order_line().note)
-
- user_id = fields.Many2one('res.users', default=lambda self: self.env.user.id)
- edit = fields.Boolean('Edit Mode', default=lambda self: bool(self.env.context.get('line_id')))
-
- @api.depends('product_id')
- def _compute_available_toppings(self):
- for wizard in self:
- wizard.available_toppings_1 = bool(wizard.env['lunch.topping'].search_count([('category_id', '=', wizard.product_category.id), ('topping_category', '=', 1)]))
- wizard.available_toppings_2 = bool(wizard.env['lunch.topping'].search_count([('category_id', '=', wizard.product_category.id), ('topping_category', '=', 2)]))
- wizard.available_toppings_3 = bool(wizard.env['lunch.topping'].search_count([('category_id', '=', wizard.product_category.id), ('topping_category', '=', 3)]))
-
- @api.constrains('topping_ids_1', 'topping_ids_2', 'topping_ids_3')
- def _check_topping_quantity(self):
- errors = {
- '1_more': _('You should order at least one %s'),
- '1': _('You have to order one and only one %s'),
- }
- for wizard in self:
- for index in range(1, 4):
- availability = wizard['available_toppings_%s' % index]
- quantity = wizard['topping_quantity_%s' % index]
- toppings = wizard['topping_ids_%s' % index].filtered(lambda x: x.topping_category == index)
- label = wizard['topping_label_%s' % index]
-
- if availability and quantity != '0_more':
- check = bool(len(toppings) == 1 if quantity == '1' else toppings)
- if not check:
- raise ValidationError(errors[quantity] % label)
-
- @api.depends('product_id', 'topping_ids_1', 'topping_ids_2', 'topping_ids_3', 'quantity')
- def _compute_price_total(self):
- for wizard in self:
- wizard.price_total = wizard.quantity * (wizard.product_id.price +
- sum((wizard.topping_ids_1 | wizard.topping_ids_2 | wizard.topping_ids_3).mapped('price')))
-
- def _get_matching_lines(self):
- domain = [('user_id', '=', self.user_id.id), ('product_id', '=', self.product_id.id), ('date', '=', fields.Date.today()), ('note', '=', self._get_note())]
- lines = self.env['lunch.order'].search(domain)
- return lines.filtered(lambda line: (line.topping_ids_1 | line.topping_ids_2 | line.topping_ids_3) == self.topping_ids_1)
-
- def _get_note(self):
- """
- returns self.note, but make sure that if it is an empty string it becomes False
- """
- return self.note if self.note else False
-
- def add_to_cart(self):
- self.ensure_one()
- line_id = self.env.context.get('line_id')
-
- matching_line = False
- matching_lines = self._get_matching_lines()
-
- if matching_lines:
- matching_line = matching_lines[0]
- quantity = 1
-
- if matching_line.id != line_id:
- if self.edit:
- line = self.env['lunch.order'].browse(line_id)
- quantity = line.quantity
- line.sudo().unlink()
- else:
- quantity = 0
-
- matching_line.quantity += quantity
- else:
- if self.edit:
- line = self.env['lunch.order'].browse(line_id)
-
- line.topping_ids_1 = self.topping_ids_1
- line.topping_ids_2 = self.topping_ids_2
- line.topping_ids_3 = self.topping_ids_3
- line.note = self._get_note()
- else:
- self.env['lunch.order'].create({
- 'product_id': self.product_id.id,
- 'topping_ids_1': [(6, 0, self.topping_ids_1.ids)],
- 'topping_ids_2': [(6, 0, self.topping_ids_2.ids)],
- 'topping_ids_3': [(6, 0, self.topping_ids_3.ids)],
- 'quantity': self.quantity,
- 'note': self._get_note()
- })
diff --git a/addons/lunch/wizard/lunch_line_wizard_views.xml b/addons/lunch/wizard/lunch_line_wizard_views.xml
deleted file mode 100644
index 094c24b6cff..00000000000
--- a/addons/lunch/wizard/lunch_line_wizard_views.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
-<data>
- <record id="view_lunch_order_temp" model="ir.ui.view">
- <field name="name">view.lunch.order.temp</field>
- <field name="model">lunch.order.temp</field>
- <field name="arch" type="xml">
- <form>
- <field name="currency_id" invisible="1"/>
- <field name="quantity" invisible="1"/>
- <field name="product_id" invisible="1"/>
- <field name="product_category" invisible="1"/>
- <field name="available_toppings_1" invisible="1"/>
- <field name="available_toppings_2" invisible="1"/>
- <field name="available_toppings_3" invisible="1"/>
- <field name="edit" invisible="1"/>
- <div class="d-flex">
- <div class="flex-grow-0 pr-5">
- <field name="image_1920" widget="image" class="o_lunch_image" options="{'image_preview': 'image_128'}"/>
- </div>
- <div class="flex-grow-1 pr-5">
- <h2><field name="product_name"/></h2>
- <h3 class="pt-3"><field name="price_total" widget="monetary"/></h3>
- </div>
- </div>
- <div class="o_lunch_wizard">
- <div class="row">
- <div class="col-2">
- <field name="topping_label_1" nolabel="1" attrs="{'invisible': [('available_toppings_1', '=', False)]}" class="font-weight-bold"/>
- </div>
- <div class="col-10">
- <field name="topping_ids_1" attrs="{'invisible': [('available_toppings_1', '=', False)]}" widget="many2many_checkboxes" nolabel="1"/>
- </div>
- </div>
- <div class="row">
- <div class="col-2">
- <field name="topping_label_2" nolabel="1" attrs="{'invisible': [('available_toppings_2', '=', False)]}" class="font-weight-bold"/>
- </div>
- <div class="col-10">
- <field name="topping_ids_2" attrs="{'invisible': [('available_toppings_2', '=', False)]}" widget="many2many_checkboxes" nolabel="1"/>
- </div>
- </div>
- <div class="row">
- <div class="col-2">
- <field name="topping_label_3" nolabel="1" attrs="{'invisible': [('available_toppings_3', '=', False)]}" class="font-weight-bold"/>
- </div>
- <div class="col-10">
- <field name="topping_ids_3" attrs="{'invisible': [('available_toppings_3', '=', False)]}" widget="many2many_checkboxes" nolabel="1"/>
- </div>
- </div>
- <div class="row">
- <div class="col-2">
- <label for="product_description" class="font-weight-bold"/>
- </div>
- <div class="col-10">
- <field name="product_description" nolabel="1"/>
- </div>
- </div>
- <div class="row">
- <div class="col-2">
- <label for="note" class="font-weight-bold" />
- </div>
- <div class="col-10">
- <field name="note" nolabel="1" placeholder="Informations, allergens, ..." />
- </div>
- </div>
- </div>
- <footer>
- <button name="add_to_cart" string="Add To Cart" type="object" class="oe_highlight" attrs="{'invisible': [('edit', '=', True)]}"/>
- <button name="add_to_cart" string="Save" type="object" class="oe_highlight" attrs="{'invisible': [('edit', '=', False)]}"/>
- <button string="Discard" special="cancel"/>
- </footer>
- </form>
- </field>
- </record>
-</data>
-</odoo>