summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2019-01-08 13:12:33 +0100
committerYannick Tivisse <yti@odoo.com>2019-01-08 13:12:33 +0100
commitd1c3948a05acbe73fc6965833a442a1b35efdbf8 (patch)
tree0a6ba422c415c2c57cd2cde9ddea12e2c1c49f46 /addons
parent7eefa3758327e0f09190c740a3a4d498fba40605 (diff)
Revert "[IMP] lunch: automatize lunch orders"
This reverts commit f727b8f0b57aad49f2ea8c54600ab73bbd02570f.
Diffstat (limited to 'addons')
-rw-r--r--addons/lunch/__init__.py3
-rw-r--r--addons/lunch/__manifest__.py7
-rw-r--r--addons/lunch/controllers/__init__.py1
-rw-r--r--addons/lunch/controllers/main.py67
-rw-r--r--addons/lunch/data/ir_cron_data.xml17
-rw-r--r--addons/lunch/data/lunch_data.xml15
-rw-r--r--addons/lunch/data/lunch_demo.xml155
-rw-r--r--addons/lunch/models/__init__.py2
-rw-r--r--addons/lunch/models/lunch_alert.py85
-rw-r--r--addons/lunch/models/lunch_cashmove.py7
-rw-r--r--addons/lunch/models/lunch_order.py309
-rw-r--r--addons/lunch/models/lunch_product.py87
-rw-r--r--addons/lunch/models/lunch_supplier.py180
-rw-r--r--addons/lunch/security/ir.model.access.csv4
-rw-r--r--addons/lunch/static/img/4formaggio.pngbin8362 -> 0 bytes
-rw-r--r--addons/lunch/static/img/Coke.pngbin7469 -> 0 bytes
-rw-r--r--addons/lunch/static/img/bacon_burger.pngbin8027 -> 0 bytes
-rw-r--r--addons/lunch/static/img/brie.pngbin6794 -> 0 bytes
-rw-r--r--addons/lunch/static/img/cheeseburger.pngbin7020 -> 0 bytes
-rw-r--r--addons/lunch/static/img/chicken_curry.pngbin7147 -> 0 bytes
-rw-r--r--addons/lunch/static/img/chirashi.pngbin7221 -> 0 bytes
-rw-r--r--addons/lunch/static/img/club.pngbin7011 -> 0 bytes
-rw-r--r--addons/lunch/static/img/coke_zero.pngbin7153 -> 0 bytes
-rw-r--r--addons/lunch/static/img/fanta.pngbin5855 -> 0 bytes
-rw-r--r--addons/lunch/static/img/fuze_black.pngbin5623 -> 0 bytes
-rw-r--r--addons/lunch/static/img/fuze_green.pngbin4724 -> 0 bytes
-rw-r--r--addons/lunch/static/img/gouda.pngbin7864 -> 0 bytes
-rw-r--r--addons/lunch/static/img/italiana.pngbin11549 -> 0 bytes
-rw-r--r--addons/lunch/static/img/lipton.pngbin6784 -> 0 bytes
-rw-r--r--addons/lunch/static/img/maki.pngbin8806 -> 0 bytes
-rw-r--r--addons/lunch/static/img/mozza.pngbin6191 -> 0 bytes
-rw-r--r--addons/lunch/static/img/napoli.pngbin9099 -> 0 bytes
-rw-r--r--addons/lunch/static/img/pasta_bolognese.pngbin6727 -> 0 bytes
-rw-r--r--addons/lunch/static/img/pizza_funghi.pngbin9213 -> 0 bytes
-rw-r--r--addons/lunch/static/img/pizza_margherita.pngbin8597 -> 0 bytes
-rw-r--r--addons/lunch/static/img/pizza_veggie.pngbin7354 -> 0 bytes
-rw-r--r--addons/lunch/static/img/salmon_sushi.pngbin6885 -> 0 bytes
-rw-r--r--addons/lunch/static/img/temaki.pngbin9244 -> 0 bytes
-rw-r--r--addons/lunch/static/img/tuna_sandwich.pngbin6425 -> 0 bytes
-rw-r--r--addons/lunch/static/src/js/lunch.js94
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_controller.js190
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_model.js19
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_record.js22
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_renderer.js29
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_view.js42
-rw-r--r--addons/lunch/static/src/js/lunch_kanban_widget.js87
-rw-r--r--addons/lunch/static/src/js/lunch_payment_dialog.js24
-rw-r--r--addons/lunch/static/src/scss/lunch.scss14
-rw-r--r--addons/lunch/static/src/scss/lunch_kanban.scss48
-rw-r--r--addons/lunch/static/src/xml/lunch_kanban.xml118
-rw-r--r--addons/lunch/static/tests/lunch_tests.js145
-rw-r--r--addons/lunch/static/tests/test_lunch_kanban.js207
-rw-r--r--addons/lunch/tests/__init__.py3
-rw-r--r--addons/lunch/tests/common.py75
-rw-r--r--addons/lunch/tests/test_lunch.py143
-rw-r--r--addons/lunch/tests/test_supplier.py158
-rw-r--r--addons/lunch/views/lunch_alert_views.xml134
-rw-r--r--addons/lunch/views/lunch_orders_views.xml31
-rw-r--r--addons/lunch/views/lunch_product_views.xml92
-rw-r--r--addons/lunch/views/lunch_supplier_views.xml113
-rw-r--r--addons/lunch/views/lunch_templates.xml17
-rw-r--r--addons/lunch/views/lunch_views.xml8
-rw-r--r--addons/lunch/wizard/__init__.py2
-rw-r--r--addons/lunch/wizard/lucky_order.py44
-rw-r--r--addons/lunch/wizard/lucky_order_view.xml35
-rw-r--r--addons/lunch/wizard/lunch_line_wizard.py77
-rw-r--r--addons/lunch/wizard/lunch_line_wizard_views.xml35
-rw-r--r--addons/lunch_hr/__init__.py4
-rw-r--r--addons/lunch_hr/__manifest__.py19
-rw-r--r--addons/lunch_hr/data/lunch_hr_demo.xml28
-rw-r--r--addons/lunch_hr/models/__init__.py5
-rw-r--r--addons/lunch_hr/models/lunch_product.py33
-rw-r--r--addons/lunch_hr/models/lunch_supplier.py10
-rw-r--r--addons/lunch_hr/views/lunch_product_views.xml13
-rw-r--r--addons/lunch_hr/views/lunch_supplier_views.xml13
75 files changed, 1089 insertions, 1981 deletions
diff --git a/addons/lunch/__init__.py b/addons/lunch/__init__.py
index 88710707227..b6f07433380 100644
--- a/addons/lunch/__init__.py
+++ b/addons/lunch/__init__.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import controllers
-from . import models
from . import wizard
+from . import models
diff --git a/addons/lunch/__manifest__.py b/addons/lunch/__manifest__.py
index 5268ecca6a7..e28b88a82f2 100644
--- a/addons/lunch/__manifest__.py
+++ b/addons/lunch/__manifest__.py
@@ -27,17 +27,16 @@ If you want to save your employees' time and avoid them to always have coins in
'report/lunch_order_views.xml',
'report/lunch_order_templates.xml',
'views/lunch_templates.xml',
- 'wizard/lunch_line_wizard_views.xml',
+ 'wizard/lucky_order_view.xml',
+ 'views/lunch_alert_views.xml',
'views/lunch_cashmove_views.xml',
'views/lunch_orders_views.xml',
'views/lunch_product_views.xml',
- 'views/lunch_supplier_views.xml',
'views/lunch_views.xml',
'data/lunch_data.xml',
- 'data/ir_cron_data.xml'
],
'demo': ['data/lunch_demo.xml'],
- 'qweb': ['static/src/xml/lunch_kanban.xml', ],
+ 'qweb': ['static/src/xml/lunch.xml', ],
'installable': True,
'application': True,
'certificate': '001292377792581874189',
diff --git a/addons/lunch/controllers/__init__.py b/addons/lunch/controllers/__init__.py
deleted file mode 100644
index 12a7e529b67..00000000000
--- a/addons/lunch/controllers/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import main
diff --git a/addons/lunch/controllers/main.py b/addons/lunch/controllers/main.py
deleted file mode 100644
index 314cf5ad292..00000000000
--- a/addons/lunch/controllers/main.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import http, fields
-from odoo.http import request
-
-
-class LunchController(http.Controller):
- @http.route('/lunch/infos', type='json', auth='user')
- def infos(self, user_id=None):
- user = request.env['res.users'].browse(user_id) if user_id else request.env.user
-
- infos = {
- 'order': False,
- 'wallet': request.env['lunch.cashmove'].get_wallet_balance(user),
- 'username': user.sudo().name,
- 'userimage': '/web/image?model=res.users&id=%s&field=image_small' % user.id,
- 'is_manager': request.env.user.has_group('lunch.group_lunch_manager'),
- 'users': request.env['res.users'].search_read([('groups_id', 'not in', [request.env.ref('base.group_portal').id])], ['name']),
- }
-
- order = self._get_current_order(user.id)
- if order:
- lines = [{'id': line.id,
- 'product': (line.product_id.name, line.product_id.price),
- 'toppings': [(topping.name, topping.price) for topping in line.topping_ids],
- 'quantity': line.quantity,
- 'price': line.price} for line in order.order_line_ids]
- infos.update({
- 'order': order.id,
- 'total': order.total,
- 'state': order.state,
- 'lines': lines,
- })
- return infos
-
- @http.route('/lunch/trash', type='json', auth='user')
- def trash(self, user_id=None):
- user = request.env['res.users'].browse(user_id) if user_id else request.env.user
-
- order = self._get_current_order(user.id)
- order.unlink()
-
- @http.route('/lunch/pay', type='json', auth='user')
- def pay(self, user_id=None):
- user = request.env['res.users'].browse(user_id) if user_id else request.env.user
-
- order = self._get_current_order(user.id)
- if order:
- wallet_balance = request.env['lunch.cashmove'].get_wallet_balance(user)
-
- if order.state == 'new' and order.total <= wallet_balance:
- return self._make_payment(order)
-
- return False
-
- @http.route('/lunch/payment_message', type='json', auth='user')
- def payment_message(self):
- return {'message': request.env['ir.qweb'].render('lunch.lunch_payment_dialog', {})}
-
- def _get_current_order(self, user_id):
- order = request.env['lunch.order'].search([('user_id', '=', user_id), ('date', '=', fields.Date.today())], limit=1)
- return order
-
- def _make_payment(self, order):
- order.action_order()
- return True
diff --git a/addons/lunch/data/ir_cron_data.xml b/addons/lunch/data/ir_cron_data.xml
deleted file mode 100644
index 19275e67f21..00000000000
--- a/addons/lunch/data/ir_cron_data.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0"?>
-<odoo>
- <data noupdate="1">
- <record id="ir_cron_lunch" model="ir.cron">
- <field name="name">Lunch: automatic email send</field>
- <field name="model_id" ref="model_lunch_supplier"/>
- <field name="state">code</field>
- <field name="code">model._auto_email_send()</field>
- <field name="active" eval="True"/>
- <field name="user_id" ref="base.user_root"/>
- <field name="interval_number">10</field>
- <field name="interval_type">minutes</field>
- <field name="numbercall">-1</field>
- <field name="doall" eval="False"/>
- </record>
- </data>
-</odoo>
diff --git a/addons/lunch/data/lunch_data.xml b/addons/lunch/data/lunch_data.xml
index 0f269f42d0c..2332d2e5272 100644
--- a/addons/lunch/data/lunch_data.xml
+++ b/addons/lunch/data/lunch_data.xml
@@ -1,12 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
+ <record id="lunch_order_line_action_order" model="ir.actions.server">
+ <field name="name">Lunch: Order meals</field>
+ <field name="model_id" ref="model_lunch_order_line"/>
+ <field name="binding_model_id" ref="model_lunch_order_line"/>
+ <field name="state">code</field>
+ <field name="code">
+if records:
+ action = records.order()
+ </field>
+ </record>
+
<record id="lunch_order_line_action_confirm" model="ir.actions.server">
<field name="name">Lunch: Receive meals</field>
<field name="model_id" ref="model_lunch_order_line"/>
<field name="binding_model_id" ref="model_lunch_order_line"/>
<field name="state">code</field>
- <field name="code">records.action_confirm()</field>
+ <field name="code">records.confirm()</field>
</record>
<record id="lunch_order_line_action_cancel" model="ir.actions.server">
@@ -14,7 +25,7 @@
<field name="model_id" ref="model_lunch_order_line"/>
<field name="binding_model_id" ref="model_lunch_order_line"/>
<field name="state">code</field>
- <field name="code">records.action_cancel()</field>
+ <field name="code">records.cancel()</field>
</record>
</data>
<data noupdate="0">
diff --git a/addons/lunch/data/lunch_demo.xml b/addons/lunch/data/lunch_demo.xml
index f91a57a183d..bd36db1f0af 100644
--- a/addons/lunch/data/lunch_demo.xml
+++ b/addons/lunch/data/lunch_demo.xml
@@ -28,7 +28,7 @@
<field name="name">Chirashi</field>
</record>
- <record id="partner_coin_gourmand" model="res.partner">
+ <record model="res.partner" id="partner_coin_gourmand">
<field name="name">Coin gourmand</field>
<field name="city">Tirana</field>
<field name="country_id" ref="base.al"/>
@@ -37,13 +37,7 @@
<field name="email">coin.gourmand@yourcompany.example.com</field>
</record>
- <record model="lunch.supplier" id="supplier_coin_gourmand">
- <field name="partner_id" ref="partner_coin_gourmand"/>
- <field name="recurrency_from">8</field>
- <field name="recurrency_to">23</field>
- </record>
-
- <record id="partner_pizza_inn" model="res.partner">
+ <record model="res.partner" id="partner_pizza_inn">
<field name="name">Pizza Inn</field>
<field name="city">New Delhi TN</field>
<field name="country_id" ref="base.us"/>
@@ -62,12 +56,6 @@
<field name="email">info@corner.com</field>
</record>
- <record model="lunch.supplier" id="supplier_corner">
- <field name="partner_id" ref="partner_corner"/>
- <field name="recurrency_from">8</field>
- <field name="recurrency_to">23</field>
- </record>
-
<record model="res.partner" id="partner_sushi_shop">
<field name="name">Sushi Shop</field>
<field name="city">Paris</field>
@@ -77,228 +65,178 @@
<field name="email">order@sushi.com</field>
</record>
- <record model="lunch.supplier" id="supplier_sushi_shop">
- <field name="partner_id" ref="partner_sushi_shop"/>
- <field name="recurrency_from">8</field>
- <field name="recurrency_to">23</field>
- </record>
-
<record model="lunch.product" id="product_bacon">
<field name="name">Bacon</field>
<field name="category_id" eval="str(ref('categ_burger'))"/>
<field name="price">7.5</field>
- <field name="supplier_id" ref="supplier_corner"/>
+ <field name="supplier" eval="str(ref('partner_corner'))"/>
<field name="description">Beef, Bacon, Salad, Cheddar, Fried Onion, BBQ Sauce</field>
- <field name="image" type="base64" file="lunch/static/img/bacon_burger.png"/>
</record>
<record model="lunch.product" id="product_cheeseburger">
<field name="name">Cheese Burger</field>
<field name="category_id" eval="str(ref('categ_burger'))"/>
<field name="price">7.0</field>
- <field name="supplier_id" ref="supplier_corner"/>
+ <field name="supplier" eval="str(ref('partner_corner'))"/>
<field name="description">Beef, Cheddar, Salad, Fried Onions, BBQ Sauce</field>
- <field name="image" type="base64" file="lunch/static/img/cheeseburger.png"/>
</record>
<record model="lunch.product" id="product_chicken_curry">
<field name="name">Chicken Curry</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">3.0</field>
- <field name="supplier_id" ref="supplier_corner"/>
- <field name="image" type="base64" file="lunch/static/img/chicken_curry.png"/>
+ <field name="supplier" eval="str(ref('partner_corner'))"/>
</record>
<record model="lunch.product" id="product_spicy_tuna">
<field name="name">Spicy Tuna</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">3.0</field>
- <field name="supplier_id" ref="supplier_corner"/>
- <field name="image" type="base64" file="lunch/static/img/chicken_curry.png"/>
+ <field name="supplier" eval="str(ref('partner_corner'))"/>
</record>
<record model="lunch.product" id="product_mozzarella">
<field name="name">Mozzarella</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">3.9</field>
- <field name="supplier_id" ref="supplier_corner"/>
+ <field name="supplier" eval="str(ref('partner_corner'))"/>
<field name="description">Mozzarella, Pesto, Tomatoes</field>
- <field name="image" type="base64" file="lunch/static/img/mozza.png"/>
</record>
<record model="lunch.product" id="product_club">
<field name="name">Club</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">3.4</field>
- <field name="supplier_id" ref="supplier_corner"/>
+ <field name="supplier" eval="str(ref('partner_corner'))"/>
<field name="description">Ham, Cheese, Vegetables</field>
- <field name="image" type="base64" file="lunch/static/img/club.png"/>
</record>
<record model="lunch.product" id="product_maki">
<field name="name">Lunch Maki 18pc</field>
<field name="category_id" eval="str(ref('categ_sushi'))"/>
<field name="price">12.0</field>
- <field name="supplier_id" ref="supplier_sushi_shop"/>
+ <field name="supplier" eval="str(ref('partner_sushi_shop'))"/>
<field name="description">6 Maki Salmon - 6 Maki Tuna - 6 Maki Shrimp/Avocado</field>
- <field name="image" type="base64" file="lunch/static/img/maki.png"/>
</record>
<record model="lunch.product" id="product_salmon">
<field name="name">Lunch Salmon 20pc</field>
<field name="category_id" eval="str(ref('categ_sushi'))"/>
<field name="price">13.80</field>
- <field name="supplier_id" ref="supplier_sushi_shop"/>
+ <field name="supplier" eval="str(ref('partner_sushi_shop'))"/>
<field name="description">4 Sushi Salmon - 6 Maki Salmon - 4 Sashimi Salmon </field>
- <field name="image" type="base64" file="lunch/static/img/salmon_sushi.png"/>
</record>
<record model="lunch.product" id="product_temaki">
<field name="name">Lunch Temaki mix 3pc</field>
<field name="category_id" eval="str(ref('categ_temaki'))"/>
<field name="price">14.0</field>
- <field name="supplier_id" ref="supplier_sushi_shop"/>
- <field name="description">1 Avocado - 1 Salmon - 1 Eggs - 1 Tuna</field>
- <field name="image" type="base64" file="lunch/static/img/temaki.png"/>
+ <field name="supplier" eval="str(ref('partner_sushi_shop'))"/>
+ <field name="description">1 Avocado/Cooked Tuna - 1 Salmon/Eggs - 1 Salmon/Avocado - 1 Tuna/Mango</field>
</record>
<record model="lunch.product" id="product_chirashi">
<field name="name">Salmon and Avocado</field>
<field name="category_id" eval="str(ref('categ_chirashi'))"/>
<field name="price">9.25</field>
- <field name="supplier_id" ref="supplier_sushi_shop"/>
- <field name="description">2 Tempuras, Cabbages, Onions, Sesame Sauce</field>
- <field name="image" type="base64" file="lunch/static/img/chirashi.png"/>
- </record>
-
- <record model="lunch.supplier" id="supplier_pizza_inn">
- <field name="partner_id" ref="partner_pizza_inn"/>
- <field name="send_by">mail</field>
- <field name="automatic_email_send" eval="True"/>
- <field name="automatic_email_time">11</field>
- <field name="recurrency_from">8</field>
- <field name="recurrency_to">23</field>
- <field name="recurrency">reccurent</field>
- <field name="recurrency_monday" eval="True"/>
- <field name="recurrency_tuesday" eval="True"/>
- <field name="recurrency_wednesday" eval="True"/>
- <field name="recurrency_thursday" eval="True"/>
- <field name="recurrency_friday" eval="True"/>
+ <field name="supplier" eval="str(ref('partner_sushi_shop'))"/>
+ <field name="description">Shrimp Tempura, Yam Tempura, Chinese Cabbage, Pickled Onions, Sesame Sauce</field>
</record>
<record model="lunch.product" id="product_cheese_ham">
<field name="name">Cheese And Ham</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">3.30</field>
- <field name="supplier_id" eval="str(ref('supplier_coin_gourmand'))"/>
+ <field name="supplier" eval="str(ref('partner_coin_gourmand'))"/>
<field name="description">Cheese, Ham, Salad, Tomatoes, cucumbers, eggs</field>
- <field name="image" type="base64" file="lunch/static/img/club.png"/>
</record>
<record model="lunch.product" id="product_country">
<field name="name">The Country</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">3.30</field>
- <field name="supplier_id" eval="str(ref('supplier_coin_gourmand'))"/>
+ <field name="supplier" eval="str(ref('partner_coin_gourmand'))"/>
<field name="description">Brie, Honey, Walnut Kernels</field>
- <field name="image" type="base64" file="lunch/static/img/brie.png"/>
</record>
<record model="lunch.product" id="product_tuna">
<field name="name">Tuna</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">2.50</field>
- <field name="supplier_id" eval="str(ref('supplier_coin_gourmand'))"/>
+ <field name="supplier" eval="str(ref('partner_coin_gourmand'))"/>
<field name="description">Tuna, Mayonnaise</field>
- <field name="image" type="base64" file="lunch/static/img/tuna_sandwich.png"/>
</record>
<record model="lunch.product" id="product_gouda">
<field name="name">Gouda Cheese</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">2.50</field>
- <field name="supplier_id" eval="str(ref('supplier_coin_gourmand'))"/>
+ <field name="supplier" eval="str(ref('partner_coin_gourmand'))"/>
<field name="description"></field>
- <field name="image" type="base64" file="lunch/static/img/gouda.png"/>
</record>
<record model="lunch.product" id="product_chicken_curry">
<field name="name">Chicken Curry</field>
<field name="category_id" eval="str(ref('categ_sandwich'))"/>
<field name="price">2.60</field>
- <field name="supplier_id" eval="str(ref('supplier_coin_gourmand'))"/>
+ <field name="supplier" eval="str(ref('partner_coin_gourmand'))"/>
<field name="description"></field>
- <field name="image" type="base64" file="lunch/static/img/chicken_curry.png"/>
</record>
<record model="lunch.product" id="product_margherita">
<field name="name">Pizza Margherita</field>
<field name="category_id" eval="str(ref('categ_pizza'))"/>
<field name="price">6.90</field>
- <field name="supplier_id" eval="str(ref('supplier_pizza_inn'))"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description">Tomatoes, Mozzarella</field>
- <field name="image" type="base64" file="lunch/static/img/pizza_margherita.png"/>
</record>
<record model="lunch.product" id="product_funghi">
<field name="name">Pizza Funghi</field>
<field name="category_id" eval="str(ref('categ_pizza'))"/>
<field name="price">7.00</field>
- <field name="supplier_id" ref="supplier_pizza_inn"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description">Tomatoes, Mushrooms, Mozzarella</field>
- <field name="image" type="base64" file="lunch/static/img/pizza_funghi.png"/>
</record>
<record model="lunch.product" id="product_vege">
<field name="name">Pizza Vegetarian</field>
<field name="category_id" eval="str(ref('categ_pizza'))"/>
<field name="price">7.00</field>
- <field name="supplier_id" ref="supplier_pizza_inn"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description">Tomatoes, Mozzarella, Mushrooms, Peppers, Olives</field>
- <field name="image" type="base64" file="lunch/static/img/pizza_veggie.png"/>
</record>
<record model="lunch.product" id="product_italiana">
<field name="name">Pizza Italiana</field>
<field name="category_id" eval="str(ref('categ_pizza'))"/>
<field name="price">7.40</field>
- <field name="supplier_id" eval="str(ref('supplier_pizza_inn'))"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description">Fresh Tomatoes, Basil, Mozzarella</field>
- <field name="image" type="base64" file="lunch/static/img/italiana.png"/>
</record>
<record model="lunch.product" id="product_Bolognese">
<field name="name">Bolognese Pasta</field>
<field name="category_id" eval="str(ref('categ_pasta'))"/>
<field name="price">7.70</field>
- <field name="supplier_id" eval="str(ref('supplier_pizza_inn'))"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description"></field>
- <field name="image" type="base64" file="lunch/static/img/pasta_bolognese.png"/>
</record>
<record model="lunch.product" id="product_Napoli">
<field name="name">Napoli Pasta</field>
<field name="category_id" eval="str(ref('categ_pasta'))"/>
<field name="price">7.70</field>
- <field name="supplier_id" eval="str(ref('supplier_pizza_inn'))"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description">Tomatoes, Basil</field>
- <field name="image" type="base64" file="lunch/static/img/napoli.png"/>
- </record>
-
- <record model="lunch.product" id="product_olives">
- <field name="name">Olives</field>
- <field name="price">0.30</field>
- <field name="supplier_id" eval="str(ref('supplier_pizza_inn'))"/>
- <field name="is_topping" eval="True"/>
</record>
<record model="lunch.product" id="product_4formaggi">
<field name="name">4 Formaggi</field>
<field name="category_id" eval="str(ref('categ_pasta'))"/>
<field name="price">5.50</field>
- <field name="supplier_id" ref="supplier_pizza_inn"/>
+ <field name="supplier" eval="str(ref('partner_pizza_inn'))"/>
<field name="description">Tomato sauce, Olive oil, Fresh Tomatoes, Onions, Vegetables, Parmesan</field>
- <field name="image" type="base64" file="lunch/static/img/4formaggio.png"/>
</record>
<record model="lunch.order" id="order_1">
@@ -306,7 +244,6 @@
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="order_line_ids" eval="[]"/>
<field name="state">new</field>
- <field name="mail_sent" eval="True"/>
</record>
<record model="lunch.order" id="order_2">
@@ -314,7 +251,6 @@
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
<field name="order_line_ids" eval="[]"/>
<field name="state">confirmed</field>
- <field name="mail_sent" eval="True"/>
</record>
<record model="lunch.order" id="order_3">
@@ -322,7 +258,6 @@
<field name="date" eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d')"/>
<field name="order_line_ids" eval="[]"/>
<field name="state">confirmed</field>
- <field name="mail_sent" eval="True"/>
</record>
<record model="lunch.order" id="order_4">
@@ -338,9 +273,9 @@
<field name="price">7.70</field>
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="state">new</field>
- <field name="supplier_id" ref="supplier_pizza_inn"/>
+ <field name="supplier" ref="partner_pizza_inn"/>
+ <field name="note">+Emmental</field>
<field name="order_id" ref="order_1"/>
- <field name="quantity">1</field>
</record>
<record model="lunch.order.line" id="order_line_2">
@@ -349,9 +284,9 @@
<field name="price">7.40</field>
<field name="date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
<field name="state">confirmed</field>
- <field name="supplier_id" ref="supplier_pizza_inn"/>
+ <field name="supplier" ref="partner_pizza_inn"/>
+ <field name="note">+Mushrooms</field>
<field name="order_id" ref="order_2"/>
- <field name="quantity">1</field>
</record>
<record model="lunch.order.line" id="order_line_3">
@@ -360,9 +295,9 @@
<field name="price">2.50</field>
<field name="date" eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d')"/>
<field name="state">cancelled</field>
- <field name="supplier_id" ref="supplier_coin_gourmand"/>
+ <field name="supplier" ref="partner_coin_gourmand"/>
+ <field name="note">+Salad +Tomatoes +Cucumbers</field>
<field name="order_id" ref="order_3"/>
- <field name="quantity">1</field>
</record>
<record model="lunch.order.line" id="order_line_4">
@@ -371,9 +306,9 @@
<field name="price">2.60</field>
<field name="date" eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d')"/>
<field name="state">confirmed</field>
- <field name="supplier_id" ref="supplier_coin_gourmand"/>
+ <field name="supplier" ref="partner_coin_gourmand"/>
+ <field name="note">+Salad +Tomatoes +Eggs</field>
<field name="order_id" ref="order_3"/>
- <field name="quantity">1</field>
</record>
<record model="lunch.order.line" id="order_line_5">
@@ -382,7 +317,7 @@
<field name="price">5.50</field>
<field name="date" eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d')"/>
<field name="state">confirmed</field>
- <field name="supplier_id" ref="supplier_pizza_inn"/>
+ <field name="supplier" ref="partner_pizza_inn"/>
<field name="order_id" ref="order_4"/>
</record>
@@ -391,7 +326,7 @@
<field name="date" eval="(DateTime.now() + timedelta(days=2)).strftime('%Y-%m-%d')"/>
<field name="description">Pizza Italiana</field>
<field name="amount">-7.40</field>
- <field name="order_line_id" ref="order_line_1"/>
+ <field name="order_id" ref="order_2"/>
<field name="state">order</field>
</record>
@@ -400,7 +335,7 @@
<field name="date" eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d')"/>
<field name="description">Chicken curry</field>
<field name="amount">-2.60</field>
- <field name="order_line_id" ref="order_line_2"/>
+ <field name="order_id" ref="order_3"/>
<field name="state">order</field>
</record>
@@ -425,8 +360,22 @@
<field name="date" eval="(DateTime.now() + timedelta(days=3)).strftime('%Y-%m-%d')"/>
<field name="description">Pasta 4 Formaggi</field>
<field name="amount">-5.50</field>
- <field name="order_line_id" ref="order_line_3"/>
+ <field name="order_id" ref="order_4"/>
<field name="state">order</field>
</record>
+
+ <record model="lunch.alert" id="alert_1">
+ <field name="alert_type">week</field>
+ <field name="monday" eval="True"/>
+ <field name="tuesday" eval="True"/>
+ <field name="wednesday" eval="True"/>
+ <field name="thursday" eval="True"/>
+ <field name="friday" eval="True"/>
+ <field name="saturday" eval="False"/>
+ <field name="sunday" eval="False"/>
+ <field name="start_hour">7</field>
+ <field name="end_hour">23</field>
+ <field name="message">Lunch must be ordered before 10:30 AM</field>
+ </record>
</data>
</odoo>
diff --git a/addons/lunch/models/__init__.py b/addons/lunch/models/__init__.py
index 58a34e7891b..f99750c87d0 100644
--- a/addons/lunch/models/__init__.py
+++ b/addons/lunch/models/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
+from . import lunch_alert
from . import lunch_cashmove
from . import lunch_order
from . import lunch_product
-from . import lunch_supplier
diff --git a/addons/lunch/models/lunch_alert.py b/addons/lunch/models/lunch_alert.py
new file mode 100644
index 00000000000..325a64f4385
--- /dev/null
+++ b/addons/lunch/models/lunch_alert.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import datetime
+
+from odoo import api, fields, models, _
+
+
+class LunchAlert(models.Model):
+ """ Alerts to display during a lunch order. An alert can be specific to a
+ given day, weekly or daily. The alert is displayed from start to end hour. """
+ _name = 'lunch.alert'
+ _description = 'Lunch Alert'
+ _rec_name = 'message'
+
+ display = fields.Boolean(compute='_compute_display_get')
+ message = fields.Text('Message', required=True)
+ alert_type = fields.Selection([('specific', 'Specific Day'),
+ ('week', 'Every Week'),
+ ('days', 'Every Day')],
+ string='Recurrence', required=True, index=True, default='specific')
+ partner_id = fields.Many2one('res.partner', string="Vendor",
+ help="If specified, the selected vendor can be ordered only on selected days")
+ specific_day = fields.Date('Day', default=fields.Date.context_today)
+ monday = fields.Boolean('Monday')
+ tuesday = fields.Boolean('Tuesday')
+ wednesday = fields.Boolean('Wednesday')
+ thursday = fields.Boolean('Thursday')
+ friday = fields.Boolean('Friday')
+ saturday = fields.Boolean('Saturday')
+ sunday = fields.Boolean('Sunday')
+ start_hour = fields.Float('Between', oldname='active_from', required=True, default=7)
+ end_hour = fields.Float('And', oldname='active_to', required=True, default=23)
+ active = fields.Boolean(default=True)
+
+ @api.multi
+ def name_get(self):
+ return [(alert.id, '%s %s' % (_('Alert'), '#%d' % alert.id)) for alert in self]
+
+ @api.depends('alert_type', 'specific_day', 'monday', 'tuesday', 'thursday',
+ 'friday', 'saturday', 'sunday', 'start_hour', 'end_hour')
+ def _compute_display_get(self):
+ """
+ This method check if the alert can be displayed today
+ if alert type is specific : compare specific_day(date) with today's date
+ if alert type is week : check today is set as alert (checkbox true) eg. self['monday']
+ if alert type is day : True
+ return : Message if can_display_alert is True else False
+ """
+ days_codes = {'0': 'sunday',
+ '1': 'monday',
+ '2': 'tuesday',
+ '3': 'wednesday',
+ '4': 'thursday',
+ '5': 'friday',
+ '6': 'saturday'}
+ fullday = False
+ now = datetime.datetime.now()
+ if self.env.context.get('lunch_date'):
+ # lunch_date is a fields.Date -> 00:00:00
+ lunch_date = fields.Datetime.from_string(self.env.context['lunch_date'])
+ # if lunch_date is in the future, planned lunch, ignore hours
+ fullday = lunch_date > now
+ now = max(lunch_date, now)
+ mynow = fields.Datetime.context_timestamp(self, now)
+
+ for alert in self:
+ can_display_alert = {
+ 'specific': (str(alert.specific_day) == fields.Date.to_string(mynow)),
+ 'week': alert[days_codes[mynow.strftime('%w')]],
+ 'days': True
+ }
+
+ if can_display_alert[alert.alert_type]:
+ hour_to = int(alert.end_hour)
+ min_to = int((alert.end_hour - hour_to) * 60)
+ to_alert = datetime.time(hour_to, min_to)
+ hour_from = int(alert.start_hour)
+ min_from = int((alert.start_hour - hour_from) * 60)
+ from_alert = datetime.time(hour_from, min_from)
+
+ if fullday or (from_alert <= mynow.time() <= to_alert):
+ alert.display = True
+ else:
+ alert.display = False
diff --git a/addons/lunch/models/lunch_cashmove.py b/addons/lunch/models/lunch_cashmove.py
index 2cb71a77c69..7d4a6b56ae4 100644
--- a/addons/lunch/models/lunch_cashmove.py
+++ b/addons/lunch/models/lunch_cashmove.py
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
-from odoo.tools import float_round
class LunchCashMove(models.Model):
@@ -15,14 +14,10 @@ class LunchCashMove(models.Model):
date = fields.Date('Date', required=True, default=fields.Date.context_today)
amount = fields.Float('Amount', required=True, help='Can be positive (payment) or negative (order or payment if user wants to get his money back)')
description = fields.Text('Description', help='Can be an order or a payment')
- order_line_id = fields.Many2one('lunch.order.line', 'Order', ondelete='cascade')
+ order_id = fields.Many2one('lunch.order.line', 'Order', ondelete='cascade')
state = fields.Selection([('order', 'Order'), ('payment', 'Payment')],
'Is an order or a payment', default='payment')
@api.multi
def name_get(self):
return [(cashmove.id, '%s %s' % (_('Lunch Cashmove'), '#%d' % cashmove.id)) for cashmove in self]
-
- @api.model
- def get_wallet_balance(self, user):
- return float_round(sum(move['amount'] for move in self.search_read([('user_id', '=', user.id)], ['amount'])), precision_digits=2)
diff --git a/addons/lunch/models/lunch_order.py b/addons/lunch/models/lunch_order.py
index 623a9cada50..f4354913db8 100644
--- a/addons/lunch/models/lunch_order.py
+++ b/addons/lunch/models/lunch_order.py
@@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from collections import OrderedDict
+
+import json
+
from odoo import api, fields, models, _
-from odoo.exceptions import UserError, ValidationError
+from odoo.exceptions import AccessError, ValidationError
from odoo.addons import decimal_precision as dp
class LunchOrder(models.Model):
"""
A lunch order contains one or more lunch order line(s). It is associated to a user for a given
- date.
+ date. When creating a lunch order, applicable lunch alerts are displayed.
"""
_name = 'lunch.order'
_description = 'Lunch Order'
@@ -35,29 +39,24 @@ class LunchOrder(models.Model):
states={'new': [('readonly', False)], False: [('readonly', False)]})
total = fields.Float(compute='_compute_total', string="Total", store=True)
state = fields.Selection([('new', 'New'),
- ('ordered', 'Ordered'),
('confirmed', 'Received'),
('cancelled', 'Cancelled')],
- 'Status', readonly=True, index=True, copy=False, default='new')
+ 'Status', readonly=True, index=True, copy=False,
+ compute='_compute_order_state', store=True)
+ alerts = fields.Text(compute='_compute_alerts_get', string="Alerts")
company_id = fields.Many2one('res.company', related='user_id.company_id', store=True, readonly=False)
currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True, store=True)
cash_move_balance = fields.Monetary(compute='_compute_cash_move_balance', multi='cash_move_balance')
balance_visible = fields.Boolean(compute='_compute_cash_move_balance', multi='cash_move_balance')
+ previous_order_ids = fields.Many2many('lunch.order.line', compute='_compute_previous_order')
+ previous_order_widget = fields.Text(compute='_compute_previous_order')
- mail_sent = fields.Boolean('Mail Sent', readonly=True)
- supplier_ids = fields.Many2many('lunch.supplier', compute='_compute_supplier_ids', store=True)
-
- @api.depends('order_line_ids', 'order_line_ids.supplier_id', 'order_line_ids.product_id')
- def _compute_supplier_ids(self):
- for order in self:
- order.supplier_ids = order.mapped('order_line_ids.supplier_id')
-
- @api.depends('order_line_ids', 'order_line_ids.quantity', 'order_line_ids.price')
+ @api.one
+ @api.depends('order_line_ids')
def _compute_total(self):
"""
get and sum the order lines' price
"""
- self.ensure_one()
self.total = sum(
orderline.price for orderline in self.order_line_ids)
@@ -65,55 +64,96 @@ class LunchOrder(models.Model):
def name_get(self):
return [(order.id, '%s %s' % (_('Lunch Order'), '#%d' % order.id)) for order in self]
+ @api.depends('state')
+ def _compute_alerts_get(self):
+ """
+ get the alerts to display on the order form
+ """
+ alert_msg = [alert.message
+ for alert in self.env['lunch.alert'].search([])
+ if alert.display]
+
+ if self.state == 'new':
+ self.alerts = alert_msg and '\n'.join(alert_msg) or False
+
+ @api.multi
+ @api.depends('user_id', 'state')
+ def _compute_previous_order(self):
+ self.ensure_one()
+ self.previous_order_widget = json.dumps(False)
+
+ prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid), ('product_id.active', '!=', False)], limit=20, order='date desc, id desc')
+ # If we use prev_order.ids, we will have duplicates (identical orders).
+ # Therefore, this following part removes duplicates based on product_id and note.
+ self.previous_order_ids = list({
+ (order.product_id, order.note): order.id
+ for order in prev_order
+ }.values())
+
+ if self.previous_order_ids:
+ lunch_data = {}
+ for line in self.previous_order_ids:
+ lunch_data[line.id] = {
+ 'line_id': line.id,
+ 'product_id': line.product_id.id,
+ 'product_name': line.product_id.name,
+ 'supplier': line.supplier.name,
+ 'note': line.note,
+ 'price': line.price,
+ 'date': fields.Date.to_string(line.date),
+ 'currency_id': line.currency_id.id,
+ }
+ # sort the old lunch orders by (date, id)
+ lunch_data = OrderedDict(sorted(lunch_data.items(), key=lambda t: (t[1]['date'], t[0]), reverse=True))
+ self.previous_order_widget = json.dumps(lunch_data)
+
+ @api.one
@api.depends('user_id')
def _compute_cash_move_balance(self):
- self.ensure_one()
domain = [('user_id', '=', self.user_id.id)]
lunch_cash = self.env['lunch.cashmove'].read_group(domain, ['amount', 'user_id'], ['user_id'])
- if lunch_cash:
+ if len(lunch_cash):
self.cash_move_balance = lunch_cash[0]['amount']
self.balance_visible = (self.user_id == self.env.user) or self.user_has_groups('lunch.group_lunch_manager')
+ @api.one
@api.constrains('date')
def _check_date(self):
"""
Prevents the user to create an order in the past
"""
- self.ensure_one()
date_order = self.date
date_today = fields.Date.context_today(self)
if date_order < date_today:
raise ValidationError(_('The date of your order is in the past.'))
- def action_order(self):
- for order in self:
- for line in order.order_line_ids:
- self.env['lunch.cashmove'].sudo().create({
- 'user_id': line.user_id.id,
- 'amount': -line.price,
- 'description': line.product_id.name,
- 'order_line_id': line.id,
- 'state': 'order',
- 'date': line.date,
- })
-
- self.mapped('order_line_ids').write({'state': 'ordered'})
- self.write({'state': 'ordered'})
+ @api.one
+ @api.depends('order_line_ids.state')
+ def _compute_order_state(self):
+ """
+ Update the state of lunch.order based on its orderlines. Here is the logic:
+ - if at least one order line is cancelled, the order is set as cancelled
+ - if no line is cancelled but at least one line is not confirmed, the order is set as new
+ - if all lines are confirmed, the order is set as confirmed
+ """
+ if not self.order_line_ids:
+ self.state = 'new'
+ else:
+ isConfirmed = True
+ for orderline in self.order_line_ids:
+ if orderline.state == 'cancelled':
+ self.state = 'cancelled'
+ return
+ elif orderline.state == 'confirmed':
+ continue
+ else:
+ isConfirmed = False
- def action_confirm(self, supplier=None):
- for order in self:
- if supplier:
- order.order_line_ids.filtered(lambda line: line.state == 'ordered' and line.supplier_id == supplier).write({'state': 'confirmed'})
+ if isConfirmed:
+ self.state = 'confirmed'
else:
- order.order_line_ids.filtered(lambda line: line.state == 'ordered').write({'state': 'confirmed'})
- order.write({'state': 'confirmed',
- 'mail_sent': True})
-
- def action_cancel(self):
- for order in self:
- # RLI FIXME maybe we don't need to filter
- order.order_line_ids.filtered(lambda line: line.state != 'confirmed').action_cancel()
- order.write({'state': 'cancelled'})
+ self.state = 'new'
+ return
class LunchOrderLine(models.Model):
@@ -123,58 +163,153 @@ class LunchOrderLine(models.Model):
name = fields.Char(related='product_id.name', string="Product Name", readonly=True)
order_id = fields.Many2one('lunch.order', 'Order', ondelete='cascade', required=True)
- topping_ids = fields.Many2many('lunch.product', string='Toppings')
- product_id = fields.Many2one('lunch.product', string="Product", domain=[('is_topping', '=', False)], required=True)
+ product_id = fields.Many2one('lunch.product', 'Product', required=True,
+ domain=[('available', '=', True)])
category_id = fields.Many2one('lunch.product.category', string='Product Category',
related='product_id.category_id', readonly=True, store=True)
date = fields.Date(string='Date', related='order_id.date', readonly=True, store=True)
- supplier_id = fields.Many2one('lunch.supplier', string='Vendor', related='product_id.supplier_id',
- readonly=True, store=True, index=True)
+ supplier = fields.Many2one('res.partner', string='Vendor', related='product_id.supplier',
+ readonly=True, store=True)
user_id = fields.Many2one('res.users', string='User', related='order_id.user_id',
readonly=True, store=True)
note = fields.Text('Note')
- price = fields.Float('Total Price', compute='_compute_total_price', readonly=True, store=True,
+ price = fields.Float(related='product_id.price', readonly=True, store=True,
digits=dp.get_precision('Account'))
state = fields.Selection([('new', 'New'),
- ('ordered', 'Ordered'),
('confirmed', 'Received'),
+ ('ordered', 'Ordered'),
('cancelled', 'Cancelled')],
'Status', readonly=True, index=True, default='new')
- cashmove = fields.One2many('lunch.cashmove', 'order_line_id', 'Cash Move')
+ cashmove = fields.One2many('lunch.cashmove', 'order_id', 'Cash Move')
currency_id = fields.Many2one('res.currency', related='order_id.currency_id', readonly=False)
- quantity = fields.Float('Quantity', required=True, default=1)
-
- @api.depends('topping_ids', 'product_id', 'quantity')
- def _compute_total_price(self):
- for line in self:
- line.price = line.quantity * sum(product.price for product in line.product_id | line.topping_ids)
-
- def update_quantity(self, increment):
- for line in self:
- old_cost = line.price
- line.quantity += increment
- new_cost = line.price
- if new_cost - old_cost > self.env['lunch.cashmove'].get_wallet_balance(line.user_id):
- raise UserError(_('Your wallet does not contain enough money to order that.'
- 'To add some money to your wallet, please contact your lunch manager.'))
- cashmove = self.env['lunch.cashmove'].search([('order_line_id', '=', line.id)], limit=1)
- if cashmove:
- cashmove.amount = -new_cost
- if line.quantity <= 0:
- order_id = line.order_id
- line.sudo().unlink()
- if not order_id.order_line_ids:
- order_id.sudo().unlink()
-
- def action_confirm(self):
- self.write({'state': 'confirmed'})
-
- for order in self.mapped('order_id'):
- if all(line.state == 'confirmed' for line in order.order_line_ids):
- order.write({'state': 'confirmed', 'mail_sent': True})
-
- def action_cancel(self):
- for line in self:
- line.cashmove.sudo().unlink()
-
- self.write({'state': 'cancelled'})
+
+ def _check_supplier_availibility(self):
+ products = self.mapped('product_id')
+ if not all(product.available for product in products):
+ supplier_name = ", ".join(product.supplier.display_name for product in products if not product.available)
+ raise ValidationError(_("Vendor(s) '%s' is not available today") % supplier_name)
+
+ @api.model
+ def create(self, vals):
+ """ Override as an onchange would not apply if using the history buttons """
+ res = super(LunchOrderLine, self).create(vals)
+ res.with_context(lunch_date=res.order_id.date)._check_supplier_availibility()
+ return res
+
+ @api.model
+ def write(self, vals):
+ """ Override as an onchange would not apply if using the history buttons """
+ res = super(LunchOrderLine, self).write(vals)
+ if vals.get('product_id'):
+ for line in self:
+ line.with_context(lunch_date=line.order_id.date)._check_supplier_availibility()
+ return res
+
+ def order(self):
+ """
+ The order_line is ordered to the vendor but isn't received yet
+ """
+ if self.user_has_groups("lunch.group_lunch_manager"):
+ self.write({'state': 'ordered'})
+
+ order = {
+ 'supplier': False,
+ 'company': False,
+ 'currency': False,
+ }
+ group_lines = {}
+ for line in self:
+ if not line.supplier:
+ # do not send emails for products with no suppliers
+ continue
+
+ if order['supplier'] and line.supplier != order['supplier']:
+ raise ValidationError(_("Validate order for one supplier at a time to send emails (mixed orders from %s and %s)") % (
+ order['supplier'].display_name, line.supplier.display_name))
+ order['supplier'] = line.supplier
+
+ if order['company'] and line.order_id.company_id != order['company']:
+ raise ValidationError(_("Validate order for one company at a time to send emails (mixed orders from %s and %s)") % (
+ order['company'].name, line.order_id.company_id.name))
+ order['company'] = line.order_id.company_id
+
+ if order['currency'] and line.currency_id != order['currency']:
+ raise ValidationError(_("Validate order for one currency at a time to send emails (mixed orders from %s and %s)") % (
+ order['currency'].name, line.currency_id.name))
+ order['currency'] = line.currency_id
+
+ # group the order by products and note
+ key = (line.product_id, line.note)
+ group_lines.setdefault(key, 0)
+ group_lines[key] += 1
+
+ order['company_name'] = order['company'].name
+ order['currency_id'] = order['currency'].id
+ order['supplier_id'] = order['supplier'].id
+ order['supplier_name'] = order['supplier'].name
+ order['supplier_email'] = order['supplier'].email_formatted
+
+ lines = []
+ # sort by product name, note
+ for product, note in sorted(group_lines, key=lambda k: (k[0].name, bool(k[1]))):
+ quantity = group_lines[(product, note)]
+ lines.append({
+ 'product': product.name,
+ 'note': note or '',
+ 'quantity': quantity,
+ 'price': product.price * quantity,
+ })
+
+ order['amount_total'] = sum(l['price'] for l in lines)
+
+ template = self.env.ref('lunch.lunch_order_mail_supplier', raise_if_not_found=False)
+ ctx = dict(
+ default_composition_mode='mass_mail',
+ default_use_template=bool(template),
+ default_template_id=template.id,
+ default_lang=order['supplier'].lang or self.env.user.lang,
+ order=order,
+ lines=lines,
+ )
+ return {
+ 'type': 'ir.actions.act_window',
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'mail.compose.message',
+ 'target': 'new',
+ 'context': ctx,
+ }
+
+ else:
+ raise AccessError(_("Only your lunch manager processes the orders."))
+
+ @api.one
+ def confirm(self):
+ """
+ confirm one or more order line, update order status and create new cashmove
+ """
+ if self.user_has_groups("lunch.group_lunch_manager"):
+ if self.state != 'confirmed':
+ values = {
+ 'user_id': self.user_id.id,
+ 'amount': -self.price,
+ 'description': self.product_id.name,
+ 'order_id': self.id,
+ 'state': 'order',
+ 'date': self.date,
+ }
+ self.env['lunch.cashmove'].create(values)
+ self.state = 'confirmed'
+ else:
+ raise AccessError(_("Only your lunch manager sets the orders as received."))
+
+ @api.one
+ def cancel(self):
+ """
+ cancel one or more order.line, update order status and unlink existing cashmoves
+ """
+ if self.user_has_groups("lunch.group_lunch_manager"):
+ self.state = 'cancelled'
+ self.cashmove.unlink()
+ else:
+ raise AccessError(_("Only your lunch manager cancels the orders."))
diff --git a/addons/lunch/models/lunch_product.py b/addons/lunch/models/lunch_product.py
index 1751ea7a6e1..8ded108fc81 100644
--- a/addons/lunch/models/lunch_product.py
+++ b/addons/lunch/models/lunch_product.py
@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from dateutil.relativedelta import relativedelta
-
-from odoo import api, fields, models, tools, _
+from odoo import api, fields, models, _
from odoo.addons import decimal_precision as dp
+from odoo.osv import expression
class LunchProductCategory(models.Model):
@@ -21,52 +20,44 @@ class LunchProduct(models.Model):
_name = 'lunch.product'
_description = 'Lunch Product'
- name = fields.Char('Name', required=True)
- category_id = fields.Many2one('lunch.product.category', 'Product Category')
+ name = fields.Char('Product', required=True)
+ category_id = fields.Many2one('lunch.product.category', 'Product Category', required=True)
description = fields.Text('Description')
- price = fields.Float('Price', digits=dp.get_precision('Account'), required=True)
- supplier_id = fields.Many2one('lunch.supplier', 'Vendor', required=True)
+ price = fields.Float('Price', digits=dp.get_precision('Account'))
+ supplier = fields.Many2one('res.partner', 'Vendor')
active = fields.Boolean(default=True)
- is_topping = fields.Boolean("This product is an extra garniture")
-
- # image: all image fields are base64 encoded and PIL-supported
- image = fields.Binary(
- "Image", attachment=True,
- help="This field holds the image used as image for the product, limited to 1024x1024px.")
- image_medium = fields.Binary(
- "Medium-sized image", attachment=True,
- help="Medium-sized image of the product. It is automatically "
- "resized as a 128x128px image, with aspect ratio preserved, "
- "only when the image exceeds one of those sizes. Use this field in form views or some kanban views.")
- image_small = fields.Binary(
- "Small-sized image", attachment=True,
- help="Small-sized image of the product. It is automatically "
- "resized as a 64x64px image, with aspect ratio preserved. "
- "Use this field anywhere a small image is required.")
-
- already_ordered = fields.Boolean('Has Already Been Ordered', compute='_compute_already_ordered')
-
- @api.model
- def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
- res = super(LunchProduct, self).search_read(domain, fields, offset, limit, order)
-
- if not order and 'already_ordered' in fields:
- res = sorted(res, key=lambda x: x['already_ordered'], reverse=True)
-
- return res
-
- @api.model_create_multi
- def create(self, vals_list):
- for values in vals_list:
- tools.image_resize_images(values)
- return super(LunchProduct, self).create(vals_list)
-
- def write(self, values):
- tools.image_resize_images(values)
- return super(LunchProduct, self).write(values)
+ available = fields.Boolean(compute='_get_available_product', search='_search_available_products')
- def _compute_already_ordered(self):
- last_ordered = fields.Date.today() - relativedelta(weeks=2)
+ @api.depends('supplier')
+ def _get_available_product(self):
for product in self:
- product.already_ordered = bool(self.env['lunch.order.line'].search_count(
- [('product_id', '=', product.id), ('date', '>=', last_ordered), ('user_id', '=', self.env.user.id)]))
+ if not product.supplier:
+ product.available = True
+ else:
+ alerts = self.env['lunch.alert'].search([
+ ('partner_id', '=', self.supplier.id)
+ ])
+ if alerts and not any(alert.display for alert in alerts):
+ # every alert is not available
+ product.available = False
+ else:
+ # no alert for the supplier or at least one is not available
+ product.available = True
+
+ def _search_available_products(self, operator, value):
+ alerts = self.env['lunch.alert'].search([])
+ supplier_w_alerts = alerts.mapped('partner_id')
+ available_suppliers = alerts.filtered(lambda a: a.display).mapped('partner_id')
+ available_products = self.search([
+ '|',
+ ('supplier', 'not in', supplier_w_alerts.ids),
+ ('supplier', 'in', available_suppliers.ids)
+ ])
+
+ if (operator in expression.NEGATIVE_TERM_OPERATORS and value) or \
+ (operator not in expression.NEGATIVE_TERM_OPERATORS and not value):
+ # e.g. (available = False) or (available != True)
+ return [('id', 'not in', available_products.ids)]
+ else:
+ # e.g. (available = True) or (available != False)
+ return [('id', 'in', available_products.ids)]
diff --git a/addons/lunch/models/lunch_supplier.py b/addons/lunch/models/lunch_supplier.py
deleted file mode 100644
index 07286aa1ff0..00000000000
--- a/addons/lunch/models/lunch_supplier.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import math
-import pytz
-
-from datetime import datetime, time
-
-from odoo import api, fields, models
-from odoo.osv import expression
-from odoo.tools import float_round
-
-from odoo.addons.base.models.res_partner import _tz_get
-
-
-WEEKDAY_TO_NAME = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
-
-def float_to_time(hours, tz=None):
- """ Convert a number of hours into a time object. """
- if hours == 24.0:
- return time.max
- fractional, integral = math.modf(hours)
- res = time(int(integral), int(float_round(60 * fractional, precision_digits=0)), 0)
- if tz:
- res = res.replace(tzinfo=pytz.timezone(tz))
- return res
-
-def time_to_float(t):
- return float_round(t.hour + t.minute/60 + t.second/3600, precision_digits=2)
-
-class LunchSupplier(models.Model):
- _name = 'lunch.supplier'
- _description = 'Lunch Supplier'
-
- partner_id = fields.Many2one('res.partner', string='Linked partner', required=True)
-
- name = fields.Char('Name', related='partner_id.name')
- description = fields.Text('Description')
-
- email = fields.Char(related='partner_id.email')
- email_formatted = fields.Char(related='partner_id.email_formatted', readonly=True)
- phone = fields.Char(related='partner_id.phone')
- street = fields.Char(related='partner_id.street')
- street2 = fields.Char(related='partner_id.street2')
- zip_code = fields.Char(related='partner_id.zip')
- city = fields.Char(related='partner_id.city')
- state_id = fields.Many2one("res.country.state", related='partner_id.state_id')
- country_id = fields.Many2one('res.country', related='partner_id.country_id')
-
- vat = fields.Char(related='partner_id.vat')
-
- image = fields.Binary(related='partner_id.image', readonly=False)
- image_medium = fields.Binary(related='partner_id.image_medium', readonly=False)
- image_small = fields.Binary(related='partner_id.image_small', readonly=False)
-
- send_by = fields.Selection([
- ('phone', 'Phone'),
- ('mail', 'Email'),
- ], 'Send Order By', default='phone')
- automatic_email_send = fields.Boolean('Automatic Email Sending')
- automatic_email_time = fields.Float('Hour')
-
- recurrency = fields.Selection([('once', 'Specific Day'), ('reccurent', 'Reccurent')], 'Recurrency', default='once')
- recurrency_from = fields.Float('From')
- recurrency_to = fields.Float('To')
- recurrency_date = fields.Date('Day', default=fields.Date.today())
- recurrency_date_from = fields.Datetime('from', compute='_compute_recurrency_date_from', store=True)
- recurrency_date_to = fields.Datetime('to', compute='_compute_recurrency_date_to', store=True)
- recurrency_monday = fields.Boolean('Monday')
- recurrency_tuesday = fields.Boolean('Tuesday')
- recurrency_wednesday = fields.Boolean('Wednesday')
- recurrency_thursday = fields.Boolean('Thursday')
- recurrency_friday = fields.Boolean('Friday')
- recurrency_saturday = fields.Boolean('Saturday')
- recurrency_sunday = fields.Boolean('Sunday')
-
- available_today = fields.Boolean('This is True when if the supplier is available today',
- compute='_compute_available_today', search='_search_available_today')
-
- tz = fields.Selection(_tz_get, string='Timezone', required=True, default='UTC')
-
- _sql_constraints = [
- ('automatic_email_time_range', 'CHECK(automatic_email_time >= 0 AND automatic_email_time <= 24)', 'Automatic Email Sending Time should be between 0 and 24'),
- ('recurrency_from', 'CHECK(recurrency_from >= 0 AND recurrency_from <= 24)', 'Recurrency From should be between 0 and 24'),
- ('recurrency_to', 'CHECK(recurrency_to >= 0 AND recurrency_to <= 24)', 'Recurrency To should be between 0 and 24')
- ]
-
- @api.model
- def _auto_email_send(self):
- """
- This method is called every 10 minutes via a cron.
- Its job is simply to get all the orders made for each supplier and send an email
- automatically to the supplier if the supplier is configured for it and we are ready
- to send it (usually at 11am or so)
- """
- records = self.search([('automatic_email_send', '=', True)])
-
- for supplier in records:
- send_at = datetime.combine(fields.Date.today(), float_to_time(supplier.automatic_email_time, supplier.tz)).astimezone(pytz.UTC).replace(tzinfo=None)
- if supplier.available_today and fields.Datetime.now() > send_at:
- orders = self.env['lunch.order'].search([('mail_sent', '=', False), ('supplier_ids', 'in', [supplier.id]), ('state', '=', 'ordered')])
-
- if orders:
- order = {
- 'company_name': orders[0].company_id.name,
- 'currency_id': orders[0].currency_id.id,
- 'supplier_id': supplier.partner_id.id,
- 'supplier_name': supplier.name,
- 'supplier_email': supplier.email_formatted
- }
-
- lines = orders.mapped('order_line_ids').filtered(lambda line: line.supplier_id == supplier)
- lines = [{'product': line.product_id.name, 'note': line.note, 'quantity': line.quantity, 'price': line.price} for line in lines]
-
- order['amount_total'] = sum(line['price'] for line in lines)
-
- self.env.ref('lunch.lunch_order_mail_supplier').with_context(order=order, lines=lines).send_mail(supplier.id)
-
- orders.action_confirm(supplier)
-
- @api.depends('recurrency_date', 'recurrency_from')
- def _compute_recurrency_date_from(self):
- for supplier in self:
- if supplier.recurrency_date and supplier.recurrency_from:
- supplier.recurrency_date_from = datetime.combine(supplier.recurrency_date, float_to_time(supplier.recurrency_from))
-
- @api.depends('recurrency_date', 'recurrency_to')
- def _compute_recurrency_date_to(self):
- for supplier in self:
- if supplier.recurrency_date and supplier.recurrency_to:
- supplier.recurrency_date_to = datetime.combine(supplier.recurrency_date, float_to_time(supplier.recurrency_to))
-
- @api.depends('recurrency', 'recurrency_date', 'recurrency_from', 'recurrency_to', 'recurrency_monday',
- 'recurrency_tuesday', 'recurrency_wednesday', 'recurrency_thursday',
- 'recurrency_friday', 'recurrency_saturday', 'recurrency_sunday')
- def _compute_available_today(self):
- now = fields.Datetime.now().replace(tzinfo=pytz.UTC)
-
- for supplier in self:
- now = now.astimezone(pytz.timezone(supplier.tz))
- time_from = float_to_time(supplier.recurrency_from)
- time_to = float_to_time(supplier.recurrency_to)
-
- if supplier.recurrency == 'once':
- supplier.available_today = (supplier.reccurrency_date_from <= now <= supplier.reccurrency_date_to)
- else:
- fieldname = 'recurrency_%s' % (WEEKDAY_TO_NAME[now.weekday()])
- supplier.available_today = supplier[fieldname] and (time_from <= now.time() <= time_to)
-
- def _search_available_today(self, operator, value):
- if (not operator in ['=', '!=']) or (not value in [True, False]):
- return []
-
- searching_for_true = (operator == '=' and value) or (operator == '!=' and not value)
- now = fields.Datetime.now()
- float_now = time_to_float(now.time())
- fieldname = 'recurrency_%s' % (WEEKDAY_TO_NAME[now.weekday()])
-
- if searching_for_true:
- specific = expression.AND([
- [('recurrency', '=', 'once')],
- [('recurrency_date_from', '<=', now)],
- [('recurrency_date_to', '>=', now)]
- ])
- else:
- specific = expression.AND([
- [('recurrency', '=', 'once')],
- expression.OR([
- [('recurrency_date_from', '>=', now)],
- [('recurrency_date_to', '<=', now)]
- ])
- ])
-
- recurrence = expression.AND([
- [(fieldname, operator, value)],
- [('recurrency_from', '<=' if searching_for_true else '>=', float_now)],
- [('recurrency_to', '>=' if searching_for_true else '<=', float_now)]
- ])
-
- return expression.OR([specific, recurrence])
diff --git a/addons/lunch/security/ir.model.access.csv b/addons/lunch/security/ir.model.access.csv
index 1efb5be6c6d..244fbf11f18 100644
--- a/addons/lunch/security/ir.model.access.csv
+++ b/addons/lunch/security/ir.model.access.csv
@@ -4,10 +4,10 @@ order_line_manager,"Order Line user",model_lunch_order_line,group_lunch_manager,
cashmove_manager,"Cashmove user",model_lunch_cashmove,group_lunch_manager,1,1,1,1
product_manager,"Product user",model_lunch_product,group_lunch_manager,1,1,1,1
product_category_manager,"Product category user",model_lunch_product_category,group_lunch_manager,1,1,1,1
+alert_manager,"Alert user",model_lunch_alert,group_lunch_manager,1,1,1,1
order_user,"Order user",model_lunch_order,group_lunch_user,1,1,1,0
order_line_user,"Order Line user",model_lunch_order_line,group_lunch_user,1,1,1,1
cashmove_user,"Cashmove user",model_lunch_cashmove,group_lunch_user,1,0,0,0
product_user,"Product user",model_lunch_product,group_lunch_user,1,0,0,0
product_category_user,"Product category user",model_lunch_product_category,group_lunch_user,1,0,0,0
-lunch_supplier_user,"Lunch Supplier User Rights",model_lunch_supplier,group_lunch_user,1,0,0,0
-lunch_supplier_manager,"Lunch Supplier Manager Rights",model_lunch_supplier,group_lunch_manager,1,1,1,1
+alert_user,"Alert user",model_lunch_alert,group_lunch_user,1,0,0,0
diff --git a/addons/lunch/static/img/4formaggio.png b/addons/lunch/static/img/4formaggio.png
deleted file mode 100644
index f19c95ac934..00000000000
--- a/addons/lunch/static/img/4formaggio.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/Coke.png b/addons/lunch/static/img/Coke.png
deleted file mode 100644
index e822a2656ac..00000000000
--- a/addons/lunch/static/img/Coke.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/bacon_burger.png b/addons/lunch/static/img/bacon_burger.png
deleted file mode 100644
index ad4177d8ac2..00000000000
--- a/addons/lunch/static/img/bacon_burger.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/brie.png b/addons/lunch/static/img/brie.png
deleted file mode 100644
index 5b76bfbb1ee..00000000000
--- a/addons/lunch/static/img/brie.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/cheeseburger.png b/addons/lunch/static/img/cheeseburger.png
deleted file mode 100644
index f9d5a3e5c01..00000000000
--- a/addons/lunch/static/img/cheeseburger.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/chicken_curry.png b/addons/lunch/static/img/chicken_curry.png
deleted file mode 100644
index 09e494482d6..00000000000
--- a/addons/lunch/static/img/chicken_curry.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/chirashi.png b/addons/lunch/static/img/chirashi.png
deleted file mode 100644
index 65143711848..00000000000
--- a/addons/lunch/static/img/chirashi.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/club.png b/addons/lunch/static/img/club.png
deleted file mode 100644
index 5f3e36f3f37..00000000000
--- a/addons/lunch/static/img/club.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/coke_zero.png b/addons/lunch/static/img/coke_zero.png
deleted file mode 100644
index 72dbba5542b..00000000000
--- a/addons/lunch/static/img/coke_zero.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/fanta.png b/addons/lunch/static/img/fanta.png
deleted file mode 100644
index b7a48332e0d..00000000000
--- a/addons/lunch/static/img/fanta.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/fuze_black.png b/addons/lunch/static/img/fuze_black.png
deleted file mode 100644
index 1daac338d90..00000000000
--- a/addons/lunch/static/img/fuze_black.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/fuze_green.png b/addons/lunch/static/img/fuze_green.png
deleted file mode 100644
index 9a274ab6796..00000000000
--- a/addons/lunch/static/img/fuze_green.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/gouda.png b/addons/lunch/static/img/gouda.png
deleted file mode 100644
index c50d6d8dfbf..00000000000
--- a/addons/lunch/static/img/gouda.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/italiana.png b/addons/lunch/static/img/italiana.png
deleted file mode 100644
index 2f8c4244436..00000000000
--- a/addons/lunch/static/img/italiana.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/lipton.png b/addons/lunch/static/img/lipton.png
deleted file mode 100644
index a1f8ef21f2e..00000000000
--- a/addons/lunch/static/img/lipton.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/maki.png b/addons/lunch/static/img/maki.png
deleted file mode 100644
index c92dd8c83a4..00000000000
--- a/addons/lunch/static/img/maki.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/mozza.png b/addons/lunch/static/img/mozza.png
deleted file mode 100644
index 8820fbb9cd5..00000000000
--- a/addons/lunch/static/img/mozza.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/napoli.png b/addons/lunch/static/img/napoli.png
deleted file mode 100644
index c7ecd65db8a..00000000000
--- a/addons/lunch/static/img/napoli.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/pasta_bolognese.png b/addons/lunch/static/img/pasta_bolognese.png
deleted file mode 100644
index 64f9972e5c2..00000000000
--- a/addons/lunch/static/img/pasta_bolognese.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/pizza_funghi.png b/addons/lunch/static/img/pizza_funghi.png
deleted file mode 100644
index 00bceba96b6..00000000000
--- a/addons/lunch/static/img/pizza_funghi.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/pizza_margherita.png b/addons/lunch/static/img/pizza_margherita.png
deleted file mode 100644
index 0e317265524..00000000000
--- a/addons/lunch/static/img/pizza_margherita.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/pizza_veggie.png b/addons/lunch/static/img/pizza_veggie.png
deleted file mode 100644
index 950afb54a12..00000000000
--- a/addons/lunch/static/img/pizza_veggie.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/salmon_sushi.png b/addons/lunch/static/img/salmon_sushi.png
deleted file mode 100644
index 1467b54f120..00000000000
--- a/addons/lunch/static/img/salmon_sushi.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/temaki.png b/addons/lunch/static/img/temaki.png
deleted file mode 100644
index 8f47e7c035a..00000000000
--- a/addons/lunch/static/img/temaki.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/img/tuna_sandwich.png b/addons/lunch/static/img/tuna_sandwich.png
deleted file mode 100644
index b616d6156a5..00000000000
--- a/addons/lunch/static/img/tuna_sandwich.png
+++ /dev/null
Binary files differ
diff --git a/addons/lunch/static/src/js/lunch.js b/addons/lunch/static/src/js/lunch.js
new file mode 100644
index 00000000000..8c455083b0d
--- /dev/null
+++ b/addons/lunch/static/src/js/lunch.js
@@ -0,0 +1,94 @@
+odoo.define('lunch.previous_orders', function (require) {
+"use strict";
+
+var AbstractField = require('web.AbstractField');
+var core = require('web.core');
+var field_registry = require('web.field_registry');
+var field_utils = require('web.field_utils');
+
+var QWeb = core.qweb;
+
+var LunchPreviousOrdersWidget = AbstractField.extend({
+ events: {
+ 'click .o_add_button': '_onAddOrder',
+ },
+ supportedFieldTypes: ['one2many'],
+ /**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ this.lunchData = JSON.parse(this.value);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Used by the widget to render the previous order price like a monetary.
+ *
+ * @private
+ * @param {Object} order
+ * @returns {string} the monetary formatting of order price
+ */
+ _formatValue: function (order) {
+ var options = _.extend({}, this.nodeOptions, order);
+ return field_utils.format.monetary(order.price, this.field, options);
+ },
+ /**
+ * @private
+ * @override
+ */
+ _render: function () {
+ if (this.lunchData !== false) {
+ // group data by supplier for display
+ var categories = _.groupBy(this.lunchData, 'supplier');
+ this.$el.html(QWeb.render('LunchPreviousOrdersWidgetList', {
+ formatValue: this._formatValue.bind(this),
+ categories: categories,
+ }));
+ } else {
+ return this.$el.html(QWeb.render('LunchPreviousOrdersWidgetNoOrder'));
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onAddOrder: function (event) {
+ // Get order details from line
+ var lineID = parseInt($(event.currentTarget).data('id'));
+ if (!lineID) {
+ return;
+ }
+ var values = {
+ product_id: {
+ id: this.lunchData[lineID].product_id,
+ display_name: this.lunchData[lineID].product_name,
+ },
+ note: this.lunchData[lineID].note,
+ price: this.lunchData[lineID].price,
+ };
+
+ // create a new order line
+ this.trigger_up('field_changed', {
+ dataPointID: this.dataPointID,
+ changes: {
+ order_line_ids: {
+ operation: 'CREATE',
+ data: values,
+ },
+ },
+ });
+ },
+});
+
+field_registry.add('previous_order', LunchPreviousOrdersWidget);
+
+});
diff --git a/addons/lunch/static/src/js/lunch_kanban_controller.js b/addons/lunch/static/src/js/lunch_kanban_controller.js
deleted file mode 100644
index 5c9a0228f46..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_controller.js
+++ /dev/null
@@ -1,190 +0,0 @@
-odoo.define('lunch.LunchKanbanController', function (require) {
-"use strict";
-
-/**
- * This file defines the Controller for the Lunch Kanban view, which is an
- * override of the KanbanController.
- */
-
-var core = require('web.core');
-var KanbanController = require('web.KanbanController');
-var LunchKanbanWidget = require('lunch.LunchKanbanWidget');
-var LunchPaymentDialog = require('lunch.LunchPaymentDialog');
-var session = require('web.session');
-
-var qweb = core.qweb;
-var _t = core._t;
-
-var LunchKanbanController = KanbanController.extend({
- custom_events: _.extend({}, KanbanController.prototype.custom_events, {
- add_money: '_onAddMoney',
- add_product: '_onAddProduct',
- change_user: '_onUserChanged',
- edit_order: '_onEditOrder',
- open_wizard: '_onOpenWizard',
- order_now: '_onOrderNow',
- remove_product: '_onRemoveProduct',
- save_order: '_onSaveOrder',
- unlink_order: '_onUnlinkOrder',
- }),
-
- /**
- * @override
- */
- init: function () {
- this.userId = null;
- this.editMode = false;
- return this._super.apply(this, arguments);
- },
- start: function () {
- this.$el.addClass('o_lunch_kanban');
- return this._super.apply(this, arguments);
- },
-
- _fetchPaymentInfo: function (){
- return this._rpc({
- route: '/lunch/payment_message',
- });
- },
-
- _onAddMoney: function (ev) {
- var self = this;
-
- ev.stopPropagation();
-
- this._showPaymentDialog(_t('Your wallet is empty!'));
- },
- _onAddProduct: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- model: 'lunch.order.line',
- method: 'update_quantity',
- args: [[ev.data.lineId], 1],
- }).then(function () {
- self.reload();
- });
- },
- _onEditOrder: function (ev) {
- ev.stopPropagation();
-
- this.editMode = true;
- this.reload();
- },
- _onOpenWizard: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- var ctx = this.userId ? {default_user_id: this.userId}: {};
-
- var options = {
- on_close: function () {
- self.reload();
- },
- };
-
- this.do_action({
- res_model: 'lunch.order.line.temp',
- type: 'ir.actions.act_window',
- views: [[false, 'form']],
- target: 'new',
- context: _.extend(ctx, {default_product_id: ev.data.res_id}),
- }, options);
- },
- _onOrderNow: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- route: '/lunch/pay',
- params: {
- user_id: this.userId,
- },
- }).then(function (isPaid) {
- if (isPaid) {
- // TODO: feedback?
- self.reload();
- } else {
- self._showPaymentDialog();
- self.reload();
- }
- });
- },
- _onRemoveProduct: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- model: 'lunch.order.line',
- method: 'update_quantity',
- args: [[ev.data.lineId], -1],
- }).then(function () {
- self.reload();
- });
- },
- _onSaveOrder: function (ev) {
- ev.stopPropagation();
-
- this.editMode = false;
- this.reload();
- },
- _onUserChanged: function (ev) {
- ev.stopPropagation();
-
- this.userId = ev.data.userId;
- this.reload();
- },
- _onUnlinkOrder: function (ev) {
- var self = this;
- ev.stopPropagation();
-
- this._rpc({
- route: '/lunch/trash',
- params: {
- user_id: this.userId,
- },
- }).then(function () {
- self.reload();
- });
- },
- _orderPaid: function () {
- Dialog.alert(this, _t('Your order has been paid have a nice day'), {'title': 'Order Paid'});
- },
- _orderNotPaid: function () {
- this._showPaymentDialog();
- },
- _showPaymentDialog: function (title) {
- var self = this;
-
- title = title || '';
-
- this._fetchPaymentInfo().then(function (data) {
- var paymentDialog = new LunchPaymentDialog(self, _.extend(data, {title: title}));
- paymentDialog.open();
- });
- },
- _update: function () {
- var self = this;
-
- this._rpc({
- route: '/lunch/infos',
- params: {
- user_id: this.userId,
- },
- }).then(function (data) {
- if (self.widget) {
- self.widget.destroy();
- }
- data.wallet = parseFloat(data.wallet).toFixed(2);
- self.widget = new LunchKanbanWidget(self, _.extend(data, {edit: self.editMode}));
- self.widget.insertBefore(self.$('.o_kanban_view'));
- });
-
- return this._super.apply(this, arguments);
- },
-});
-
-return LunchKanbanController;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_model.js b/addons/lunch/static/src/js/lunch_kanban_model.js
deleted file mode 100644
index c28f5594b7a..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_model.js
+++ /dev/null
@@ -1,19 +0,0 @@
-odoo.define('lunch.LunchKanbanModel', function (require) {
-"use strict";
-
-/**
- * This file defines the Model for the Lunch Kanban view, which is an
- * override of the KanbanModel.
- */
-
-var KanbanModel = require('web.KanbanModel');
-var core = require('web.core');
-
-var _t = core._t;
-
-var LunchKanbanModel = KanbanModel.extend({
-});
-
-return LunchKanbanModel;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_record.js b/addons/lunch/static/src/js/lunch_kanban_record.js
deleted file mode 100644
index 45a928b892d..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_record.js
+++ /dev/null
@@ -1,22 +0,0 @@
-odoo.define('lunch.LunchKanbanRecord', function (require) {
-"use strict";
-
-/**
- * This file defines the KanbanRecord for the Lunch Kanban view.
- */
-
-var KanbanRecord = require('web.KanbanRecord');
-
-var LunchKanbanRecord = KanbanRecord.extend({
- _onGlobalClick: function (ev) {
- ev.preventDefault();
- // ignore clicks on oe_kanban_action elements
- if (!$(ev.target).hasClass('oe_kanban_action')) {
- this.trigger_up('open_wizard', {res_id: this.id});
- }
- },
-});
-
-return LunchKanbanRecord;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_renderer.js b/addons/lunch/static/src/js/lunch_kanban_renderer.js
deleted file mode 100644
index bffe639a655..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_renderer.js
+++ /dev/null
@@ -1,29 +0,0 @@
-odoo.define('lunch.LunchKanbanRenderer', function (require) {
-"use strict";
-
-/**
- * This file defines the Renderer for the Lunch Kanban view, which is an
- * override of the KanbanRenderer.
- */
-
-var LunchKanbanRecord = require('lunch.LunchKanbanRecord');
-
-var KanbanRenderer = require('web.KanbanRenderer');
-
-var LunchKanbanRenderer = KanbanRenderer.extend({
- config: _.extend({}, KanbanRenderer.prototype.config, {
- KanbanRecord: LunchKanbanRecord,
- }),
-
- /**
- * @override
- */
- start: function () {
- this.$el.addClass('o_lunch_kanban_view position-relative align-content-start flex-grow-1 flex-shrink-1');
- return this._super.apply(this, arguments);
- },
-});
-
-return LunchKanbanRenderer;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_view.js b/addons/lunch/static/src/js/lunch_kanban_view.js
deleted file mode 100644
index 296ebac08cd..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_view.js
+++ /dev/null
@@ -1,42 +0,0 @@
-odoo.define('lunch.LunchKanbanView', function (require) {
-"use strict";
-
-var LunchKanbanController = require('lunch.LunchKanbanController');
-var LunchKanbanModel = require('lunch.LunchKanbanModel');
-var LunchKanbanRenderer = require('lunch.LunchKanbanRenderer');
-
-var config = require('web.config');
-var core = require('web.core');
-var KanbanView = require('web.KanbanView');
-var view_registry = require('web.view_registry');
-
-var _lt = core._lt;
-
-if (config.device.isMobile) {
- // use the classical KanbanView in mobile
- view_registry.add('lunch_kanban', KanbanView);
- return;
-}
-
-var LunchKanbanView = KanbanView.extend({
- config: _.extend({}, KanbanView.prototype.config, {
- Controller: LunchKanbanController,
- Model: LunchKanbanModel,
- Renderer: LunchKanbanRenderer,
- }),
- display_name: _lt('Lunch Kanban'),
- groupable: false,
-
- /**
- * @override
- */
- init: function () {
- return this._super.apply(this, arguments);
- },
-});
-
-view_registry.add('lunch_kanban', LunchKanbanView);
-
-return LunchKanbanView;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_kanban_widget.js b/addons/lunch/static/src/js/lunch_kanban_widget.js
deleted file mode 100644
index cfc81ab8c47..00000000000
--- a/addons/lunch/static/src/js/lunch_kanban_widget.js
+++ /dev/null
@@ -1,87 +0,0 @@
-odoo.define('lunch.LunchKanbanWidget', function (require) {
-"use strict";
-
-var core = require('web.core');
-var session = require('web.session');
-var Widget = require('web.Widget');
-
-var qweb = core.qweb;
-
-var LunchKanbanWidget = Widget.extend({
- template: 'LunchKanbanWidget',
- custom_events: {
- },
- events: {
- 'click .o_add_money': '_onAddMoney',
- 'click .o_add_product': '_onAddProduct',
- 'click .o_lunch_widget_edit': '_onEditOrder',
- 'click .o_lunch_widget_order_button': '_onOrderNow',
- 'click .o_remove_product': '_onRemoveProduct',
- 'click .o_lunch_widget_save': '_onSaveOrder',
- 'click .o_lunch_widget_unlink': '_onUnlinkOrder',
- 'change select': '_onUserChanged',
- },
-
- init: function (parent, params) {
- var self = this;
- this._super.apply(this, arguments);
- this.data = params;
- this.currency = session.get_currency(session.company_currency_id);
- },
- renderElement: function () {
- var self = this;
- this._super.apply(this, arguments);
- this.$('select').val(this.data.username);
- },
-
- _onAddMoney: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
- this.trigger_up('add_money', {});
- },
- _onAddProduct: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('add_product', {lineId: $(ev.currentTarget).data('id')});
- },
- _onEditOrder: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('edit_order', {});
- },
- _onOrderNow: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('order_now', {});
- },
- _onRemoveProduct: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('remove_product', {lineId: $(ev.currentTarget).data('id')});
- },
- _onSaveOrder: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('save_order', {});
- },
- _onUnlinkOrder: function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
-
- this.trigger_up('unlink_order', {});
- },
- _onUserChanged: function (ev) {
- ev.stopPropagation();
-
- this.trigger_up('change_user', {userId: $(ev.currentTarget).find(':selected').data('user-id')});
- }
-});
-
-return LunchKanbanWidget;
-
-});
diff --git a/addons/lunch/static/src/js/lunch_payment_dialog.js b/addons/lunch/static/src/js/lunch_payment_dialog.js
deleted file mode 100644
index 00e49299189..00000000000
--- a/addons/lunch/static/src/js/lunch_payment_dialog.js
+++ /dev/null
@@ -1,24 +0,0 @@
-odoo.define('lunch.LunchPaymentDialog', function (require) {
-"use strict";
-
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-
-var qweb = core.qweb;
-
-var LunchPaymentDialog = Dialog.extend({
- template: 'lunch.LunchPaymentDialog',
-
- init: function (parent, options) {
- var self = this;
- this._super.apply(this, arguments);
-
- options = (options || {});
-
- this.message = options.message || '';
- },
-});
-
-return LunchPaymentDialog;
-
-});
diff --git a/addons/lunch/static/src/scss/lunch.scss b/addons/lunch/static/src/scss/lunch.scss
new file mode 100644
index 00000000000..9e36f20dbd8
--- /dev/null
+++ b/addons/lunch/static/src/scss/lunch.scss
@@ -0,0 +1,14 @@
+
+.o_web_client .o_lunch {
+ .o_lunch_last_orders {
+ width: 100%;
+
+ .o_lunch_vignette {
+ padding: 5px 0;
+
+ + .o_lunch_vignette {
+ border-top: 1px solid $o-brand-lightsecondary;
+ }
+ }
+ }
+}
diff --git a/addons/lunch/static/src/scss/lunch_kanban.scss b/addons/lunch/static/src/scss/lunch_kanban.scss
deleted file mode 100644
index 44bac01fa4b..00000000000
--- a/addons/lunch/static/src/scss/lunch_kanban.scss
+++ /dev/null
@@ -1,48 +0,0 @@
-.o_lunch_kanban {
- .o_pink {
- color: nth($o-colors, 10);
- }
-
- .o_lunch_widget {
- display: flex;
- overflow: auto;
- border-bottom: 1px solid #CED4DA;
- background-color: white;
- min-height: 90px;
- max-height: 90px;
-
- .o_lunch_widget_info {
- padding: 5px 12px;
- margin: 4px 8px;
- width: 300px;
- flex: 1 1 auto;
- overflow-y: auto;
-
- .o_lunch_ordered {
- color: white;
- background-color: #F0D970;
- padding: 1px 3px;
- border-radius: .25em;
- }
-
- .o_lunch_confirmed {
- color: white;
- background-color: #00BA4E;
- padding: 1px 3px;
- border-radius: .25em;
- }
-
- .o_lunch_widget_title {
- font-weight: bold;
- font-size: initial;
- }
-
- .o_lunch_widget_order_button {
- display: block;
- width: 100%;
- margin-bottom: 4px;
- margin-top: 12px;
- }
- }
- }
-}
diff --git a/addons/lunch/static/src/xml/lunch_kanban.xml b/addons/lunch/static/src/xml/lunch_kanban.xml
deleted file mode 100644
index 74a57f7cfec..00000000000
--- a/addons/lunch/static/src/xml/lunch_kanban.xml
+++ /dev/null
@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<templates>
- <div class="o_lunch_widget" t-name="LunchKanbanWidget">
- <div class="o_lunch_widget_info d-flex flex-row">
- <div>
- <img class="rounded-circle" t-attf-src="{{ widget.data.userimage }}"/>
- </div>
- <div class="w-50 ml-2 py-3">
- <div>
- <t t-if="widget.data.is_manager">
- <select class="o_input">
- <t t-foreach="widget.data.users" t-as="user">
- <option t-attf-data-user-id="{{ user.id }}" t-attf-value="{{ user.name }}"><t t-esc="user.name"/></option>
- </t>
- </select>
- </t>
- <t t-else="">
- <t t-esc="widget.data.username"/>
- </t>
- </div>
- <div class="d-flex flex-row">
- <span class="flex-grow-1">
- <i class="fa fa-money"/>
- <t t-call="currency_field">
- <t t-set="value" t-value="widget.data.wallet"/>
- <t t-set="currency" t-value="widget.currency"/>
- </t>
- </span>
- <span>
- <a class="o_add_money" href="#">Add money</a>
- </span>
- </div>
- </div>
- </div>
- <div class="o_lunch_widget_info">
- <t t-if="widget.data.order">
- <t t-if="widget.data.state == 'ordered'">
- <t t-set="state_class" t-value="'o_lunch_ordered'"/>
- </t>
- <t t-else="widget.data.state == 'confirmed'">
- <t t-set="state_class" t-value="'o_lunch_confirmed'"/>
- </t>
- <div class="d-flex flex-row">
- <div class="flex-grow-1">
- <span class="o_lunch_widget_title">Your order</span>
- <a href="#"><i t-if="widget.data.state != 'confirmed'" class="fa fa-trash o_lunch_widget_unlink"/></a>
- <a t-if="widget.data.state == 'ordered' &amp;&amp; !widget.data.edit" href="#" class="o_lunch_widget_edit">Edit</a>
- <a t-if="widget.data.state == 'ordered' &amp;&amp; widget.data.edit" href="#" class="o_lunch_widget_save">Save</a>
- </div>
- <div t-if="widget.data.state != 'new'" t-esc="widget.data.state" t-attf-class="flex-grow-0 {{ state_class }}"/>
- </div>
- <div t-foreach="widget.data.lines" t-as="line">
- <div class="d-flex flex-row">
- <div class="flex-grow-0">
- <a class="o_remove_product" t-if="widget.data.state == 'new' || widget.data.edit" t-attf-data-id="{{ line.id }}" href="#"><i class="fa fa-minus-circle"/></a>
- <span t-esc="line.quantity"/>
- <a class="o_add_product" t-if="widget.data.state == 'new' || widget.data.edit" t-attf-data-id="{{ line.id }}" href="#"><i class="fa fa-plus-circle"/></a>
- </div>
- <div class="flex-grow-1 pl-2">
- <span t-esc="line.product[0]"/>
- </div>
- <div class="flex-grow-0">
- <t t-call="currency_field">
- <t t-set="value" t-value="line.product[1]"/>
- <t t-set="currency" t-value="widget.currency"/>
- </t>
- </div>
- </div>
- <div t-foreach="line.toppings" t-as="topping" class="d-flex flex-row">
- <div class="flex-grow-1 pl-5 ml-5">
- <span>+ <t t-esc="topping[0]"/></span>
- </div>
- <div class="flex-grow-0">
- <t t-call="currency_field">
- <t t-set="value" t-value="topping[1]"/>
- <t t-set="currency" t-value="widget.currency"/>
- </t>
- </div>
- </div>
- </div>
- </t>
- </div>
- <div class="o_lunch_widget_info">
- <t t-if="widget.data.order &amp;&amp; widget.data.state == 'new'">
- <div class="d-flex flex-row">
- <div class="o_lunch_widget_title flex-grow-1">
- Total
- </div>
- <div class="flex-grow-0 pr-1">
- <t t-call="currency_field">
- <t t-set="value" t-value="widget.data.total"/>
- <t t-set="currency" t-value="widget.currency"/>
- </t>
- </div>
- </div>
- <button class="btn btn-primary o_lunch_widget_order_button" t-attf-data-order-id="{{ widget.data.order }}">ORDER NOW</button>
- </t>
- </div>
- </div>
-
- <t t-name="currency_field">
- <t t-if="currency">
- <t t-if="currency.position == 'after'">
- <t t-esc="value"/><t t-esc="currency.symbol"/>
- </t>
- <t t-else="">
- <t t-esc="currency.symbol"/><t t-esc="value"/>
- </t>
- </t>
- <t t-else="">
- <t t-esc="value"/>
- </t>
- </t>
-
- <div t-name="lunch.LunchPaymentDialog">
- <span t-esc="widget.message"/>
- </div>
-</templates>
diff --git a/addons/lunch/static/tests/lunch_tests.js b/addons/lunch/static/tests/lunch_tests.js
new file mode 100644
index 00000000000..3ae1e3491cb
--- /dev/null
+++ b/addons/lunch/static/tests/lunch_tests.js
@@ -0,0 +1,145 @@
+odoo.define('lunch.tests', function (require) {
+"use strict";
+
+var FormView = require('web.FormView');
+var testUtils = require('web.test_utils');
+
+var createView = testUtils.createView;
+
+QUnit.module('lunch', {
+ beforeEach: function () {
+ this.data = {
+ order: {
+ fields: {
+ previous_order_widget: {type: 'text'},
+ order_line_ids: {type: 'many2many', relation: 'order_line'}
+ },
+ records: [{
+ id: 1,
+ name: 'coucou',
+ }, {
+ id: 2,
+ name: 'coucou',
+ }, {
+ id: 11,
+ }],
+ },
+ order_line: {
+ fields: {
+ product_id: {type: 'many2one', relation: 'product'},
+ note: {type: 'text'},
+ price: {type: 'float'},
+ },
+ },
+ supplier: {
+ fields: {},
+ records: [{
+ id: 1,
+ display_name: "Coin Gourmant",
+ }, {
+ id: 2,
+ display_name: "Pizza Inn",
+ }]
+ },
+ product: {
+ fields: {},
+ records: [{
+ id: 1,
+ name: 'Pizza Margherita',
+ }],
+ }
+ };
+ },
+}, function () {
+
+ QUnit.test("empty previous_order widget", function (assert) {
+ assert.expect(2);
+
+ var form = createView({
+ View: FormView,
+ arch: '<form>' +
+ '<sheet>' +
+ '<field name="previous_order_widget" widget="previous_order"/>' +
+ '<field name="order_line_ids"/>' +
+ '</sheet>' +
+ '</form>',
+ data: this.data,
+ model: 'order',
+ archs: {
+ "order_line,false,list": '<tree string="Orders">' +
+ '<field name="product_id"/>' +
+ '<field name="note"/>' +
+ '<field name="price"/>' +
+ '</tree>',
+ },
+ });
+
+ var widgetText = form.$('.o_field_widget[name="previous_order_widget"]').text();
+ assert.ok(widgetText.indexOf('This is the first time you order a meal') !== -1,
+ "the widget should display its no content text");
+ assert.containsNone(form, '.o_lunch_vignette',
+ "there should be no vignette");
+
+ form.destroy();
+ });
+
+ QUnit.test("add an order line with previous_order widget", function (assert) {
+ assert.expect(4);
+
+ var form = createView({
+ View: FormView,
+ arch: '<form>' +
+ '<sheet>' +
+ '<field name="previous_order_widget" widget="previous_order"/>' +
+ '<field name="order_line_ids"/>' +
+ '</sheet>' +
+ '</form>',
+ data: this.data,
+ model: 'order',
+ mockRPC: function (route, args) {
+ if (args.method === 'default_get') {
+ return $.when({
+ previous_order_widget: '{' +
+ '"8": {' +
+ '"note": "coucou",' +
+ '"line_id": 8,' +
+ '"digits": [69, 2],' +
+ '"product_id": 1,' +
+ '"currency": "$",' +
+ '"supplier": "Pizza Inn",' +
+ '"position": "before",' +
+ '"price": 6.9,' +
+ '"product_name": "Pizza Margherita"' +
+ '}' +
+ '}'
+ });
+ }
+ return this._super.apply(this, arguments);
+ },
+ archs: {
+ "order_line,false,list": '<tree string="Orders">' +
+ '<field name="product_id"/>' +
+ '<field name="note"/>' +
+ '<field name="price"/>' +
+ '</tree>',
+ },
+ });
+
+ assert.containsNone(form, '.o_list_view tr.o_data_row',
+ "there should be no row in the one2many list");
+
+ assert.containsOnce(form, '.o_lunch_vignette',
+ "there should be a vignette");
+
+ // add an order
+ testUtils.dom.click(form.$('.o_lunch_vignette .o_add_button'));
+
+ assert.containsOnce(form, '.o_list_view tr.o_data_row',
+ "there should be one new row in the one2many list");
+ assert.strictEqual(form.$('.o_list_view tr.o_data_row').text(), "Pizza Margheritacoucou6.90",
+ "the line should contain the correct data");
+
+ form.destroy();
+ });
+});
+});
diff --git a/addons/lunch/static/tests/test_lunch_kanban.js b/addons/lunch/static/tests/test_lunch_kanban.js
deleted file mode 100644
index b2062609974..00000000000
--- a/addons/lunch/static/tests/test_lunch_kanban.js
+++ /dev/null
@@ -1,207 +0,0 @@
-odoo.define('lunch.lunchKanbanTests', function (require) {
-"use strict";
-
-var LunchKanbanView = require('lunch.LunchKanbanView');
-var testUtils = require('web.test_utils');
-
-var createView = testUtils.createView;
-
-QUnit.module('Views', {
- beforeEach: function () {
- this.data = {
- product: {
- fields: {
- id: {string: 'ID', type: 'integer'},
- },
- records: [
- {id: 1, name: 'Tuna sandwich', price: 3.0},
- ]
- },
- };
- },
-}, function () {
- QUnit.module('LunchKanbanView');
-
- QUnit.test('Simple rendering of LunchKanbanView', function (assert) {
- assert.expect(8);
-
- var lunchKanban = createView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: '<kanban><templates><t t-name="kanban-box">' +
- '<div>' +
- '</div>' +
- '</t></templates></kanban>',
- mockRPC: function(route, args) {
- if (route === '/lunch/infos') {
- return $.when({
- order: 1,
- wallet: 20,
- username: 'Marc Demo',
- userimage: '',
- is_manager: false,
- users: [
- {id: 1, name: 'Mitchell Admin'},
- {id: 2, name: 'Marc Demo'},
- ],
- total: 7.4,
- state: 'new',
- lines: [
- {id: 1, product: ['Pizza Italiana', 7.4], toppings: [], quantity: 1.0, price: 7.4}
- ]
- });
- } else if (route.startsWith('data:image/png;base64,')) {
- return $.when();
- }
- return this._super(route, args);
- },
- });
-
- var $section = $(lunchKanban.$('.o_lunch_widget_info')[0]);
- // username
- assert.strictEqual($section.find('div:eq(1) div:eq(0)').text().trim(), 'Marc Demo', 'Username should have been Marc Demo');
- // wallet_balance
- assert.strictEqual($section.find('div:eq(1) div:eq(1) span:eq(0)').text().trim(), '20.00', 'Wallet balance should have been 20');
- // your order section
- $section = $(lunchKanban.$('.o_lunch_widget_info')[1]);
- // only one line
- assert.strictEqual($section.find('div:eq(2) div.d-flex').length, 1, 'There should be only one line');
- // quantity = 1
- assert.strictEqual($section.find('div:eq(2) div:eq(0) span:eq(0)').text().trim(), '1', 'The line should contain only one product');
- // buttons to remove and to add product
- assert.strictEqual($section.find('.o_remove_product').length, 1, 'There should be a remove product button');
- assert.strictEqual($section.find('.o_add_product').length, 1, 'There should be a add product button');
-
- $section = $(lunchKanban.$('.o_lunch_widget_info')[2]);
- // total
- assert.strictEqual($section.find('div:eq(0) div:eq(1)').text().trim(), '7.4', 'total should be of 7.4');
- // order now button
- assert.strictEqual($section.find('button').length, 1, 'order now button should be available');
-
- lunchKanban.destroy();
- });
-
- QUnit.test('User interactions', function (assert) {
- assert.expect(8);
-
- var state = 'new';
-
- var lunchKanban = createView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: '<kanban><templates><t t-name="kanban-box">' +
- '<div>' +
- '</div>' +
- '</t></templates></kanban>',
- mockRPC: function(route, args) {
- if (route === '/lunch/infos') {
- return $.when({
- order: 1,
- wallet: 20,
- username: 'Marc Demo',
- userimage: '',
- is_manager: false,
- users: [
- {id: 1, name: 'Mitchell Admin'},
- {id: 2, name: 'Marc Demo'},
- ],
- total: 7.4,
- state: state,
- lines: [
- {id: 1, product: ['Pizza Italiana', 7.4], toppings: [], quantity: 1.0, price: 7.4}
- ]
- });
- } else if (route === '/lunch/payment_message') {
- return $.when({message: 'Hello'});
- } else if (route === '/lunch/pay') {
- return $.when(true);
- } else if (args.method === 'update_quantity') {
- assert.step(args.args);
- return $.when();
- } else if (route.startsWith('data:image/png;base64,')) {
- return $.when();
- }
- return this._super(route, args);
- },
- });
-
- lunchKanban.$('.o_add_money').click();
- var modal = $('.modal-dialog');
- assert.strictEqual(modal.find('.modal-body').text().trim(), 'Hello', 'Message should have been Hello');
- modal.find('.btn.btn-primary').click();
-
- lunchKanban.$('.o_add_product').click();
- lunchKanban.$('.o_remove_product').click();
-
- state = 'ordered';
- lunchKanban.$('.o_lunch_widget_order_button').click();
- // state is shown
- assert.strictEqual(lunchKanban.$('.o_lunch_ordered').length, 1, 'state should be shown as ordered');
- // no more buttons
- assert.strictEqual(lunchKanban.$('.o_remove_product').length, 0, 'button to remove product should not be shown anymore');
- assert.strictEqual(lunchKanban.$('.o_add_product').length, 0, 'button to add product should not be shown anymore');
- state = 'confirmed';
- lunchKanban.reload();
- // state is updated
- assert.strictEqual(lunchKanban.$('.o_lunch_confirmed').length, 1, 'state should be shown as confirmed');
-
- assert.verifySteps([
- [[1], 1],
- [[1], -1],
- ]);
-
- lunchKanban.destroy();
- });
-
- QUnit.test('Manager interactions', function (assert) {
- assert.expect(5);
-
- var lunchKanban = createView({
- View: LunchKanbanView,
- model: 'product',
- data: this.data,
- arch: '<kanban><templates><t t-name="kanban-box">' +
- '<div>' +
- '</div>' +
- '</t></templates></kanban>',
- mockRPC: function(route, args) {
- if (route === '/lunch/infos') {
- return $.when({
- order: 1,
- wallet: 20,
- username: 'Marc Demo',
- userimage: '',
- is_manager: true,
- users: [
- {id: 1, name: 'Mitchell Admin'},
- {id: 2, name: 'Marc Demo'},
- ],
- total: 7.4,
- state: 'new',
- lines: [
- {id: 1, product: ['Pizza Italiana', 7.4], toppings: [], quantity: 1.0, price: 7.4}
- ]
- });
- } else if (route.startsWith('data:image/png;base64,')) {
- return $.when();
- }
- return this._super(route, args);
- },
- });
-
- var select = lunchKanban.$('select.o_input');
- var options = select.find('option');
-
- assert.strictEqual(select.length, 1, 'There should be a selection field');
- assert.strictEqual(options.length, 2, 'Select should have two options');
- assert.strictEqual(options.text().trim(), 'Mitchell AdminMarc Demo', 'Options should be the name of the users');
- assert.strictEqual($(options[0]).data('user-id'), 1, 'user id should be on option');
- assert.strictEqual($(options[1]).data('user-id'), 2, 'user id should be on option');
-
- lunchKanban.destroy();
- });
-});
-
-});
diff --git a/addons/lunch/tests/__init__.py b/addons/lunch/tests/__init__.py
index 08959c769be..f9d697d7b2c 100644
--- a/addons/lunch/tests/__init__.py
+++ b/addons/lunch/tests/__init__.py
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
-from . import common
-from . import test_supplier
+from . import test_lunch
diff --git a/addons/lunch/tests/common.py b/addons/lunch/tests/common.py
deleted file mode 100644
index f38b9c547ec..00000000000
--- a/addons/lunch/tests/common.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo.tests import common
-
-
-class TestsCommon(common.TransactionCase):
-
- def setUp(self):
- super(TestsCommon, self).setUp()
-
- self.partner_pizza_inn = self.env['res.partner'].create({
- 'name': 'Pizza Inn',
- })
-
- self.supplier_pizza_inn = self.env['lunch.supplier'].create({
- 'partner_id': self.partner_pizza_inn.id,
- 'send_by': 'mail',
- 'automatic_email_send': True,
- 'automatic_email_time': 11,
- 'recurrency': 'reccurent',
- 'recurrency_from': 8,
- 'recurrency_to': 23,
- 'recurrency_monday': True,
- 'recurrency_tuesday': True,
- 'recurrency_wednesday': True,
- 'recurrency_thursday': True,
- 'recurrency_friday': True,
- })
-
- self.partner_coin_gourmand = self.env['res.partner'].create({
- 'name': 'Coin Gourmand',
- })
-
- self.supplier_coin_gourmand = self.env['lunch.supplier'].create({
- 'partner_id': self.partner_coin_gourmand.id,
- 'send_by': 'phone',
- 'recurrency': 'reccurent',
- 'recurrency_from': 8,
- 'recurrency_to': 23,
- 'recurrency_monday': True,
- 'recurrency_tuesday': True,
- 'recurrency_wednesday': True,
- 'recurrency_thursday': True,
- 'recurrency_friday': True,
- })
-
- self.category_pizza = self.env['lunch.product.category'].create({
- 'name': 'Pizza',
- })
-
- self.category_sandwich = self.env['lunch.product.category'].create({
- 'name': 'Sandwich',
- })
-
- self.product_pizza = self.env['lunch.product'].create({
- 'name': 'Pizza',
- 'category_id': self.category_pizza.id,
- 'price': 9,
- 'supplier_id': self.supplier_pizza_inn.id,
- })
-
- self.product_sandwich_tuna = self.env['lunch.product'].create({
- 'name': 'Tuna Sandwich',
- 'category_id': self.category_sandwich.id,
- 'price': 3,
- 'supplier_id': self.supplier_coin_gourmand.id,
- })
-
- self.topping_olives = self.env['lunch.product'].create({
- 'name': 'Olives',
- 'price': 0.3,
- 'supplier_id': self.supplier_pizza_inn.id,
- 'is_topping': True,
- })
diff --git a/addons/lunch/tests/test_lunch.py b/addons/lunch/tests/test_lunch.py
new file mode 100644
index 00000000000..c6325a3f19c
--- /dev/null
+++ b/addons/lunch/tests/test_lunch.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.exceptions import ValidationError
+from odoo.tests import common
+
+from datetime import date, datetime
+from dateutil import relativedelta
+
+class Test_Lunch(common.TransactionCase):
+
+ def setUp(self):
+ """*****setUp*****"""
+ super(Test_Lunch, self).setUp()
+
+ self.demo_user = self.env['res.users'].search([('name', '=', 'Marc Demo')])
+ self.product_Bolognese_id = self.env.ref('lunch.product_Bolognese')
+ self.new_id_order = self.env['lunch.order'].create({
+ 'user_id': self.demo_user.id,
+ 'order_line_ids': '[]',
+ })
+ self.new_id_order_line = self.env['lunch.order.line'].create({
+ 'order_id': self.new_id_order.id,
+ 'product_id': self.product_Bolognese_id.id,
+ 'note': '+Emmental',
+ 'cashmove': [],
+ 'price': self.product_Bolognese_id.price,
+ })
+
+ def test_00_lunch_order(self):
+ """Change the state of an order line from 'new' to 'ordered'. Check that there are no cashmove linked to that order line"""
+ self.order_one = self.new_id_order_line
+ #we check that our order_line is a 'new' one and that there are no cashmove linked to that order_line:
+ self.assertEqual(self.order_one.state, 'new')
+ self.assertEqual(list(self.order_one.cashmove), [])
+ #we order that orderline so it's state will be 'ordered'
+ self.order_one.order()
+ self.order_one = self.new_id_order_line
+ #we check that our order_line is a 'ordered' one and that there are no cashmove linked to that order_line:
+ self.assertEqual(self.order_one.state, 'ordered')
+ self.assertEqual(list(self.order_one.cashmove), [])
+
+ def test_01_lunch_order(self):
+ """Change the state of an order line from 'new' to 'ordered' then to 'confirmed'. Check that there is a cashmove linked to the order line"""
+ self.test_00_lunch_order()
+ #We receive the order so we confirm the order line so it's state will be 'confirmed'
+ #A cashmove will be created and we will test that the cashmove amount equals the order line price
+ self.order_one.confirm()
+ self.order_one = self.new_id_order_line
+ #we check that our order_line is a 'confirmed' one and that there are a cashmove linked to that order_line with an amount equals to the order line price:
+ self.assertEqual(self.order_one.state, 'confirmed')
+ self.assertTrue(self.order_one.cashmove)
+ self.assertTrue(self.order_one.cashmove[0].amount == -self.order_one.price)
+
+ def test_02_lunch_order(self):
+ """Change the state of an order line from 'confirmed' to 'cancelled' and check that the cashmove linked to that order line will be deleted"""
+ self.test_01_lunch_order()
+ #We have a confirmed order with its associate cashmove
+ #We execute the cancel function
+ self.order_one.cancel()
+ self.order_one = self.new_id_order_line
+ #We check that the state is cancelled and that the cashmove has been deleted
+ self.assertEqual(self.order_one.state, 'cancelled')
+ self.assertFalse(self.order_one.cashmove)
+
+ def test_03_lunch_alert(self):
+ """ specify a lunch alert and make sure it is not possible to make an
+ order that day
+ """
+ alert_01 = self.env['lunch.alert'].create({
+ 'message': 'Order Bolognese only on Tuesday',
+ 'partner_id': self.product_Bolognese_id.supplier.id,
+ 'alert_type': 'week',
+ 'tuesday': True
+ })
+ wednesday = date.today() + relativedelta.relativedelta(days=7, weekday=2)
+ next_tuesday = datetime.today() + relativedelta.relativedelta(days=14, weekday=1)
+ next_wednesday = datetime.today() + relativedelta.relativedelta(days=14, weekday=2)
+
+ # We check that we can make an order on a Tuesday
+ order_01 = self.env['lunch.order'].create({
+ 'user_id': self.demo_user.id,
+ 'date': date.today() + relativedelta.relativedelta(days=7, weekday=1),
+ 'order_line_ids': [(0, 0, {
+ 'product_id': self.product_Bolognese_id.id,
+ })]
+ })
+ order_01.order_line_ids.order()
+
+ with self.assertRaises(ValidationError):
+ # Planning an order to an non-authorized day must not be possible
+ order_02 = self.env['lunch.order'].create({
+ 'user_id': self.demo_user.id,
+ 'date': wednesday,
+ 'order_line_ids': [(0, 0, {
+ 'product_id': self.product_Bolognese_id.id,
+ })]
+ })
+
+ alert_02 = self.env['lunch.alert'].create({
+ 'message': 'Working one more day',
+ 'partner_id': self.product_Bolognese_id.supplier.id,
+ 'alert_type': 'specific',
+ 'specific_day': wednesday,
+ })
+
+ # should now be possible to order tomorrow
+ order_03 = self.env['lunch.order'].create({
+ 'user_id': self.demo_user.id,
+ 'date': wednesday,
+ 'order_line_ids': [(0, 0, {
+ 'product_id': self.product_Bolognese_id.id,
+ })]
+ })
+
+ # should be possible to planned on an allowed day
+ order_04 = self.env['lunch.order'].create({
+ 'user_id': self.demo_user.id,
+ 'date': next_tuesday,
+ 'order_line_ids': [(0, 0, {
+ 'product_id': self.product_Bolognese_id.id,
+ })]
+ })
+ order_04.order_line_ids.order()
+
+ # combinaision of alerts should work
+ alert_03 = self.env['lunch.alert'].create({
+ 'message': 'Order Bolognese also on Wednesday',
+ 'partner_id': self.product_Bolognese_id.supplier.id,
+ 'alert_type': 'week',
+ 'wednesday': True
+ })
+ # Planning an order Wednesday must now be possible
+ order_05 = self.env['lunch.order'].create({
+ 'user_id': self.demo_user.id,
+ 'date': next_wednesday,
+ 'order_line_ids': [(0, 0, {
+ 'product_id': self.product_Bolognese_id.id,
+ })]
+ })
+ order_05.order_line_ids.order()
+
+
diff --git a/addons/lunch/tests/test_supplier.py b/addons/lunch/tests/test_supplier.py
deleted file mode 100644
index 55c57c8e744..00000000000
--- a/addons/lunch/tests/test_supplier.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from datetime import datetime
-from unittest.mock import patch
-
-from odoo import fields
-
-from odoo.addons.lunch.tests.common import TestsCommon
-
-
-class TestSupplier(TestsCommon):
- def setUp(self):
- super(TestSupplier, self).setUp()
-
- self.monday_3am = datetime(2018, 10, 29, 3, 0, 0)
- self.monday_10am = datetime(2018, 10, 29, 10, 0, 0)
- self.monday_1pm = datetime(2018, 10, 29, 13, 0, 0)
- self.monday_8pm = datetime(2018, 10, 29, 20, 0, 0)
-
- self.saturday_3am = datetime(2018, 11, 3, 3, 0, 0)
- self.saturday_10am = datetime(2018, 11, 3, 10, 0, 0)
- self.saturday_1pm = datetime(2018, 11, 3, 13, 0, 0)
- self.saturday_8pm = datetime(2018, 11, 3, 20, 0, 0)
-
- def test_compute_available_today(self):
- tests = [(self.monday_3am, False), (self.monday_10am, True),
- (self.monday_1pm, True), (self.monday_8pm, True),
- (self.saturday_3am, False), (self.saturday_10am, False),
- (self.saturday_1pm, False), (self.saturday_8pm, False)]
-
- for value, result in tests:
- with patch.object(fields.Datetime, 'now', return_value=value) as _:
- assert self.supplier_pizza_inn.available_today == result,\
- 'supplier pizza inn should %s considered available on %s' % ('be' if result else 'not be', value)
-
- self.env['lunch.supplier'].invalidate_cache(['available_today'], [self.supplier_pizza_inn.id])
-
- def test_search_available_today(self):
- Supplier = self.env['lunch.supplier']
-
- tests = [(self.monday_3am, 3.0, 'monday'), (self.monday_10am, 10.0, 'monday'),
- (self.monday_1pm, 13.0, 'monday'), (self.monday_8pm, 20.0, 'monday'),
- (self.saturday_3am, 3.0, 'saturday'), (self.saturday_10am, 10.0, 'saturday'),
- (self.saturday_1pm, 13.0, 'saturday'), (self.saturday_8pm, 20.0, 'saturday')]
-
- assert Supplier._search_available_today('>', 7) == []
- assert Supplier._search_available_today('>', True) == []
-
- for value, rvalue, dayname in tests:
- with patch.object(fields.Datetime, 'now', return_value=value) as _:
- assert Supplier._search_available_today('=', True) == ['|', '&', '&', ('recurrency', '=', 'once'), ('recurrency_date_from', '<=', value),
- ('recurrency_date_to', '>=', value), '&', '&', ('recurrency_%s' % (dayname), '=', True),
- ('recurrency_from', '<=', rvalue), ('recurrency_to', '>=', rvalue)], 'Wrong domain generated for values (%s, %s)' % (value, rvalue)
-
- assert self.supplier_pizza_inn in Supplier.search([('available_today', '=', True)])
-
- def test_auto_email_send(self):
- order = self.env['lunch.order'].create({})
- line = self.env['lunch.order.line'].create({
- 'order_id': order.id,
- 'product_id': self.product_pizza.id,
- })
-
- assert len(order.supplier_ids) == 1 and self.supplier_pizza_inn in order.supplier_ids
-
- order.action_order()
- assert order.state == 'ordered'
- assert line.state == 'ordered'
-
- with patch.object(fields.Datetime, 'now', return_value=self.monday_1pm):
- with patch.object(fields.Date, 'today', return_value=self.monday_1pm.date()):
- self.supplier_pizza_inn._auto_email_send()
-
- assert order.state == 'confirmed'
- assert line.state == 'confirmed'
- assert order.mail_sent
-
- # Do the same but this time order contains multiple lines with products from at least two suppliers
- # Check if the lines for pizza_inn have been sent, and put in state='confirmed' only those lines should be
- # moreover the order should not yet be in state='confirmed' as there still are some lines to confirm
- order = self.env['lunch.order'].create({})
- line = self.env['lunch.order.line'].create({
- 'order_id': order.id,
- 'product_id': self.product_pizza.id,
- 'topping_ids': [(6, 0, [self.topping_olives.id])],
- })
- line2 = self.env['lunch.order.line'].create({
- 'order_id': order.id,
- 'product_id': self.product_sandwich_tuna.id,
- })
-
- assert len(order.supplier_ids) == 2 and self.supplier_coin_gourmand in order.supplier_ids and self.supplier_pizza_inn in order.supplier_ids
-
- order.action_order()
- assert order.state == 'ordered'
- assert line.state == 'ordered'
- assert line2.state == 'ordered'
-
- with patch.object(fields.Datetime, 'now', return_value=self.monday_1pm):
- with patch.object(fields.Date, 'today', return_value=self.monday_1pm.date()):
- self.supplier_pizza_inn._auto_email_send()
-
- assert order.state == 'confirmed'
- assert line.state == 'confirmed'
- assert line2.state == 'ordered'
-
- assert order.mail_sent
-
- # Check with multiple orders
- order_1 = self.env['lunch.order'].create({})
-
- line_1_1 = self.env['lunch.order.line'].create({
- 'order_id': order_1.id,
- 'product_id': self.product_pizza.id,
- })
-
- line_1_2 = line_1_1 = self.env['lunch.order.line'].create({
- 'order_id': order_1.id,
- 'product_id': self.product_pizza.id,
- 'topping_ids': [(6, 0, [self.topping_olives.id])]
- })
-
- order_2 = self.env['lunch.order'].create({})
-
- line_2_1 = self.env['lunch.order.line'].create({
- 'order_id': order_2.id,
- 'product_id': self.product_sandwich_tuna.id
- })
-
- line_2_2 = self.env['lunch.order.line'].create({
- 'order_id': order_2.id,
- 'product_id': self.product_pizza.id,
- })
-
- order_3 = self.env['lunch.order'].create({})
-
- line_3_1 = self.env['lunch.order.line'].create({
- 'order_id': order_3.id,
- 'product_id': self.product_sandwich_tuna.id
- })
-
- (order_1 | order_2 | order_3).action_order()
-
- assert all(order.state == 'ordered' for order in [order_1, order_2, order_3])
- assert all(line.state == 'ordered' for line in [line_1_1, line_1_2, line_2_1, line_2_2, line_3_1])
-
- with patch.object(fields.Datetime, 'now', return_value=self.monday_1pm):
- with patch.object(fields.Date, 'today', return_value=self.monday_1pm.date()):
- self.supplier_pizza_inn._auto_email_send()
-
- assert all(order.state == 'confirmed' for order in [order_1, order_2])
- assert order_3.state == 'ordered'
- assert all(line.state == 'confirmed' for line in [line_1_1, line_1_2, line_2_2])
- assert all(line.state == 'ordered' for line in [line_2_1, line_3_1])
-
- assert all(order.mail_sent for order in [order_1, order_2])
- assert not order_3.mail_sent
diff --git a/addons/lunch/views/lunch_alert_views.xml b/addons/lunch/views/lunch_alert_views.xml
new file mode 100644
index 00000000000..bb6d807a9ff
--- /dev/null
+++ b/addons/lunch/views/lunch_alert_views.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="lunch_alert_view_search" model="ir.ui.view">
+ <field name="name">lunch.alert.search</field>
+ <field name="model">lunch.alert</field>
+ <field name="arch" type="xml">
+ <search string="Search">
+ <field name="message"/>
+ <filter name="inactive" string="Archived" domain="[('active','=',False)]"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="lunch_alert_view_tree" model="ir.ui.view">
+ <field name="name">lunch.alert.tree</field>
+ <field name="model">lunch.alert</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="message"/>
+ <field name="alert_type"/>
+ <field name='start_hour' widget='float_time'/>
+ <field name='end_hour' widget='float_time'/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="lunch_alert_view_form" model="ir.ui.view">
+ <field name="name">lunch.alert.form</field>
+ <field name="model">lunch.alert</field>
+ <field name="arch" type="xml">
+ <form>
+ <sheet>
+ <div class="oe_button_box" name="button_box">
+ <button name="toggle_active" type="object"
+ class="oe_stat_button" icon="fa-archive">
+ <field name="active" widget="boolean_button"
+ options='{"terminology": "archive"}'/>
+ </button>
+ </div>
+ <group>
+ <group name="schedule" string="Schedule">
+ <group colspan="4">
+ <field name="alert_type"/>
+ <field name="specific_day"
+ attrs="{'invisible': [('alert_type','!=','specific')], 'required':[('alert_type','=','specific')]}"/>
+ <field name='start_hour' widget='float_time' />
+ <field name='end_hour' widget='float_time' />
+ </group>
+ <group name="weekdays" col="4" attrs="{'invisible': [('alert_type','!=','week')]}">
+ <field name="monday"/>
+ <field name="tuesday"/>
+ <field name="wednesday"/>
+ <field name="thursday"/>
+ <field name="friday"/>
+ <field name="saturday"/>
+ <field name="sunday"/>
+ </group>
+ </group>
+ <group name="vendor" string="Vendor">
+ <group name="partner" colspan="4">
+ <field name="partner_id"/>
+ <field name="display" invisible="True"/>
+ </group>
+ <div class="oe_grey" colspan="4"
+ attrs="{'invisible': ['|', ('partner_id', '=', False), ('display', '=', True)]}">
+ Products from this vendor can <strong>not</strong> be ordered at that moment.
+ </div>
+ <div class="oe_grey" colspan="4"
+ attrs="{'invisible': ['|', ('partner_id', '=', False), ('display', '=', False)]}">
+ Products from this vendor can be ordered at that moment.
+ </div>
+ </group>
+ </group>
+ <group string='Message'>
+ <field name='message' nolabel='1' placeholder="Write the message you want to display during the defined period..."/>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="view_lunch_alert_kanban" model="ir.ui.view">
+ <field name="name">lunch.alert.kanban</field>
+ <field name="model">lunch.alert</field>
+ <field name="arch" type="xml">
+ <kanban class="o_kanban_mobile">
+ <field name="message"/>
+ <field name="alert_type"/>
+ <field name='start_hour'/>
+ <field name='end_hour'/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_global_click">
+ <div class="o_kanban_record_top">
+ <strong class="o_kanban_record_title"><t t-esc="record.message.value"/></strong>
+ </div>
+ <div class="row mb4">
+ <div class="col-6">
+ Between: <t t-esc="record.start_hour.value"/> -
+ <t t-esc="record.end_hour.value"/>
+ </div>
+ <div class="col-6">
+ <span class="badge badge-pill float-right">
+ <t t-esc="record.alert_type.value"/>
+ </span>
+ </div>
+ </div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record id="lunch_alert_action" model="ir.actions.act_window">
+ <field name="name">Alerts</field>
+ <field name="res_model">lunch.alert</field>
+ <field name="view_mode">tree,kanban,form</field>
+ <field name="search_view_id" ref="lunch_alert_view_search"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">
+ Create a new lunch alert
+ </p><p>
+ Alerts are used to warn employee from possible issues concerning the lunch orders.
+ To create a lunch alert you have to define its recurrence, the time interval during which the alert should be executed and the message to display.
+ </p><p>
+ Example:<br/>
+ Recurency: Everyday,<br/>
+ Time interval: from 00h00 am to 11h59 pm,<br/>
+ Message: "You must order before 10h30 am".
+ </p>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/lunch/views/lunch_orders_views.xml b/addons/lunch/views/lunch_orders_views.xml
index b8665398153..8cd4658a8cb 100644
--- a/addons/lunch/views/lunch_orders_views.xml
+++ b/addons/lunch/views/lunch_orders_views.xml
@@ -19,6 +19,7 @@
<field name="arch" type="xml">
<form string='Orders Form' class="o_lunch">
<header>
+ <button name="%(action_lunch_order_line_lucky)d" type="action" string="Feeling Lucky" class="oe_highlight"/>
<field name='state' widget='statusbar' statusbar_visible='new,confirmed'/>
</header>
<sheet>
@@ -42,15 +43,24 @@
<field name='user_id' class="oe_inline" required='1'/>
<field name='date'/>
</group>
+ <div>
+ <div attrs="{'invisible': ['|',('state','!=','new'),('alerts','=',False)]}" class="alert alert-danger oe_edit_only" role="alert">
+ <field name='alerts'/>
+ </div>
+ </div>
</group>
+ <div>
+ <field name='previous_order_widget' widget='previous_order' class='o_lunch_last_orders'/>
+ </div>
+
<separator string='Select your order'/>
<field name='order_line_ids'>
<tree string='List' editable='bottom'>
<field name='product_id' context="{'lunch_date': parent.date}"/>
<field name='note' />
<field name='price'/>
- <field name='supplier_id' invisible="1"/>
+ <field name='supplier' invisible="1"/>
<field name="state" invisible="1"/>
</tree>
<form string='Lunch Order Form'>
@@ -85,7 +95,7 @@
<field name="user_id"/>
<group expand="0" string="Group By">
<filter name="group_by_order_id" string="By Order" context="{'group_by':'order_id'}"/>
- <filter name="group_by_supplier" string="By Vendor" context="{'group_by':'supplier_id'}"/>
+ <filter name="group_by_supplier" string="By Vendor" context="{'group_by':'supplier'}"/>
<filter name="group_by_date" string="Order Date" context="{'group_by':'date'}" help="Vendor Orders by Date"/>
</group>
</search>
@@ -98,14 +108,15 @@
<field name="arch" type="xml">
<tree string="Order lines Tree" create="false" edit="false">
<field name='date'/>
- <field name='supplier_id' invisible='1'/>
+ <field name='supplier' invisible='1'/>
<field name='product_id'/>
<field name='note'/>
<field name='state'/>
<field name='user_id'/>
<field name='price' sum="Total" string="Price"/>
- <button name="action_confirm" string="Confirm" type="object" icon="fa-phone text-success" attrs="{'invisible': [('state', '!=', 'ordered')]}" groups="lunch.group_lunch_manager"/>
- <button name="action_cancel" string="Cancel" type="object" icon="fa-times-circle text-danger" attrs="{'invisible': [('state', 'in', ['cancelled', 'confirmed'])]}" groups="lunch.group_lunch_manager"/>
+ <button name="order" string="Order" type="object" icon="fa-phone text-success" attrs="{'invisible': ['|',('state','=','confirmed'),('state','=','ordered')]}" groups="lunch.group_lunch_manager"/>
+ <button name="confirm" string="Receive" type="object" icon="fa-check text-success" attrs="{'invisible': [('state','!=','ordered')]}" groups="lunch.group_lunch_manager"/>
+ <button name="cancel" string="Cancel" type="object" icon="fa-times-circle text-danger" attrs="{'invisible': [('state','=','cancelled')]}" groups="lunch.group_lunch_manager"/>
</tree>
</field>
</record>
@@ -115,11 +126,11 @@
<field name="model">lunch.order.line</field>
<field name="arch" type="xml">
<kanban>
- <field name="product_id"/>
- <field name="note"/>
- <field name="state"/>
- <field name="user_id"/>
- <field name="date"/>
+ <field name='product_id'/>
+ <field name='note'/>
+ <field name='state'/>
+ <field name='user_id'/>
+ <field name='date'/>
<field name="price"/>
<templates>
<t t-name="kanban-box">
diff --git a/addons/lunch/views/lunch_product_views.xml b/addons/lunch/views/lunch_product_views.xml
index 70a8695564e..e7647207d6d 100644
--- a/addons/lunch/views/lunch_product_views.xml
+++ b/addons/lunch/views/lunch_product_views.xml
@@ -7,12 +7,12 @@
<search string="Product Search">
<field name="name"/>
<field name="category_id"/>
- <field name="supplier_id"/>
+ <field name="supplier"/>
<field name="description"/>
- <filter name="inactive" string="Archived" domain="[('active','=',False)]"/>
<group expand="0" string="Group By">
- <filter name="supplier" string="Vendor" context="{'group_by': 'supplier_id'}"/>
+ <filter name="supplier" string="Vendor" context="{'group_by': 'supplier'}"/>
<filter name="category" string="Product Category" context="{'group_by': 'category_id'}"/>
+ <filter name="inactive" string="Archived" domain="[('active','=',False)]"/>
</group>
</search>
</field>
@@ -25,7 +25,7 @@
<tree string="Products Tree">
<field name="name"/>
<field name="category_id"/>
- <field name="supplier_id"/>
+ <field name="supplier"/>
<field name="description"/>
<field name="price"/>
</tree>
@@ -37,6 +37,8 @@
<field name="model">lunch.product</field>
<field name="arch" type="xml">
<form string="Products Form">
+ <header>
+ </header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="toggle_active" type="object"
@@ -45,24 +47,18 @@
options='{"terminology": "archive"}'/>
</button>
</div>
- <field name="image" widget="image" class="oe_avatar" options="{'preview_image': 'image_medium'}"/>
- <label for="name"/>
- <field name='name'/>
- <div>
- <field name="is_topping"/>
- <label for="is_topping"/>
- </div>
<group>
- <field name='category_id' attrs="{'invisible': [('is_topping', '=', True)], 'required': [('is_topping', '=', False)]}"/>
- <field name='supplier_id'/>
- <field name='price'/>
+ <group>
+ <field name='name'/>
+ <field name='category_id'/>
+ </group>
+ <group>
+ <field name='supplier'/>
+ <field name='price'/>
+ </group>
</group>
- <notebook>
- <page string="Description" name="description">
- <label for='description'/>
- <field name='description'/>
- </page>
- </notebook>
+ <label for='description'/>
+ <field name='description'/>
</sheet>
</form>
</field>
@@ -72,37 +68,28 @@
<field name="name">lunch.product.kanban</field>
<field name="model">lunch.product</field>
<field name="arch" type="xml">
- <kanban js_class="lunch_kanban" create="0" class="o_kanban_mobile">
- <field name="id"/>
+ <kanban class="o_kanban_mobile">
<field name="name"/>
<field name="category_id"/>
- <field name="supplier_id"/>
+ <field name="supplier"/>
<field name="description"/>
<field name="price"/>
- <field name="image_small"/>
- <field name="already_ordered"/>
<templates>
<t t-name="kanban-box">
- <div class="oe_kanban_global_click o_kanban_record_has_image_fill">
- <div class="o_kanban_image_fill_left d-none d-md-block flex-grow-0"
- t-attf-style="background-image:url('#{kanban_image('lunch.product', 'image_medium', record.id.raw_value)}')"/>
- <div class="d-flex flex-column flex-grow-1">
- <div class="o_kanban_record_top d-flex flex-row">
- <div class="o_kanban_record_headings flex-grow-1">
- <i t-if="record.already_ordered.raw_value" class="fa fa-heart mr-1 o_pink"/><strong class="o_kanban_record_title"><t t-esc="record.name.value"/></strong>
- </div>
- <span class="badge badge-pill">
- Price: <t t-esc="record.price.value"/>
- </span>
- </div>
- <div class="o_kanban_record_bottom d-flex flex-column">
- <div>
- <t t-esc="record.supplier_id.value"/>
- </div>
- <div>
- <t t-esc="record.description.value"/>
- </div>
+ <div t-attf-class="oe_kanban_global_click">
+ <div class="o_kanban_record_top">
+ <div class="o_kanban_record_headings">
+ <strong class="o_kanban_record_title"><t t-esc="record.name.value"/></strong>
</div>
+ <span class="badge badge-pill float-right">
+ Price: <t t-esc="record.price.value"/>
+ </span>
+ </div>
+ <div>
+ <t t-esc="record.supplier.value"/>
+ </div>
+ <div>
+ <t t-esc="record.description.value"/>
</div>
</div>
</t>
@@ -111,16 +98,6 @@
</field>
</record>
- <record id="lunch_product_category_view_tree" model="ir.ui.view">
- <field name="name">Product category Tree</field>
- <field name="model">lunch.product.category</field>
- <field name="arch" type="xml">
- <tree string="Products List" editable="bottom">
- <field name='name' string="Product Category: "/>
- </tree>
- </field>
- </record>
-
<record id="lunch_product_category_view_form" model="ir.ui.view">
<field name="name">Product category Form</field>
<field name="model">lunch.product.category</field>
@@ -148,13 +125,6 @@
</field>
</record>
- <record id="lunch_product_action_order" model="ir.actions.act_window">
- <field name="name">Order</field>
- <field name="res_model">lunch.product</field>
- <field name="view_mode">kanban</field>
- <field name="domain">[('is_topping', '=', False), ('supplier_id.available_today', '=', True)]</field>
- </record>
-
<record id="lunch_product_category_action" model="ir.actions.act_window">
<field name="name">Product Categories</field>
<field name="res_model">lunch.product.category</field>
diff --git a/addons/lunch/views/lunch_supplier_views.xml b/addons/lunch/views/lunch_supplier_views.xml
deleted file mode 100644
index 0d6ec048451..00000000000
--- a/addons/lunch/views/lunch_supplier_views.xml
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <record id="lunch_supplier_view_tree" model="ir.ui.view">
- <field name="name">lunch.supplier.view.tree</field>
- <field name="model">lunch.supplier</field>
- <field name="arch" type="xml">
- <tree>
- <field name="name"/>
- <field name="phone"/>
- <field name="email"/>
- </tree>
- </field>
- </record>
-
- <record id="lunch_supplier_view_form" model="ir.ui.view">
- <field name="name">lunch.supplier.view.form</field>
- <field name="model">lunch.supplier</field>
- <field name="arch" type="xml">
- <form>
- <sheet>
- <field name="image" widget="image" class="oe_avatar" options="{'preview_image': 'image_medium'}"/>
- <div class="oe_title">
- <label for="name" class="oe_edit_only"/>
- <h1><field name="name" required="1"/></h1>
- </div>
- <group>
- <field name="partner_id"/>
- <label for="street" string="Address"/>
- <div class="o_address_format">
- <field name="street" placeholder="Street..." class="o_address_street"/>
- <field name="street2" placeholder="Street 2..." class="o_address_street"/>
- <field name="city" placeholder="City" class="o_address_city"/>
- <field name="state_id" class="o_address_state" placeholder="State" options="{'no_open': True}" context="{'country_id': country_id, 'zip': zip_code}"/>
- <field name="zip_code" placeholder="ZIP" class="o_address_zip"/>
- <field name="country_id" placeholder="Country" class="o_address_country" options="{'no_open': True, 'no_create': True}"/>
- </div>
- </group>
- <group>
- <field name="vat"/>
- <field name="email"/>
- <field name="phone"/>
- </group>
- <notebook colspan="4">
- <page string="Availability">
- <group>
- <field name="tz"/>
- <field name="recurrency"/>
- <field name="recurrency_date" attrs="{'invisible': [('recurrency', '!=', 'once')]}"/>
- <field name="recurrency_monday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_tuesday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_wednesday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_thursday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_friday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_saturday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_sunday" attrs="{'invisible': [('recurrency', '!=', 'reccurent')]}"/>
- <field name="recurrency_from" widget="float_time"/>
- <field name="recurrency_to" widget="float_time"/>
- </group>
- </page>
- <page string="Description">
- <label for="description" string="Type your description here" class="oe_edit_only"/>
- <field name="description"/>
- </page>
- <page string="Order">
- <label for="send_by"/>
- <field name="send_by" widget="radio"/>
- <div>
- <label for="automatic_email_send" string="Send Orders automatically at" attrs="{'invisible': [('send_by', '!=', 'mail')]}"/>
- <field name="automatic_email_time" widget="float_time" attrs="{'invisible': [('send_by', '!=', 'mail')]}"/>
- </div>
- </page>
- </notebook>
- </sheet>
- </form>
- </field>
- </record>
-
- <record id="lunch_supplier_view_kanban" model="ir.ui.view">
- <field name="name">lunch.supplier.view.kanban</field>
- <field name="model">lunch.supplier</field>
- <field name="arch" type="xml">
- <kanban>
- <field name="partner_id"/>
- <field name="image_small"/>
- <field name="city"/>
- <field name="country_id"/>
- <field name="email"/>
- <templates>
- <t t-name="kanban-box">
- <div class="oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban">
- <div class="o_kanban_image_fill_left d-none d-md-block" t-attf-style="background-image:url('#{kanban_image('res.partner', 'image_medium', record.partner_id.raw_value)}')"/>
- <div class="oe_kanban_details">
- <strong class="o_kanban_record_title oe_partner_heading"><field name="display_name"/></strong>
- <ul>
- <li t-if="record.city.raw_value and !record.country_id.raw_value"><field name="city"/></li>
- <li t-if="!record.city.raw_value and record.country_id.raw_value"><field name="country_id"/></li>
- <li t-if="record.city.raw_value and record.country_id.raw_value"><field name="city"/>, <field name="country_id"/></li>
- <li t-if="record.email.raw_value" class="o_text_overflow"><field name="email"/></li>
- </ul>
- </div>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="lunch_vendors_action" model="ir.actions.act_window">
- <field name="name">Vendors</field>
- <field name="res_model">lunch.supplier</field>
- <field name="view_mode">kanban,tree,form</field>
- </record>
-</odoo>
diff --git a/addons/lunch/views/lunch_templates.xml b/addons/lunch/views/lunch_templates.xml
index a076bbb2261..a5d8c6e6935 100644
--- a/addons/lunch/views/lunch_templates.xml
+++ b/addons/lunch/views/lunch_templates.xml
@@ -2,25 +2,14 @@
<odoo>
<template id="assets_backend" name="lunch assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
- <link rel="stylesheet" type="text/scss" href="/lunch/static/src/scss/lunch_kanban.scss"/>
-
- <script type="text/javascript" src="/lunch/static/src/js/lunch_kanban_widget.js"></script>
- <script type="text/javascript" src="/lunch/static/src/js/lunch_payment_dialog.js"></script>
- <script type="text/javascript" src="/lunch/static/src/js/lunch_kanban_view.js"></script>
- <script type="text/javascript" src="/lunch/static/src/js/lunch_kanban_controller.js"></script>
- <script type="text/javascript" src="/lunch/static/src/js/lunch_kanban_renderer.js"></script>
- <script type="text/javascript" src="/lunch/static/src/js/lunch_kanban_record.js"></script>
- <script type="text/javascript" src="/lunch/static/src/js/lunch_kanban_model.js"></script>
+ <link rel="stylesheet" type="text/scss" href="/lunch/static/src/scss/lunch.scss"/>
+ <script type="text/javascript" src="/lunch/static/src/js/lunch.js"></script>
</xpath>
</template>
<template id="qunit_suite" name="lunch tests" inherit_id="web.qunit_suite">
<xpath expr="//t[@t-set='head']" position="inside">
- <script type="text/javascript" src="/lunch/static/tests/test_lunch_kanban.js"></script>
+ <script type="text/javascript" src="/lunch/static/tests/lunch_tests.js"/>
</xpath>
</template>
-
- <template id="lunch_payment_dialog" name="Lunch Payment Dialog">
- To add some money to your wallet, please contact your lunch manager.
- </template>
</odoo>
diff --git a/addons/lunch/views/lunch_views.xml b/addons/lunch/views/lunch_views.xml
index 0f509cf2465..454085fb92b 100644
--- a/addons/lunch/views/lunch_views.xml
+++ b/addons/lunch/views/lunch_views.xml
@@ -6,7 +6,7 @@
<menuitem name="Manager" parent="menu_lunch" id="menu_lunch_admin" sequence="51" groups="group_lunch_manager"/>
<menuitem name="Configuration" parent="menu_lunch" id="menu_lunch_config" sequence="53" groups="group_lunch_manager"/>
- <menuitem name="New Order" parent="menu_lunch_title" id="lunch_order_menu_form" action="lunch.lunch_product_action_order" sequence="1"/>
+ <menuitem name="New Order" parent="menu_lunch_title" id="lunch_order_menu_form" action="lunch_order_action_form" sequence="1"/>
<menuitem name="Previous Orders" parent="menu_lunch_title" id="lunch_order_menu_tree" action="lunch_order_line_action" sequence="2"/>
<menuitem name="Your Lunch Account" parent="menu_lunch_title" id="lunch_cashmove_menu_form" action="lunch_cashmove_action_account" sequence="3"/>
@@ -15,9 +15,9 @@
<menuitem name="Control Accounts" parent="menu_lunch_admin" id="lunch_cashmove_menu_control_accounts" action="lunch_cashmove_action_control_accounts"/>
<menuitem name="Employee Payments" parent="menu_lunch_admin" id="lunch_cashmove_menu_payment" action="lunch_cashmove_action_payment"/>
- <menuitem name="Vendors" parent="menu_lunch_config" id="lunch_vendors_menu" action="lunch_vendors_action" sequence="1"/>
- <menuitem name="Products" parent="menu_lunch_config" id="lunch_product_menu" action="lunch_product_action" sequence="2"/>
- <menuitem name="Product Categories" parent="menu_lunch_config" id="lunch_product_category_menu" action="lunch_product_category_action" sequence="3"/>
+ <menuitem name="Products" parent="menu_lunch_config" id="lunch_product_menu" action="lunch_product_action"/>
+ <menuitem name="Product Categories" parent="menu_lunch_config" id="lunch_product_category_menu" action="lunch_product_category_action" />
+ <menuitem name="Alerts" parent="menu_lunch_config" id="lunch_alert_menu" action="lunch_alert_action" />
<record id="action_server_lunch_archive_product" model="ir.actions.server">
diff --git a/addons/lunch/wizard/__init__.py b/addons/lunch/wizard/__init__.py
index 9fa7ccafa50..80c2ca03500 100644
--- a/addons/lunch/wizard/__init__.py
+++ b/addons/lunch/wizard/__init__.py
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import lunch_line_wizard
+from . import lucky_order
diff --git a/addons/lunch/wizard/lucky_order.py b/addons/lunch/wizard/lucky_order.py
new file mode 100644
index 00000000000..5836a418e56
--- /dev/null
+++ b/addons/lunch/wizard/lucky_order.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import random
+
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class LunchOrderLineLucky(models.TransientModel):
+ _name = 'lunch.order.line.lucky'
+ _description = 'Lunch Order Line Lucky'
+
+ def _default_supplier(self):
+ suppliers_obj = self.env['lunch.product'].search([]).mapped("supplier")
+ return [(4, supplier.id) for supplier in suppliers_obj]
+
+ product_id = fields.Many2one('lunch.product', 'Product', store=True)
+ supplier_ids = fields.Many2many(comodel_name='res.partner', string='Vendor', domain=lambda self: [("id", "in", self.env['lunch.product'].search([]).mapped("supplier").ids)])
+ is_max_budget = fields.Boolean("I'm not feeling rich", help="Enable this option to set a maximal budget for your lucky order.", store=True)
+ max_budget = fields.Float('Max Budget', store=True)
+
+ @api.multi
+ def random_pick(self):
+ """
+ To pick a random product from the selected suppliers, and create an order with this one
+ """
+ self.ensure_one()
+ if self.is_max_budget:
+ products_obj = self.env['lunch.product'].search([('supplier', "in", self.supplier_ids.ids), ('price', '<=', self.max_budget)])
+ else:
+ products_obj = self.env['lunch.product'].search([('supplier', "in", self.supplier_ids.ids)])
+ if len(products_obj) != 0:
+ random_product_obj = self.env['lunch.product'].browse([random.choice(products_obj.ids)])
+ order_line = self.env['lunch.order.line'].create({
+ 'product_id': random_product_obj.id,
+ 'order_id': self._context['active_id']
+ })
+ else:
+ raise UserError(_('No product is matching your request. Now you will starve to death.'))
+
+
+
+
diff --git a/addons/lunch/wizard/lucky_order_view.xml b/addons/lunch/wizard/lucky_order_view.xml
new file mode 100644
index 00000000000..2aafbb762b9
--- /dev/null
+++ b/addons/lunch/wizard/lucky_order_view.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+<data>
+
+ <record id="view_lunch_order_line_lucky" model="ir.ui.view">
+ <field name="name">view.lunch.order.line.lucky</field>
+ <field name="model">lunch.order.line.lucky</field>
+ <field name="arch" type="xml">
+ <form string="I'm feeling lucky today !">
+ <group string="Select your vendor">
+ <field name="is_max_budget"/>
+ <field name="max_budget" attrs="{'required': [('is_max_budget', '=', True)], 'invisible': [('is_max_budget', '=', False)]}"/>
+ <field name="supplier_ids" widget="many2many_checkboxes"/>
+ </group>
+ <footer>
+ <button name="random_pick" string="I'm feeling lucky" type="object" class="oe_highlight"/>
+ <button string="Cancel" class="btn btn-secondary" special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_lunch_order_line_lucky" model="ir.actions.act_window">
+ <field name="name">I'm feeling lucky today !</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">lunch.order.line.lucky</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="view_lunch_order_line_lucky"/>
+ <field name="context">{'record_id' : active_id}</field>
+ <field name="target">new</field>
+ </record>
+
+</data>
+</odoo>
diff --git a/addons/lunch/wizard/lunch_line_wizard.py b/addons/lunch/wizard/lunch_line_wizard.py
deleted file mode 100644
index 1229c4a0a07..00000000000
--- a/addons/lunch/wizard/lunch_line_wizard.py
+++ /dev/null
@@ -1,77 +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 UserError
-
-
-class LunchOrderLineWizard(models.TransientModel):
- _name = 'lunch.order.line.temp'
- _description = 'Lunch Order Line Temp'
-
- product_id = fields.Many2one('lunch.product', string='Product ID')
- product_description = fields.Text('Description', related='product_id.description')
- product_name = fields.Char('Product', related='product_id.name')
- product_supplier = fields.Many2one('lunch.supplier', related='product_id.supplier_id')
- topping_ids = fields.Many2many('lunch.product', string="Extra Garniture", domain="[('is_topping', '=', True), ('supplier_id', '=', product_supplier)]")
-
- quantity = fields.Float('Quantity', default=1)
- price_total = fields.Float('Total Price', compute='_compute_price_total')
- note = fields.Text('Special Instructions')
-
- user_id = fields.Many2one('res.users', default=lambda self: self.env.user.id)
-
- @api.depends('product_id', 'topping_ids', 'quantity')
- def _compute_price_total(self):
- for wizard in self:
- wizard.price_total = wizard.quantity * sum(record.price for record in (wizard.product_id | wizard.topping_ids))
-
- def find_current_order(self):
- order_id = self.env['lunch.order'].search([('state', '!=', 'cancel'),
- ('user_id', '=', self.user_id.id),
- ('date', '=', fields.Date.today())], limit=1)
- if not order_id:
- order_id = self.env['lunch.order'].create({})
- return order_id
-
- def add_to_cart(self):
- self.ensure_one()
-
- order_id = self.find_current_order()
-
- # Do not add a line to confirmed orders as they are uneditable
- if order_id.state == 'confirmed':
- return
-
- lines = order_id.order_line_ids.filtered(lambda line: line.product_id == self.product_id and line.topping_ids == self.topping_ids)
-
- if lines:
- line = lines[0]
- line.quantity += 1
- else:
- line = self.env['lunch.order.line'].create({
- 'order_id': order_id.id,
- 'product_id': self.product_id.id,
- 'topping_ids': [(6, 0, self.topping_ids.ids)],
- 'quantity': self.quantity,
- })
- wallet_balance = self.env['lunch.cashmove'].get_wallet_balance(self.user_id)
- if wallet_balance < line.price:
- raise UserError(_('Your wallet does not contain enough money to order that.'
- 'To add some money to your wallet, please contact your lunch manager.'))
-
- if order_id.state == 'ordered':
- # Need to update cashmove
- cashmove = self.env['lunch.cashmove'].search([('order_line_id', '=', line.id)], limit=1)
- if cashmove:
- cashmove.amount = -line.price
- else:
- self.env['lunch.cashmove'].create({
- 'order_line_id': line.id,
- 'user_id': line.user_id.id,
- 'description': line.product_id.name,
- 'state': 'order',
- 'date': line.date,
- 'amount': -line.price,
- })
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 9b70e2c7f78..00000000000
--- a/addons/lunch/wizard/lunch_line_wizard_views.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
-<data>
-
- <record id="view_lunch_order_line_temp" model="ir.ui.view">
- <field name="name">view.lunch.order.line.temp</field>
- <field name="model">lunch.order.line.temp</field>
- <field name="arch" type="xml">
- <form>
- <group>
- <field name="product_id" invisible="1"/>
- <field name="product_supplier" invisible="1"/>
- <field name="product_name"/>
- <field name="product_description"/>
- <field name="topping_ids" widget="many2many_tags"/>
- </group>
-
- <group>
- <field name="note"/>
- </group>
-
- <group>
- <field name="quantity"/>
- <field name="price_total" widget="monetary"/>
- </group>
-
- <footer>
- <button name="add_to_cart" string="Add To Cart" type="object" class="oe_highlight"/>
- </footer>
- </form>
- </field>
- </record>
-
-</data>
-</odoo>
diff --git a/addons/lunch_hr/__init__.py b/addons/lunch_hr/__init__.py
deleted file mode 100644
index dc5e6b693d1..00000000000
--- a/addons/lunch_hr/__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 models
diff --git a/addons/lunch_hr/__manifest__.py b/addons/lunch_hr/__manifest__.py
deleted file mode 100644
index ff335e2c574..00000000000
--- a/addons/lunch_hr/__manifest__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-{
- 'name': 'Lunch HR',
- 'sequence': 120,
- 'version': '1.0',
- 'depends': ['lunch', 'hr'],
- 'category': 'Human Resources',
- 'summary': 'Handle lunch orders of your employees',
- 'description': """""",
- 'data': [
- 'views/lunch_product_views.xml',
- 'views/lunch_supplier_views.xml',
- ],
- 'demo': ['data/lunch_hr_demo.xml',],
- 'auto_install': True,
- 'installable': True,
- 'application': False,
-}
diff --git a/addons/lunch_hr/data/lunch_hr_demo.xml b/addons/lunch_hr/data/lunch_hr_demo.xml
deleted file mode 100644
index 62e70952c5a..00000000000
--- a/addons/lunch_hr/data/lunch_hr_demo.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <data noupdate="1">
-
- <record id="partner_farm_1" model="res.partner">
- <field name="name">Farm 1</field>
- </record>
-
- <record id="partner_farm_2" model="res.partner">
- <field name="name">Farm 2</field>
- </record>
-
- <record id="partner_brussels" model="res.partner">
- <field name="name">Brussels</field>
- </record>
-
- <record id="hr.employee_admin" model="hr.employee">
- <field name="address_id" ref="partner_farm_1"/>
- </record>
-
- <record id="lunch.supplier_pizza_inn" model="lunch.supplier">
- <field name="available_location_ids" eval="[
- (6, 0, [ref('lunch_hr.partner_farm_1'), ref('lunch_hr.partner_farm_2')]),
- ]"/>
- </record>
-
- </data>
-</odoo>
diff --git a/addons/lunch_hr/models/__init__.py b/addons/lunch_hr/models/__init__.py
deleted file mode 100644
index 8d16a6970f5..00000000000
--- a/addons/lunch_hr/models/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import lunch_product
-from . import lunch_supplier
diff --git a/addons/lunch_hr/models/lunch_product.py b/addons/lunch_hr/models/lunch_product.py
deleted file mode 100644
index 8418d994d81..00000000000
--- a/addons/lunch_hr/models/lunch_product.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models, tools, _
-
-from odoo.osv import expression
-from odoo.tools import pycompat
-
-
-class LunchProduct(models.Model):
- _inherit = 'lunch.product'
-
- # This field is used only for searching
- is_available_at = fields.Many2one('res.partner', 'Product Availability', compute='_compute_is_available_at', search='_search_is_available_at')
-
- def _compute_is_available_at(self):
- for product in self:
- product.is_available_at = False
-
- def _search_is_available_at(self, operator, value):
- if operator in ['=', '!=', 'ilike', 'not ilike'] and isinstance(value, pycompat.string_types):
- location_ids = self.env['res.partner'].search([('name', operator, value)]).ids
-
- if operator in ['='] and not value:
- if self.env.user.employee_ids and self.env.user.employee_ids[0].address_id:
- location_ids = self.env.user.employee_ids[0].address_id.ids
-
- if location_ids:
- suppliers = self.env['lunch.supplier'].search([('available_location_ids', 'in', location_ids)])
- product_ids = self.env['lunch.product'].search([('supplier', 'in', suppliers.ids)])
- return [('id', 'in', product_ids.ids)]
-
- return expression.TRUE_DOMAIN
diff --git a/addons/lunch_hr/models/lunch_supplier.py b/addons/lunch_hr/models/lunch_supplier.py
deleted file mode 100644
index 27434347ad1..00000000000
--- a/addons/lunch_hr/models/lunch_supplier.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models
-
-
-class LunchSupplier(models.Model):
- _inherit = 'lunch.supplier'
-
- available_location_ids = fields.Many2many('res.partner', string='This Supplier is available at')
diff --git a/addons/lunch_hr/views/lunch_product_views.xml b/addons/lunch_hr/views/lunch_product_views.xml
deleted file mode 100644
index 356c39fc892..00000000000
--- a/addons/lunch_hr/views/lunch_product_views.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <record id="lunch_product_view_search_inherit" model="ir.ui.view">
- <field name="name">lunch.product.search.inherit</field>
- <field name="model">lunch.product</field>
- <field name="inherit_id" ref="lunch.lunch_product_view_search"/>
- <field name="arch" type="xml">
- <xpath expr="//filter[@name='inactive']" position="after">
- <filter name="available" string="Available at current location" domain="[('is_available_at', '=', False)]"/>
- </xpath>
- </field>
- </record>
-</odoo>
diff --git a/addons/lunch_hr/views/lunch_supplier_views.xml b/addons/lunch_hr/views/lunch_supplier_views.xml
deleted file mode 100644
index 28a91e3bac7..00000000000
--- a/addons/lunch_hr/views/lunch_supplier_views.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <record id="lunch_supplier_view_form_inherit" model="ir.ui.view">
- <field name="name">lunch.supplier.form.view.inherit</field>
- <field name="model">lunch.supplier</field>
- <field name="inherit_id" ref="lunch.lunch_supplier_view_form"/>
- <field name="arch" type="xml">
- <xpath expr="//field[@name='recurrency_to']" position="after">
- <field name="available_location_ids" widget="many2many_tags"/>
- </xpath>
- </field>
- </record>
-</odoo>