diff --git a/addons/account_cash_basis_base_account/__init__.py b/addons/account_cash_basis_base_account/__init__.py new file mode 100644 index 00000000000..2971a2d9b7f --- /dev/null +++ b/addons/account_cash_basis_base_account/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import account_tax +import account_partial_reconcile diff --git a/addons/account_cash_basis_base_account/__openerp__.py b/addons/account_cash_basis_base_account/__openerp__.py new file mode 100644 index 00000000000..1b012f8c7a0 --- /dev/null +++ b/addons/account_cash_basis_base_account/__openerp__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name' : 'Tax Cash Basis Edit Base Account', + 'version' : '1.0', + 'summary': 'Add a custom account to handle base amount lines', + 'sequence': 5, + 'description': """ +Move the cash basis lines to another account. + """, + 'category': 'Accounting', + 'depends': ['account_tax_cash_basis'], + 'data': [ + 'views/account_tax_views.xml', + ], + 'installable': True, + 'auto_install': True, +} diff --git a/addons/account_cash_basis_base_account/account_partial_reconcile.py b/addons/account_cash_basis_base_account/account_partial_reconcile.py new file mode 100644 index 00000000000..6e51ac61e7a --- /dev/null +++ b/addons/account_cash_basis_base_account/account_partial_reconcile.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from openerp import api, models + + +class AccountPartialReconcileCashBasis(models.Model): + _inherit = 'account.partial.reconcile' + + def _get_tax_cash_basis_base_account(self, line, tax): + if tax.cash_basis_base_account_id: + return tax.cash_basis_base_account_id + return super(AccountPartialReconcileCashBasis, self)._get_tax_cash_basis_base_account(line, tax) diff --git a/addons/account_cash_basis_base_account/account_tax.py b/addons/account_cash_basis_base_account/account_tax.py new file mode 100644 index 00000000000..d8b6c4ca87f --- /dev/null +++ b/addons/account_cash_basis_base_account/account_tax.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from openerp import api, fields, models + + +class AccountTax(models.Model): + _inherit = 'account.tax' + + cash_basis_base_account_id = fields.Many2one('account.account', domain=[('deprecated', '=', False)], + string='Base Tax Received Account', + help='Account that will be set on lines created in cash basis journal entry and used to keep track of the tax base amount.') diff --git a/addons/account_cash_basis_base_account/views/account_tax_views.xml b/addons/account_cash_basis_base_account/views/account_tax_views.xml new file mode 100644 index 00000000000..68b076bf853 --- /dev/null +++ b/addons/account_cash_basis_base_account/views/account_tax_views.xml @@ -0,0 +1,15 @@ + + + + + tax.form.cash.basis.base.account.inherit + account.tax + + + + + + + + + diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py index 2dfcd06835a..b9dce93cdc9 100644 --- a/addons/account_voucher/models/account_voucher.py +++ b/addons/account_voucher/models/account_voucher.py @@ -286,6 +286,9 @@ class AccountVoucher(models.Model): #create one move line per voucher line where amount is not 0.0 if not line.price_subtotal: continue + line_subtotal = line.price_subtotal + if self.voucher_type == 'sale': + line_subtotal = -1 * line.price_subtotal # convert the amount set on the voucher line into the currency of the voucher's company # this calls res_curreny.compute() with the right context, # so that it will take either the rate on the voucher if it is relevant or will use the default behaviour @@ -302,7 +305,7 @@ class AccountVoucher(models.Model): 'debit': abs(amount) if self.voucher_type == 'purchase' else 0.0, 'date': self.account_date, 'tax_ids': [(4,t.id) for t in line.tax_ids], - 'amount_currency': line.price_subtotal if current_currency != company_currency else 0.0, + 'amount_currency': line_subtotal if current_currency != company_currency else 0.0, 'currency_id': company_currency != current_currency and current_currency or False, 'payment_id': self._context.get('payment_id'), } diff --git a/addons/hr_attendance/static/src/js/greeting_message.js b/addons/hr_attendance/static/src/js/greeting_message.js index c0f120983ad..f2efc987d03 100644 --- a/addons/hr_attendance/static/src/js/greeting_message.js +++ b/addons/hr_attendance/static/src/js/greeting_message.js @@ -41,11 +41,18 @@ var GreetingMessage = Widget.extend(BarcodeHandlerMixin, { if (this.next_action != 'hr_attendance.hr_attendance_action_kiosk_mode' && this.next_action.tag != 'hr_attendance_kiosk_mode') { this.stop_listening(); } + this.attendance = action.attendance; + // We receive the check in/out times in UTC + // This widget only deals with display, which should be in browser's TimeZone + this.attendance.check_in = this.attendance.check_in && moment.utc(this.attendance.check_in).local(); + this.attendance.check_out = this.attendance.check_out && moment.utc(this.attendance.check_out).local(); + this.previous_attendance_change_date = action.previous_attendance_change_date && moment.utc(action.previous_attendance_change_date).local(); + // check in/out times displayed in the greeting message template. - this.attendance.check_in_time = (new Date((new Date(this.attendance.check_in)).valueOf() - (new Date()).getTimezoneOffset()*60*1000)).toTimeString().slice(0,8); - this.attendance.check_out_time = this.attendance.check_out && (new Date((new Date(this.attendance.check_out)).valueOf() - (new Date()).getTimezoneOffset()*60*1000)).toTimeString().slice(0,8); - this.previous_attendance_change_date = action.previous_attendance_change_date; + this.format_time = 'HH:mm:ss'; + this.attendance.check_in_time = this.attendance.check_in && this.attendance.check_in.format(this.format_time); + this.attendance.check_out_time = this.attendance.check_out && this.attendance.check_out.format(this.format_time); this.employee_name = action.employee_name; }, @@ -57,13 +64,13 @@ var GreetingMessage = Widget.extend(BarcodeHandlerMixin, { welcome_message: function() { var self = this; - var now = new Date((new Date(this.attendance.check_in)).valueOf() - (new Date()).getTimezoneOffset()*60*1000); + var now = this.attendance.check_in.clone(); this.return_to_main_menu = setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, 5000); - if (now.getHours() < 5) { + if (now.hours() < 5) { this.$('.o_hr_attendance_message_message').append(_t("Good night")); - } else if (now.getHours() < 12) { - if (now.getHours() < 8 && Math.random() < 0.3) { + } else if (now.hours() < 12) { + if (now.hours() < 8 && Math.random() < 0.3) { if (Math.random() < 0.75) { this.$('.o_hr_attendance_message_message').append(_t("The early bird catches the worm")); } else { @@ -72,16 +79,16 @@ var GreetingMessage = Widget.extend(BarcodeHandlerMixin, { } else { this.$('.o_hr_attendance_message_message').append(_t("Good morning")); } - } else if (now.getHours() < 17){ + } else if (now.hours() < 17){ this.$('.o_hr_attendance_message_message').append(_t("Good afternoon")); - } else if (now.getHours() < 23){ + } else if (now.hours() < 23){ this.$('.o_hr_attendance_message_message').append(_t("Good evening")); } else { this.$('.o_hr_attendance_message_message').append(_t("Good night")); } if(this.previous_attendance_change_date){ - var last_check_out_date = new Date((new Date(this.previous_attendance_change_date)).valueOf() - (new Date()).getTimezoneOffset()*60*1000); - if(now.valueOf() - last_check_out_date.valueOf() > 1000*60*60*24*7){ + var last_check_out_date = this.previous_attendance_change_date.clone(); + if(now - last_check_out_date > 24*7*60*60*1000){ this.$('.o_hr_attendance_random_message').html(_t("Glad to have you back, it's been a while!")); } else { if(Math.random() < 0.02){ @@ -93,33 +100,33 @@ var GreetingMessage = Widget.extend(BarcodeHandlerMixin, { farewell_message: function() { var self = this; - var now = new Date((new Date(this.attendance.check_out)).valueOf() - (new Date()).getTimezoneOffset()*60*1000); + var now = this.attendance.check_out.clone(); this.return_to_main_menu = setTimeout( function() { self.do_action(self.next_action, {clear_breadcrumbs: true}); }, 5000); if(this.previous_attendance_change_date){ - var last_check_in_date = new Date((new Date(this.previous_attendance_change_date)).valueOf() - (new Date()).getTimezoneOffset()*60*1000); - if(now.valueOf() - last_check_in_date.valueOf() > 1000*60*60*12){ + var last_check_in_date = this.previous_attendance_change_date.clone(); + if(now - last_check_in_date > 1000*60*60*12){ this.$('.o_hr_attendance_warning_message').append(_t("Warning! Last check in was over 12 hours ago.
If this isn't right, please contact Human Resources.")); clearTimeout(this.return_to_main_menu); this.stop_listening(); - } else if(now.valueOf() - last_check_in_date.valueOf() > 1000*60*60*8){ + } else if(now - last_check_in_date > 1000*60*60*8){ this.$('.o_hr_attendance_random_message').html(_t("Another good day's work! See you soon!")); } } - if (now.getHours() < 12) { + if (now.hours() < 12) { this.$('.o_hr_attendance_message_message').append(_t("Have a good day!")); - } else if (now.getHours() < 14) { + } else if (now.hours() < 14) { this.$('.o_hr_attendance_message_message').append(_t("Have a nice lunch!")); if (Math.random() < 0.05) { this.$('.o_hr_attendance_random_message').html(_t("Eat breakfast as a king, lunch as a merchant and supper as a beggar")); } else if (Math.random() < 0.06) { this.$('.o_hr_attendance_random_message').html(_t("An apple a day keeps the doctor away")); } - } else if (now.getHours() < 17) { + } else if (now.hours() < 17) { this.$('.o_hr_attendance_message_message').append(_t("Have a good afternoon")); } else { - if (now.getHours() < 18 && Math.random() < 0.2) { + if (now.hours() < 18 && Math.random() < 0.2) { this.$('.o_hr_attendance_message_message').append(_t("Early to bed and early to rise, makes a man healthy, wealthy and wise")); } else { this.$('.o_hr_attendance_message_message').append(_t("Have a good evening")); diff --git a/addons/hr_recruitment/data/hr_recruitment_data.xml b/addons/hr_recruitment/data/hr_recruitment_data.xml index faf6c6b897c..f2141e20a32 100644 --- a/addons/hr_recruitment/data/hr_recruitment_data.xml +++ b/addons/hr_recruitment/data/hr_recruitment_data.xml @@ -21,7 +21,7 @@ Applicant: Refuse Application refused - ${object.email_from} + ${object.email_from | safe} ${object.partner_id and object.partner_id.id or ''} ${object.partner_id and object.partner_id.lang or ''} @@ -37,7 +37,7 @@ Unfortunately, your profile does not match with our needs

