[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>
This commit is contained in:
committed by
Yannick Tivisse
parent
ace7f03bab
commit
69319bba3e
@@ -4,4 +4,3 @@
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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'})
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import lunch_line_wizard
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user