[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:
RomainLibert
2019-10-18 12:37:52 +00:00
committed by Yannick Tivisse
parent ace7f03bab
commit 69319bba3e
9 changed files with 220 additions and 230 deletions
-1
View File
@@ -4,4 +4,3 @@
from . import controllers
from . import models
from . import report
from . import wizard
-1
View File
@@ -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',
+129 -2
View File
@@ -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");
+73
View File
@@ -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>
-4
View File
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import lunch_line_wizard
-138
View File
@@ -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>