Applicant: Interest Application approved - ${object.email_from} + ${object.email_from | safe} ${object.partner_id and object.partner_id.id or ''} ${object.partner_id and object.partner_id.lang or ''} diff --git a/addons/l10n_ca/data/l10n_ca_chart_data.xml b/addons/l10n_ca/data/l10n_ca_chart_data.xml index 8610079a78f..64b7849d9f2 100644 --- a/addons/l10n_ca/data/l10n_ca_chart_data.xml +++ b/addons/l10n_ca/data/l10n_ca_chart_data.xml @@ -29,7 +29,7 @@ 1181 - + GST receivable @@ -37,7 +37,7 @@ 1182 - + PST/QST receivable @@ -45,7 +45,7 @@ 11831 - + HST receivable - 13% @@ -53,7 +53,7 @@ 11832 - + HST receivable - 14% @@ -61,7 +61,7 @@ 11833 - + HST receivable - 15% @@ -79,7 +79,7 @@ 2131 - + GST to pay @@ -87,7 +87,7 @@ 2132 - + PST/QST to pay @@ -95,7 +95,7 @@ 21331 - + HST to pay - 13% @@ -103,7 +103,7 @@ 21332 - + HST to pay - 14% @@ -111,7 +111,7 @@ 21333 - + HST to pay - 15% diff --git a/addons/l10n_fr_pos_cert/models/account_bank_statement.py b/addons/l10n_fr_pos_cert/models/account_bank_statement.py index 28afbf0d549..f0753885bc1 100644 --- a/addons/l10n_fr_pos_cert/models/account_bank_statement.py +++ b/addons/l10n_fr_pos_cert/models/account_bank_statement.py @@ -6,6 +6,7 @@ from openerp.exceptions import UserError class AccountBankStatement(models.Model): _inherit = 'account.bank.statement' + @api.multi def unlink(self): for statement in self.filtered(lambda s: s.company_id._is_accounting_unalterable() and s.journal_id.journal_user): raise UserError(_('You cannot modify anything on a bank statement (name: %s) that was created by point of sale operations.') % (statement.name,)) @@ -15,6 +16,7 @@ class AccountBankStatement(models.Model): class AccountBankStatementLine(models.Model): _inherit = 'account.bank.statement.line' + @api.multi def unlink(self): for line in self.filtered(lambda s: s.company_id._is_accounting_unalterable() and s.journal_id.journal_user): raise UserError(_('You cannot modify anything on a bank statement line (name: %s) that was created by point of sale operations.') % (line.name,)) diff --git a/addons/payment_stripe/models/payment.py b/addons/payment_stripe/models/payment.py index 72c797e6c7f..72c02fc5aed 100644 --- a/addons/payment_stripe/models/payment.py +++ b/addons/payment_stripe/models/payment.py @@ -40,16 +40,16 @@ class PaymentAcquirerStripe(models.Model): stripe_tx_values = dict(tx_values) temp_stripe_tx_values = { 'company': self.company_id.name, - 'amount': tx_values.get('amount'), - 'currency': tx_values.get('currency') and tx_values.get('currency').name or '', - 'currency_id': tx_values.get('currency') and tx_values.get('currency').id or '', - 'address_line1': tx_values['partner_address'], - 'address_city': tx_values['partner_city'], - 'address_country': tx_values['partner_country'] and tx_values['partner_country'].name or '', - 'email': tx_values['partner_email'], - 'address_zip': tx_values['partner_zip'], - 'name': tx_values['partner_name'], - 'phone': tx_values['partner_phone'], + 'amount': tx_values['amount'], # Mandatory + 'currency': tx_values['currency'].name, # Mandatory anyway + 'currency_id': tx_values['currency'].id, # same here + 'address_line1': tx_values.get('partner_address'), # Any info of the partner is not mandatory + 'address_city': tx_values.get('partner_city'), + 'address_country': tx_values.get('partner_country') and tx_values.get('partner_country').name or '', + 'email': tx_values.get('partner_email'), + 'address_zip': tx_values.get('partner_zip'), + 'name': tx_values.get('partner_name'), + 'phone': tx_values.get('partner_phone'), } temp_stripe_tx_values['returndata'] = stripe_tx_values.pop('return_url', '') diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py index 1c166f365e4..ef17411513a 100644 --- a/addons/point_of_sale/tests/test_frontend.py +++ b/addons/point_of_sale/tests/test_frontend.py @@ -33,7 +33,7 @@ class TestUi(odoo.tests.HttpCase): # euro's. this will cause issues as the sales journal is in # USD, because of this all products would have a different # price - main_company.currency_id = env.ref('base.USD') + cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [env.ref('base.USD').id, main_company.id]) test_sale_journal = journal_obj.create({'name': 'Sales Journal - Test', 'code': 'TSJ', diff --git a/addons/project_issue/models/account_analytic_account.py b/addons/project_issue/models/account_analytic_account.py index 76d9439abce..511cfda9bfd 100644 --- a/addons/project_issue/models/account_analytic_account.py +++ b/addons/project_issue/models/account_analytic_account.py @@ -12,7 +12,7 @@ class AccountAnalyticAccount(models.Model): @api.multi def unlink(self): - if self.env['project.issue'].search_count([('project_id.analytic_account_id', 'in', self.ids)]): + if self.env['project.issue'].sudo().search_count([('project_id.analytic_account_id', 'in', self.ids)]): raise UserError(_('Please remove existing issues in the project linked to the accounts you want to delete.')) return super(AccountAnalyticAccount, self).unlink() diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 9ffdc1110a4..c12104fd337 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -53,7 +53,7 @@ class SaleOrder(models.Model): # Search for invoices which have been 'cancelled' (filter_refund = 'modify' in # 'account.invoice.refund') # use like as origin may contains multiple references (e.g. 'SO01, SO02') - refunds = invoice_ids.search([('origin', 'like', order.name)]).filtered(lambda r: r.type in ['out_invoice', 'out_refund']) + refunds = invoice_ids.search([('origin', 'like', order.name), ('company_id', '=', order.company_id.id)]).filtered(lambda r: r.type in ['out_invoice', 'out_refund']) invoice_ids |= refunds.filtered(lambda r: order.name in [origin.strip() for origin in r.origin.split(',')]) # Search for refunds as well refund_ids = self.env['account.invoice'].browse() diff --git a/addons/stock/report/report_stock_forecast.xml b/addons/stock/report/report_stock_forecast.xml index 800aab9240e..928be3c431c 100644 --- a/addons/stock/report/report_stock_forecast.xml +++ b/addons/stock/report/report_stock_forecast.xml @@ -45,7 +45,7 @@ form pivot,graph - {'search_default_pivot_by':1, 'search_default_graph_by':1, 'search_default_product_tmpl_id': active_id} + {'search_default_product_tmpl_id': active_id} @@ -55,7 +55,7 @@ form pivot,graph - {'search_default_pivot_by':1, 'search_default_graph_by':1, 'search_default_product_id': active_id} + {'search_default_product_id': active_id} diff --git a/addons/web/static/src/less/bootstrap_overridden.less b/addons/web/static/src/less/bootstrap_overridden.less index 7949971247a..e05d7fbbc67 100644 --- a/addons/web/static/src/less/bootstrap_overridden.less +++ b/addons/web/static/src/less/bootstrap_overridden.less @@ -103,3 +103,5 @@ overflow-x: initial; } } + +@icon-font-path: "/web/static/lib/bootstrap/fonts/"; diff --git a/addons/website/static/src/less/website.ui.less b/addons/website/static/src/less/website.ui.less index b92e86a3274..2d59324039a 100644 --- a/addons/website/static/src/less/website.ui.less +++ b/addons/website/static/src/less/website.ui.less @@ -51,3 +51,5 @@ body.o_connected_user { } } } + +@icon-font-path: "/web/static/lib/bootstrap/fonts/"; diff --git a/addons/website_event_sale/tests/test_ui.py b/addons/website_event_sale/tests/test_ui.py index c2e6f04f4a6..968ab6edcd9 100644 --- a/addons/website_event_sale/tests/test_ui.py +++ b/addons/website_event_sale/tests/test_ui.py @@ -12,7 +12,7 @@ class TestUi(odoo.tests.HttpCase): # - that main demo company is gelocated in US # - that this test awaits for hardcoded USDs amount # we have to force company currency as USDs only for this test - self.env.ref('base.main_company').write({'currency_id': self.env.ref('base.USD').id}) + self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [self.env.ref('base.USD').id, self.env.ref('base.main_company').id]) self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('event_buy_tickets')", "odoo.__DEBUG__.services['web_tour.tour'].tours.event_buy_tickets.ready", login="admin") def test_demo(self): diff --git a/doc/cla/corporate/acsone.md b/doc/cla/corporate/acsone.md index 1060d7ccc02..d4ee1947927 100644 --- a/doc/cla/corporate/acsone.md +++ b/doc/cla/corporate/acsone.md @@ -26,3 +26,4 @@ Stéphane Bidoul stephane.bidoul@acsone.eu https://github.com/sbidoul Thomas Binsfeld thomas.binsfeld@acsone.eu https://github.com/ThomasBinsfeld Benjamin Willig benjamin.willig@acsone.eu https://github.com/benwillig François Honoré francois.honore@acsone.eu https://github.com/acsonefho +Denis Robinet denis.robinet@acsone.eu https://github.com/RobinetDenisAcsone diff --git a/doc/cla/individual/thomas-tankengine.md b/doc/cla/individual/thomas-tankengine.md new file mode 100644 index 00000000000..ae3ece9b174 --- /dev/null +++ b/doc/cla/individual/thomas-tankengine.md @@ -0,0 +1,11 @@ +France, 2018-04-04, + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Thomas Lemaire thomas.lemaire@ndp-systemes.fr https://github.com/thomas-tankengine \ No newline at end of file diff --git a/odoo/addons/base/res/res_config.py b/odoo/addons/base/res/res_config.py index f1bb5ffff74..8b2a450e322 100644 --- a/odoo/addons/base/res/res_config.py +++ b/odoo/addons/base/res/res_config.py @@ -531,12 +531,14 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin) IrValues.set_default(model, field, value) # group fields: modify group / implied groups - for name, groups, implied_group in classified['group']: - if self[name]: - groups.write({'implied_ids': [(4, implied_group.id)]}) - else: - groups.write({'implied_ids': [(3, implied_group.id)]}) - implied_group.write({'users': [(3, user.id) for user in groups.mapped('users')]}) + with self.env.norecompute(): + for name, groups, implied_group in classified['group']: + if self[name]: + groups.write({'implied_ids': [(4, implied_group.id)]}) + else: + groups.write({'implied_ids': [(3, implied_group.id)]}) + implied_group.write({'users': [(3, user.id) for user in groups.mapped('users')]}) + self.recompute() # other fields: execute all methods that start with 'set_' for method in dir(self):