diff options
| author | Robot Odoo <xmo+roboto@odoo.com> | 2019-01-08 14:02:06 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-01-08 14:02:06 +0100 |
| commit | 671c45fbbfb261c95b6d37f9e16dee8bf71aadb4 (patch) | |
| tree | 4bb00c18ac7d8e7c8169b71e77b8d272ad9f889f | |
| parent | 8ed2a7e68abd754b6fc3721103a2c5572c0a2e02 (diff) | |
| parent | 6ab800135f4bc6662fd99873ca024780fd9e384d (diff) | |
[REV] lunch: Revert changes introduced in f727b8f0b57aad49f2ea8c54600ab73bbd02570f
YOLO
closes odoo/odoo#30025
77 files changed, 1033 insertions, 2233 deletions
diff --git a/.tx/config b/.tx/config index 464e3cd4a54..8fd8c2cc976 100644 --- a/.tx/config +++ b/.tx/config @@ -387,11 +387,6 @@ file_filter = addons/lunch/i18n/<lang>.po source_file = addons/lunch/i18n/lunch.pot source_lang = en -[odoo-master.lunch_hr] -file_filter = addons/lunch_hr/i18n/<lang>.po -source_file = addons/lunch_hr/i18n/lunch_hr.pot -source_lang = en - [odoo-master.mail] file_filter = addons/mail/i18n/<lang>.po source_file = addons/mail/i18n/mail.pot 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 1b6c80814f0..e28b88a82f2 100644 --- a/addons/lunch/__manifest__.py +++ b/addons/lunch/__manifest__.py @@ -27,18 +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 f252eb716b9..00000000000 --- a/addons/lunch/controllers/main.py +++ /dev/null @@ -1,68 +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']), - 'alerts': request.env['lunch.alert'].search_read([('available_today', '=', True)], ['message']) - } - - 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 a5f44ebe955..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,232 +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.topping.type" id="type_garniture"> - <field name="name">Garniture</field> - </record> - - <record model="lunch.topping" id="product_olives"> - <field name="name">Olives</field> - <field name="price">0.30</field> - <field name="category_id" ref="categ_pizza"/> - <field name="type_id" ref="type_garniture"/> </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"> @@ -310,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"> @@ -318,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"> @@ -326,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"> @@ -342,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"> @@ -353,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"> @@ -364,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"> @@ -375,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"> @@ -386,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> @@ -395,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> @@ -404,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> @@ -429,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 1bdf9862ffe..f99750c87d0 100644 --- a/addons/lunch/models/__init__.py +++ b/addons/lunch/models/__init__.py @@ -4,4 +4,3 @@ 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 index bd0e7c04a9b..325a64f4385 100644 --- a/addons/lunch/models/lunch_alert.py +++ b/addons/lunch/models/lunch_alert.py @@ -1,31 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import math -import pytz +import datetime -from datetime import datetime, time - -from odoo import api, fields, models - -from odoo.osv import expression -from odoo.tools import float_round - - -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) +from odoo import api, fields, models, _ class LunchAlert(models.Model): @@ -35,81 +13,73 @@ class LunchAlert(models.Model): _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) - recurrency = fields.Selection([('once', 'Specific Day'), ('reccurent', 'Reccurent')], 'Recurrency', default='once') - recurrency_from = fields.Float('From', default=7) - recurrency_to = fields.Float('To', default=23) - 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') - - @api.depends('recurrency_date', 'recurrency_from') - def _compute_recurrency_date_from(self): - for alert in self: - if alert.recurrency_date and alert.recurrency_from: - alert.recurrency_date_from = datetime.combine(alert.recurrency_date, float_to_time(alert.recurrency_from)) - - @api.depends('recurrency_date', 'recurrency_to') - def _compute_recurrency_date_to(self): for alert in self: - if alert.recurrency_date and alert.recurrency_to: - alert.recurrency_date_to = datetime.combine(alert.recurrency_date, float_to_time(alert.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() - - for alert in self: - time_from = float_to_time(alert.recurrency_from) - time_to = float_to_time(alert.recurrency_to) - - if alert.recurrency == 'once': - alert.available_today = (alert.recurrency_date_from <= now <= alert.recurrency_date_to) - else: - fieldname = 'recurrency_%s' % (WEEKDAY_TO_NAME[now.weekday()]) - alert.available_today = alert[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]) + 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 cadd1ca449e..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.topping', string='Toppings') - product_id = fields.Many2one('lunch.product', string="Product", 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 * (line.product_id.price + sum(line.topping_ids.mapped('price'))) - - 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 af3d25df7d7..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): @@ -14,37 +13,6 @@ class LunchProductCategory(models.Model): _description = 'Lunch Product Category' name = fields.Char('Product Category', required=True) - topping_ids = fields.One2many('lunch.topping', 'category_id') - - -class LunchToppingType(models.Model): - """""" - _name = 'lunch.topping.type' - _description = 'Lunch Topping Type' - - name = fields.Char('Name', required=True) - - -class LunchTopping(models.Model): - """""" - _name = 'lunch.topping' - _description = 'Lunch Toppings' - - name = fields.Char('Name', required=True) - price = fields.Float('Price', digits=dp.get_precision('Account'), required=True) - category_id = fields.Many2one('lunch.product.category') - type_id = fields.Many2one('lunch.topping.type') - - def name_get(self): - currency_id = self.env.user.company_id.currency_id - res = dict(super(LunchTopping, self).name_get()) - for topping in self: - if currency_id.position == 'before': - price = '%s %s' % (currency_id.symbol, topping.price) - else: - price = '%s %s' % (topping.price, currency_id.symbol) - res[topping.id] = '%s %s' % (topping.name, price) - return list(res.items()) class LunchProduct(models.Model): @@ -52,51 +20,44 @@ class LunchProduct(models.Model): _name = 'lunch.product' _description = 'Lunch Product' - name = fields.Char('Name', required=True) + 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) + available = fields.Boolean(compute='_get_available_product', search='_search_available_products') - # 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) - - 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 ce16eb8460b..00000000000 --- a/addons/lunch/models/lunch_supplier.py +++ /dev/null @@ -1,183 +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') - - responsible_id = fields.Many2one('res.users', string="Responsible", domain=lambda self: [('groups_id', 'in', self.env.ref('lunch.group_lunch_manager').id)], - help="This is used to set a responsible for this particular vendor") - - 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='reccurent') - recurrency_from = fields.Float('From', default=7) - recurrency_to = fields.Float('To', default=23) - 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', default=True) - recurrency_tuesday = fields.Boolean('Tuesday', default=True) - recurrency_wednesday = fields.Boolean('Wednesday', default=True) - recurrency_thursday = fields.Boolean('Thursday', default=True) - recurrency_friday = fields.Boolean('Friday', default=True) - 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.recurrency_date_from <= now <= supplier.recurrency_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 06df5f431de..244fbf11f18 100644 --- a/addons/lunch/security/ir.model.access.csv +++ b/addons/lunch/security/ir.model.access.csv @@ -4,17 +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 -lunch_alert_access,access_lunch_alert_user,model_lunch_alert,base.group_user,1,0,0,0 -lunch_alert_user,access_lunch_alert_lunch_user,model_lunch_alert,group_lunch_user,1,1,1,1 -lunch_alert_manager,access_lunch_alert_manager,model_lunch_alert,group_lunch_manager,1,1,1,1 -lunch_topping_type_user,access_lunch_topping_type,model_lunch_topping_type,base.group_user,1,0,0,0 -lunch_topping_type_manager,access_lunch_topping_type,model_lunch_topping_type,group_lunch_manager,1,1,1,1 -lunch_topping_user,access_lunch_topping,model_lunch_topping,base.group_user,1,0,0,0 -lunch_topping_manager,access_lunch_topping,model_lunch_topping,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 Binary files differdeleted file mode 100644 index f19c95ac934..00000000000 --- a/addons/lunch/static/img/4formaggio.png +++ /dev/null diff --git a/addons/lunch/static/img/Coke.png b/addons/lunch/static/img/Coke.png Binary files differdeleted file mode 100644 index e822a2656ac..00000000000 --- a/addons/lunch/static/img/Coke.png +++ /dev/null diff --git a/addons/lunch/static/img/bacon_burger.png b/addons/lunch/static/img/bacon_burger.png Binary files differdeleted file mode 100644 index ad4177d8ac2..00000000000 --- a/addons/lunch/static/img/bacon_burger.png +++ /dev/null diff --git a/addons/lunch/static/img/brie.png b/addons/lunch/static/img/brie.png Binary files differdeleted file mode 100644 index 5b76bfbb1ee..00000000000 --- a/addons/lunch/static/img/brie.png +++ /dev/null diff --git a/addons/lunch/static/img/cheeseburger.png b/addons/lunch/static/img/cheeseburger.png Binary files differdeleted file mode 100644 index f9d5a3e5c01..00000000000 --- a/addons/lunch/static/img/cheeseburger.png +++ /dev/null diff --git a/addons/lunch/static/img/chicken_curry.png b/addons/lunch/static/img/chicken_curry.png Binary files differdeleted file mode 100644 index 09e494482d6..00000000000 --- a/addons/lunch/static/img/chicken_curry.png +++ /dev/null diff --git a/addons/lunch/static/img/chirashi.png b/addons/lunch/static/img/chirashi.png Binary files differdeleted file mode 100644 index 65143711848..00000000000 --- a/addons/lunch/static/img/chirashi.png +++ /dev/null diff --git a/addons/lunch/static/img/club.png b/addons/lunch/static/img/club.png Binary files differdeleted file mode 100644 index 5f3e36f3f37..00000000000 --- a/addons/lunch/static/img/club.png +++ /dev/null diff --git a/addons/lunch/static/img/coke_zero.png b/addons/lunch/static/img/coke_zero.png Binary files differdeleted file mode 100644 index 72dbba5542b..00000000000 --- a/addons/lunch/static/img/coke_zero.png +++ /dev/null diff --git a/addons/lunch/static/img/fanta.png b/addons/lunch/static/img/fanta.png Binary files differdeleted file mode 100644 index b7a48332e0d..00000000000 --- a/addons/lunch/static/img/fanta.png +++ /dev/null diff --git a/addons/lunch/static/img/fuze_black.png b/addons/lunch/static/img/fuze_black.png Binary files differdeleted file mode 100644 index 1daac338d90..00000000000 --- a/addons/lunch/static/img/fuze_black.png +++ /dev/null diff --git a/addons/lunch/static/img/fuze_green.png b/addons/lunch/static/img/fuze_green.png Binary files differdeleted file mode 100644 index 9a274ab6796..00000000000 --- a/addons/lunch/static/img/fuze_green.png +++ /dev/null diff --git a/addons/lunch/static/img/gouda.png b/addons/lunch/static/img/gouda.png Binary files differdeleted file mode 100644 index c50d6d8dfbf..00000000000 --- a/addons/lunch/static/img/gouda.png +++ /dev/null diff --git a/addons/lunch/static/img/italiana.png b/addons/lunch/static/img/italiana.png Binary files differdeleted file mode 100644 index 2f8c4244436..00000000000 --- a/addons/lunch/static/img/italiana.png +++ /dev/null diff --git a/addons/lunch/static/img/lipton.png b/addons/lunch/static/img/lipton.png Binary files differdeleted file mode 100644 index a1f8ef21f2e..00000000000 --- a/addons/lunch/static/img/lipton.png +++ /dev/null diff --git a/addons/lunch/static/img/maki.png b/addons/lunch/static/img/maki.png Binary files differdeleted file mode 100644 index c92dd8c83a4..00000000000 --- a/addons/lunch/static/img/maki.png +++ /dev/null diff --git a/addons/lunch/static/img/mozza.png b/addons/lunch/static/img/mozza.png Binary files differdeleted file mode 100644 index 8820fbb9cd5..00000000000 --- a/addons/lunch/static/img/mozza.png +++ /dev/null diff --git a/addons/lunch/static/img/napoli.png b/addons/lunch/static/img/napoli.png Binary files differdeleted file mode 100644 index c7ecd65db8a..00000000000 --- a/addons/lunch/static/img/napoli.png +++ /dev/null diff --git a/addons/lunch/static/img/pasta_bolognese.png b/addons/lunch/static/img/pasta_bolognese.png Binary files differdeleted file mode 100644 index 64f9972e5c2..00000000000 --- a/addons/lunch/static/img/pasta_bolognese.png +++ /dev/null diff --git a/addons/lunch/static/img/pizza_funghi.png b/addons/lunch/static/img/pizza_funghi.png Binary files differdeleted file mode 100644 index 00bceba96b6..00000000000 --- a/addons/lunch/static/img/pizza_funghi.png +++ /dev/null diff --git a/addons/lunch/static/img/pizza_margherita.png b/addons/lunch/static/img/pizza_margherita.png Binary files differdeleted file mode 100644 index 0e317265524..00000000000 --- a/addons/lunch/static/img/pizza_margherita.png +++ /dev/null diff --git a/addons/lunch/static/img/pizza_veggie.png b/addons/lunch/static/img/pizza_veggie.png Binary files differdeleted file mode 100644 index 950afb54a12..00000000000 --- a/addons/lunch/static/img/pizza_veggie.png +++ /dev/null diff --git a/addons/lunch/static/img/salmon_sushi.png b/addons/lunch/static/img/salmon_sushi.png Binary files differdeleted file mode 100644 index 1467b54f120..00000000000 --- a/addons/lunch/static/img/salmon_sushi.png +++ /dev/null diff --git a/addons/lunch/static/img/temaki.png b/addons/lunch/static/img/temaki.png Binary files differdeleted file mode 100644 index 8f47e7c035a..00000000000 --- a/addons/lunch/static/img/temaki.png +++ /dev/null diff --git a/addons/lunch/static/img/tuna_sandwich.png b/addons/lunch/static/img/tuna_sandwich.png Binary files differdeleted file mode 100644 index b616d6156a5..00000000000 --- a/addons/lunch/static/img/tuna_sandwich.png +++ /dev/null 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 eff03959d52..00000000000 --- a/addons/lunch/static/src/scss/lunch_kanban.scss +++ /dev/null @@ -1,47 +0,0 @@ -.o_lunch_kanban { - .o_pink { - color: nth($o-colors, 10); - } - - .o_lunch_widget { - display: flex; - border-bottom: 1px solid #CED4DA; - background-color: white; - min-height: 90px; - overflow-y: visible; - - .o_lunch_widget_info { - padding: 5px 12px; - margin: 4px 8px; - width: 300px; - flex: 1 1 auto; - overflow-y: visible; - - .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 61077e54e13..00000000000 --- a/addons/lunch/static/src/xml/lunch_kanban.xml +++ /dev/null @@ -1,125 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<templates> - <div t-name="LunchKanbanWidget"> - <div class="o_lunch_widget"> - <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' && !widget.data.edit" href="#" class="o_lunch_widget_edit">Edit</a> - <a t-if="widget.data.state == 'ordered' && 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 && 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-foreach="widget.data.alerts" t-as="alert"> - <div class="alert alert-danger" role="alert"> - <t t-esc="alert.message"/> - </div> - </t> - </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 649c84b680e..00000000000 --- a/addons/lunch/static/tests/test_lunch_kanban.js +++ /dev/null @@ -1,210 +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} - ], - alerts: [], - }); - } 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} - ], - alerts: [], - }); - } 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} - ], - alerts: [], - }); - } 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 3c5a31188e3..00000000000 --- a/addons/lunch/tests/common.py +++ /dev/null @@ -1,79 +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.garniture_type = self.env['lunch.topping.type'].create({ - 'name': 'Graniture', - }) - - self.topping_olives = self.env['lunch.topping'].create({ - 'name': 'Olives', - 'price': 0.3, - 'category_id': self.category_pizza.id, - 'type_id': self.garniture_type.id, - }) 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 fe9776e854b..00000000000 --- a/addons/lunch/tests/test_supplier.py +++ /dev/null @@ -1,163 +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): - ''' - This test checks that _search_available_today returns a valid domain - ''' - 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')] - - # It should return an empty domain if we compare to values other than datetime - 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) - - with patch.object(fields.Datetime, 'now', return_value=self.monday_10am) as _: - 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 index cbd587e3065..bb6d807a9ff 100644 --- a/addons/lunch/views/lunch_alert_views.xml +++ b/addons/lunch/views/lunch_alert_views.xml @@ -6,7 +6,7 @@ <field name="arch" type="xml"> <search string="Search"> <field name="message"/> - <filter name="inactive" string="Currently inactive" domain="[('available_today','=',False)]"/> + <filter name="inactive" string="Archived" domain="[('active','=',False)]"/> </search> </field> </record> @@ -17,9 +17,9 @@ <field name="arch" type="xml"> <tree> <field name="message"/> - <field name="recurrency"/> - <field name='recurrency_from' widget='float_time'/> - <field name='recurrency_to' widget='float_time'/> + <field name="alert_type"/> + <field name='start_hour' widget='float_time'/> + <field name='end_hour' widget='float_time'/> </tree> </field> </record> @@ -28,33 +28,107 @@ <field name="name">lunch.alert.form</field> <field name="model">lunch.alert</field> <field name="arch" type="xml"> - <form string="alert form"> + <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> - <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 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> - <label for='message'/> - <field name="message"/> </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">Lunch Alerts</field> + <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="view_mode">tree,form</field> - <field name="view_id" ref="lunch_alert_view_tree"/> + <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 a90cfe826a9..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="group_by_supplier" string="Vendor" context="{'group_by': 'supplier_id'}"/> - <filter name="group_by_category" string="Product Category" context="{'group_by': 'category_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,20 +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'/> <group> - <field name='category_id'/> - <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> @@ -68,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" edit="0" group_create="0" group_edit="0" group_delete="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> @@ -107,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"> - <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> @@ -124,14 +105,7 @@ <form string="Products Form"> <sheet> <group> - <field name="name" string="Product Category: "/> - <field name="topping_ids"> - <tree editable="bottom"> - <field name="type_id"/> - <field name="name"/> - <field name="price"/> - </tree> - </field> + <field name='name' string="Product Category: "/> </group> </sheet> </form> @@ -151,14 +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="search_view_id" ref="lunch_product_view_search"/> - <field name="domain">[('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 1a250b3b71b..00000000000 --- a/addons/lunch/views/lunch_supplier_views.xml +++ /dev/null @@ -1,116 +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> - <group> - <field name="responsible_id"/> - </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 e30cb85272d..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,10 +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="Alerts" parent="menu_lunch_config" id="lunch_alert_menu" action="lunch_alert_action" sequence="4"/> + <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 4b686a31c20..00000000000 --- a/addons/lunch/wizard/lunch_line_wizard.py +++ /dev/null @@ -1,91 +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' - - def _default_topping_ids(self): - last_time_ordered = self.env['lunch.order.line'].search([('product_id', '=', self.env.context.get('default_product_id', 0))], - order="date desc", limit=1) - return last_time_ordered.topping_ids - - currency_id = fields.Many2one('res.currency', default=lambda self: self.env.user.company_id.currency_id) - - 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_category = fields.Many2one('lunch.product.category', related='product_id.category_id') - topping_ids = fields.Many2many('lunch.topping', string="Extra Garniture", domain="[('category_id', '=', product_category)]", default=_default_topping_ids) - - available_toppings = fields.Boolean(help='Are toppings available for this product', compute='_compute_available_toppings') - - 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') - def _compute_available_toppings(self): - for wizard in self: - wizard.available_toppings = bool(wizard.env['lunch.topping'].search_count([('category_id', '=', wizard.product_category.id)])) - - @api.depends('product_id', 'topping_ids', 'quantity') - def _compute_price_total(self): - for wizard in self: - wizard.price_total = wizard.quantity * (wizard.product_id.price + sum(wizard.topping_ids.mapped('price'))) - - 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 cd013c0093b..00000000000 --- a/addons/lunch/wizard/lunch_line_wizard_views.xml +++ /dev/null @@ -1,34 +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> - <field name="currency_id" invisible="1"/> - <field name="quantity" invisible="1"/> - <field name="product_id" invisible="1"/> - <field name="product_category" invisible="1"/> - <h2 class="d-flex"> - <div class="flex-grow-1 pr-5"><field name="product_name"/></div> - <div class="flex-grow-0"><field name="price_total" widget="monetary"/></div> - </h2> - <group> - <field name="available_toppings" invisible="1"/> - <field name="topping_ids" attrs="{'invisible': [('available_toppings', '=', False)]}" widget="many2many_checkboxes"/> - </group> - <group> - <field name="note"/> - </group> - <footer> - <button name="add_to_cart" string="Add To Cart" type="object" class="oe_highlight"/> - <button string="Discard" special="cancel"/> - </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/i18n/lunch_hr.pot b/addons/lunch_hr/i18n/lunch_hr.pot deleted file mode 100644 index 163b9efb1ea..00000000000 --- a/addons/lunch_hr/i18n/lunch_hr.pot +++ /dev/null @@ -1,42 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * lunch_hr -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server saas~12.1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-12-24 10:59+0000\n" -"PO-Revision-Date: 2018-12-24 10:59+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: lunch_hr -#: model_terms:ir.ui.view,arch_db:lunch_hr.lunch_product_view_search_inherit -msgid "Available at current location" -msgstr "" - -#. module: lunch_hr -#: model:ir.model,name:lunch_hr.model_lunch_product -msgid "Lunch Product" -msgstr "" - -#. module: lunch_hr -#: model:ir.model,name:lunch_hr.model_lunch_supplier -msgid "Lunch Supplier" -msgstr "" - -#. module: lunch_hr -#: model:ir.model.fields,field_description:lunch_hr.field_lunch_product__is_available_at -msgid "Product Availability" -msgstr "" - -#. module: lunch_hr -#: model:ir.model.fields,field_description:lunch_hr.field_lunch_supplier__available_location_ids -msgid "This Supplier is available at" -msgstr "" - 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 6a6319842fd..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, str): - 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_id', '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> |
