diff --git a/addons/hr_holidays/models/hr_holidays.py b/addons/hr_holidays/models/hr_holidays.py index 9cf166a911f..003d4ed26f9 100644 --- a/addons/hr_holidays/models/hr_holidays.py +++ b/addons/hr_holidays/models/hr_holidays.py @@ -439,6 +439,23 @@ class Holidays(models.Model): else: holiday.action_validate() + @api.multi + def _prepare_create_by_category(self, employee): + self.ensure_one() + values = { + 'name': self.name, + 'type': self.type, + 'holiday_type': 'employee', + 'holiday_status_id': self.holiday_status_id.id, + 'date_from': self.date_from, + 'date_to': self.date_to, + 'notes': self.notes, + 'number_of_days_temp': self.number_of_days_temp, + 'parent_id': self.id, + 'employee_id': employee.id + } + return values + @api.multi def action_validate(self): if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'): @@ -479,18 +496,7 @@ class Holidays(models.Model): elif holiday.holiday_type == 'category': leaves = self.env['hr.holidays'] for employee in holiday.category_id.employee_ids: - values = { - 'name': holiday.name, - 'type': holiday.type, - 'holiday_type': 'employee', - 'holiday_status_id': holiday.holiday_status_id.id, - 'date_from': holiday.date_from, - 'date_to': holiday.date_to, - 'notes': holiday.notes, - 'number_of_days_temp': holiday.number_of_days_temp, - 'parent_id': holiday.id, - 'employee_id': employee.id - } + values = holiday._prepare_create_by_category(employee) leaves += self.with_context(mail_notify_force_send=False).create(values) # TODO is it necessary to interleave the calls? leaves.action_approve() diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index de4dda4c8fc..43d76a13ce8 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -125,7 +125,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, { "click .o_mail_request_permission": function (event) { event.preventDefault(); this.$(".o_mail_annoying_notification_bar").slideUp(); - var def = window.Notification.requestPermission(); + var def = window.Notification && window.Notification.requestPermission(); if (def) { def.then(function (value) { if (value === 'denied') { diff --git a/addons/mail/static/src/js/utils.js b/addons/mail/static/src/js/utils.js index 3bc730d15e8..b4e0c01a23a 100644 --- a/addons/mail/static/src/js/utils.js +++ b/addons/mail/static/src/js/utils.js @@ -7,7 +7,7 @@ var web_client = require('web.web_client'); function send_notification(title, content) { - if (Notification && Notification.permission === "granted") { + if (window.Notification && Notification.permission === "granted") { if (bus.is_master) { _send_native_notification(title, content); } diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 864cde778fd..590527ef2cd 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -531,7 +531,7 @@ class PaymentTransaction(models.Model): ref_suffix = 1 init_ref = reference while self.env['payment.transaction'].sudo().search_count([('reference', '=', reference)]): - reference = init_ref + '-' + str(ref_suffix) + reference = init_ref + 'x' + str(ref_suffix) ref_suffix += 1 return reference diff --git a/addons/point_of_sale/tests/test_point_of_sale_flow.py b/addons/point_of_sale/tests/test_point_of_sale_flow.py index a22f5addc55..1f63c5a02b6 100644 --- a/addons/point_of_sale/tests/test_point_of_sale_flow.py +++ b/addons/point_of_sale/tests/test_point_of_sale_flow.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- -import datetime import time -import os import odoo -from odoo import fields, tools, report as odoo_report -from odoo.tools import float_compare, mute_logger, test_reports - +from odoo import fields +from odoo.tools import float_compare, mute_logger from odoo.addons.point_of_sale.tests.common import TestPointOfSaleCommon - @odoo.tests.common.at_install(False) @odoo.tests.common.post_install(True) class TestPointOfSaleFlow(TestPointOfSaleCommon): @@ -416,17 +412,28 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): """ Simulation of sales coming from the interface, even after closing the session """ + FROMPRODUCT = object() + + def compute_tax(product, price, taxes=FROMPRODUCT, qty=1): + if taxes is FROMPRODUCT: + taxes = product.taxes_id + currency = self.pos_config.pricelist_id.currency_id + taxes = taxes.compute_all(price, currency, qty, product=product)['taxes'] + untax = price * qty + return untax, sum(tax.get('amount', 0.0) for tax in taxes) + # I click on create a new session button self.pos_config.open_session_cb() current_session = self.pos_config.current_session_id num_starting_orders = len(current_session.order_ids) + untax, atax = compute_tax(self.carotte, 0.9) carrot_order = {'data': - {'amount_paid': 0.9, + {'amount_paid': untax + atax, 'amount_return': 0, - 'amount_tax': 0, - 'amount_total': 0.9, + 'amount_tax': atax, + 'amount_total': untax + atax, 'creation_date': fields.Datetime.now(), 'fiscal_position_id': False, 'lines': [[0, @@ -437,7 +444,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'price_unit': 0.9, 'product_id': self.carotte.id, 'qty': 1, - 'tax_ids': []}]], + 'tax_ids': [(6, 0, self.carotte.taxes_id.ids)]}]], 'name': 'Order 00042-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, @@ -445,7 +452,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'statement_ids': [[0, 0, {'account_id': self.env.user.partner_id.property_account_receivable_id.id, - 'amount': 0.9, + 'amount': untax + atax, 'journal_id': self.pos_config.journal_ids[0].id, 'name': fields.Datetime.now(), 'statement_id': current_session.statement_ids[0].id}]], @@ -454,11 +461,12 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'id': '00042-003-0014', 'to_invoice': False} + untax, atax = compute_tax(self.courgette, 1.2) zucchini_order = {'data': - {'amount_paid': 1.2, + {'amount_paid': untax + atax, 'amount_return': 0, - 'amount_tax': 0, - 'amount_total': 1.1, + 'amount_tax': atax, + 'amount_total': untax + atax, 'creation_date': fields.Datetime.now(), 'fiscal_position_id': False, 'lines': [[0, @@ -469,7 +477,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'price_unit': 1.2, 'product_id': self.courgette.id, 'qty': 1, - 'tax_ids': []}]], + 'tax_ids': [(6, 0, self.courgette.taxes_id.ids)]}]], 'name': 'Order 00043-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, @@ -477,7 +485,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'statement_ids': [[0, 0, {'account_id': self.env.user.partner_id.property_account_receivable_id.id, - 'amount': 1.2, + 'amount': untax + atax, 'journal_id': self.pos_config.journal_ids[0].id, 'name': fields.Datetime.now(), 'statement_id': current_session.statement_ids[0].id}]], @@ -486,11 +494,12 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'id': '00043-003-0014', 'to_invoice': False} + untax, atax = compute_tax(self.onions, 1.28) onions_order = {'data': - {'amount_paid': 1.28, + {'amount_paid': untax + atax, 'amount_return': 0, - 'amount_tax': 0, - 'amount_total': 1.28, + 'amount_tax': atax, + 'amount_total': untax + atax, 'creation_date': fields.Datetime.now(), 'fiscal_position_id': False, 'lines': [[0, @@ -509,7 +518,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'statement_ids': [[0, 0, {'account_id': self.env.user.partner_id.property_account_receivable_id.id, - 'amount': 1.28, + 'amount': untax + atax, 'journal_id': self.pos_config.journal_ids[0].id, 'name': fields.Datetime.now(), 'statement_id': current_session.statement_ids[0].id}]], @@ -518,7 +527,6 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'id': '00044-003-0014', 'to_invoice': False} - # I create an order on an open session self.PosOrder.create_from_ui([carrot_order]) self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Submitted order not encoded") diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index afe89d3cebd..016417e1372 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -982,7 +982,8 @@ class ProcurementOrder(models.Model): cache = {} res = [] for procurement in self: - suppliers = procurement.product_id.seller_ids.filtered(lambda r: not r.product_id or r.product_id == procurement.product_id) + suppliers = procurement.product_id.seller_ids\ + .filtered(lambda r: (not r.company_id or r.company_id == procurement.company_id) and (not r.product_id or r.product_id == procurement.product_id)) if not suppliers: procurement.message_post(body=_('No vendor associated to product %s. Please set one to fix this procurement.') % (procurement.product_id.name)) continue diff --git a/addons/sale/models/sale_analytic.py b/addons/sale/models/sale_analytic.py index e937292d818..ef58a8e44aa 100644 --- a/addons/sale/models/sale_analytic.py +++ b/addons/sale/models/sale_analytic.py @@ -147,7 +147,7 @@ class AccountAnalyticLine(models.Model): @api.multi def unlink(self): - so_lines = self.mapped('so_line') + so_lines = self.sudo().mapped('so_line') res = super(AccountAnalyticLine, self).unlink() - so_lines.with_context(force_so_lines=so_lines).sudo()._compute_analytic() + so_lines.with_context(force_so_lines=so_lines)._compute_analytic() return res diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js index 5c02eb2450d..1dbb601889a 100644 --- a/addons/web_kanban/static/src/js/kanban_view.js +++ b/addons/web_kanban/static/src/js/kanban_view.js @@ -304,7 +304,7 @@ var KanbanView = View.extend({ return this._super(action); }, has_active_field: function() { - return this.fields.active; + return this.fields_view.fields.active; }, _is_quick_create_enabled: function() { if(!_.contains(['char', 'boolean', 'many2one'], this.fields[this.group_by_field].type)){ diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index e7a6bff5775..7e4e3c42bcf 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -463,7 +463,15 @@ class Website(models.Model): tx_id = request.session.get('sale_transaction_id') if tx_id: transaction = self.env['payment.transaction'].sudo().browse(tx_id) - if transaction.state != 'cancel': + # Ugly hack for SIPS: SIPS does not allow to reuse a payment reference, even if the + # payment was not not proceeded. For example: + # - Select SIPS for payment + # - Be redirected to SIPS website + # - Go back to eCommerce without paying + # - Be redirected to SIPS website again => error + # Since there is no link module between 'website_sale' and 'payment_sips', we prevent + # here to reuse any previous transaction for SIPS. + if transaction.state != 'cancel' and transaction.acquirer_id.provider != 'sips': return transaction else: request.session['sale_transaction_id'] = False diff --git a/odoo/cli/shell.py b/odoo/cli/shell.py index 6d0b0d5bd01..1e847800956 100644 --- a/odoo/cli/shell.py +++ b/odoo/cli/shell.py @@ -14,6 +14,25 @@ from . import Command _logger = logging.getLogger(__name__) +""" + Shell exit behaviors + ==================== + + Legend: + stop = The REPL main loop stop. + raise = Exception raised. + loop = Stay in REPL. + + Shell | ^D | exit() | quit() | sys.exit() | raise SystemExit() +---------------------------------------------------------------------- + python | stop | raise | raise | raise | raise + ipython | stop | stop | stop | loop | loop + ptpython | stop | raise | raise | raise | raise + bpython | stop | stop | stop | stop | stop + +""" + + def raise_keyboard_interrupt(*a): raise KeyboardInterrupt() @@ -95,6 +114,7 @@ class Shell(Command): local_vars['env'] = env local_vars['self'] = env.user self.console(local_vars) + cr.rollback() else: self.console(local_vars)