diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 2fe9f9536a8..99841241994 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1417,7 +1417,14 @@ class AccountInvoiceTax(models.Model): currency_id = fields.Many2one('res.currency', related='invoice_id.currency_id', store=True, readonly=True) base = fields.Monetary(string='Base', compute='_compute_base_amount', store=True) - + # DO NOT FORWARD-PORT!!! ONLY FOR v10 + def create(self, vals): + inv_tax = super(AccountInvoiceTax, self).create(vals) + # Workaround to make sure the tax amount is rounded to the currency precision since the ORM + # won't round it automatically at creation. + if inv_tax.company_id.tax_calculation_rounding_method == 'round_globally': + inv_tax.amount = inv_tax.currency_id.round(inv_tax.amount) + return inv_tax class AccountPaymentTerm(models.Model): diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 1c35f91a295..286a979da56 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -453,7 +453,7 @@ class AccountMoveLine(models.Model): raise ValidationError(_("You cannot create journal items with a secondary currency without filling both 'currency' and 'amount currency' field.")) @api.multi - @api.constrains('amount_currency') + @api.constrains('amount_currency', 'debit', 'credit') def _check_currency_amount(self): for line in self: if line.amount_currency: diff --git a/addons/account_check_printing/models/account_payment.py b/addons/account_check_printing/models/account_payment.py index ef33dc05ae4..ab6623b3068 100644 --- a/addons/account_check_printing/models/account_payment.py +++ b/addons/account_check_printing/models/account_payment.py @@ -4,7 +4,7 @@ import math from odoo import models, fields, api, _ -from odoo.tools import amount_to_text_en, float_round +from odoo.tools import amount_to_text_en, float_round, float_is_zero from odoo.exceptions import UserError, ValidationError @@ -55,7 +55,7 @@ class AccountPayment(models.Model): check_amount_in_words = amount_to_text_en.amount_to_text(math.floor(amount), lang='en', currency='') check_amount_in_words = check_amount_in_words.replace(' and Zero Cent', '') # Ugh decimals = amount % 1 - if decimals >= 10**-2: + if not float_is_zero(decimals, precision_digits=2): check_amount_in_words += _(' and %s/100') % str(int(round(float_round(decimals*100, precision_rounding=1)))) return check_amount_in_words diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index a55ebc6c7d1..b6245a6793d 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -853,7 +853,7 @@ class Meeting(models.Model): @api.multi def _compute_color_partner(self): - for meeting in self: + for meeting in self.sudo(): meeting.color_partner_id = meeting.user_id.partner_id.id @api.constrains('start_datetime', 'stop_datetime', 'start_date', 'stop_date') diff --git a/addons/crm/report/crm_opportunity_report_views.xml b/addons/crm/report/crm_opportunity_report_views.xml index 610fb112fc4..bf6bcd83945 100644 --- a/addons/crm/report/crm_opportunity_report_views.xml +++ b/addons/crm/report/crm_opportunity_report_views.xml @@ -55,20 +55,20 @@ crm.opportunity.report [('probability', '=', 100)] - {'group_by': ['date_closed:month'],'col_group_by': ['create_date:month'], 'measures': ['__count']} + {'group_by': ['date_closed:month'],'col_group_by': ['create_date:month'], 'measures': ['__count__']} Opportunities Won Per Team crm.opportunity.report [('probability', '=', 100)] - {'group_by': ['team_id'], 'col_group_by': ['date_last_stage_update:month'], 'measures': ['expected revenue']} + {'group_by': ['team_id'], 'col_group_by': ['date_last_stage_update:month'], 'measures': ['expected_revenue']} Leads By Salespersons crm.opportunity.report - {'col_group_by': ['create_date:month'], 'group_by': ['user_id'], 'measures': ['__count']} + {'col_group_by': ['create_date:month'], 'group_by': ['user_id'], 'measures': ['__count__']} Won By Country @@ -81,7 +81,7 @@ Expected Revenue by Team crm.opportunity.report - {'group_by': ['create_date:month', 'team_id'], 'measures': ['expected_revenue', '__count']} + {'group_by': ['create_date:month', 'team_id'], 'measures': ['expected_revenue', '__count__']} Team Activities diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index ef0a090b817..2bd9aa2b26f 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -164,7 +164,7 @@ - + diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index c8b7241e5c5..9a7d421da88 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -9,12 +9,17 @@ class AccountAnalyticLine(models.Model): task_id = fields.Many2one('project.task', 'Task') project_id = fields.Many2one('project.project', 'Project', domain=[('allow_timesheets', '=', True)]) - department_id = fields.Many2one('hr.department', "Department", related='user_id.employee_ids.department_id', store=True, readonly=True) + department_id = fields.Many2one('hr.department', "Department", compute='_compute_department_id', store=True) @api.onchange('project_id') def onchange_project_id(self): self.task_id = False + @api.depends('user_id') + def _compute_department_id(self): + for line in self: + line.department_id = line.user_id.employee_ids[:1].department_id + @api.model def create(self, vals): if vals.get('project_id'): diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py index fc2724dd31d..2ef49b7e6fd 100644 --- a/addons/hw_scanner/controllers/main.py +++ b/addons/hw_scanner/controllers/main.py @@ -4,7 +4,7 @@ import logging import time from os import listdir -from os.path import join +from os.path import join, isdir from Queue import Queue, Empty from select import select from threading import Thread, Lock @@ -121,7 +121,7 @@ class Scanner(Thread): if not evdev: return [] - if not os.path.isdir(self.input_dir): + if not isdir(self.input_dir): return [] new_devices = [device for device in listdir(self.input_dir) diff --git a/addons/l10n_multilang/models/l10n_multilang.py b/addons/l10n_multilang/models/l10n_multilang.py index de0ef064bcd..fe2f48a79e2 100644 --- a/addons/l10n_multilang/models/l10n_multilang.py +++ b/addons/l10n_multilang/models/l10n_multilang.py @@ -37,11 +37,11 @@ class AccountChartTemplate(models.Model): 'type': 'model', 'res_id': out_ids[counter].id, 'lang': lang, - 'src': element.name, + 'src': element[in_field], 'value': value[element.id], }) else: - _logger.info('Language: %s. Translation from template: there is no translation available for %s!' % (lang, element.name)) + _logger.info('Language: %s. Translation from template: there is no translation available for %s!' % (lang, element[in_field])) counter += 1 return True diff --git a/addons/payment_authorize/models/payment.py b/addons/payment_authorize/models/payment.py index 2f5e8027f91..73c7c044e0a 100644 --- a/addons/payment_authorize/models/payment.py +++ b/addons/payment_authorize/models/payment.py @@ -126,8 +126,18 @@ class PaymentAcquirerAuthorize(models.Model): for field_name in mandatory_fields: if not data.get(field_name): error[field_name] = 'missing' - if data['cc_expiry'] and datetime.now().strftime('%y%m') > datetime.strptime(data['cc_expiry'], '%m / %y').strftime('%y%m'): - return False + if data['cc_expiry']: + # FIX we split the date into their components and check if there is two components containing only digits + # this fixes multiples crashes, if there was no space between the '/' and the components the code was crashing + # the code was also crashing if the customer was proving non digits to the date. + cc_expiry = [i.strip() for i in data['cc_expiry'].split('/')] + if len(cc_expiry) != 2 or any(not i.isdigit() for i in cc_expiry): + return False + try: + if datetime.now().strftime('%y%m') > datetime.strptime('/'.join(cc_expiry), '%m/%y').strftime('%y%m'): + return False + except ValueError: + return False return False if error else True @api.multi diff --git a/addons/purchase/data/mail_template_data.xml b/addons/purchase/data/mail_template_data.xml index ecba11ce056..35445697973 100644 --- a/addons/purchase/data/mail_template_data.xml +++ b/addons/purchase/data/mail_template_data.xml @@ -15,7 +15,7 @@ ${object.partner_id.lang} Dear -% if object.partner_id.is_company and object.child_ids: +% if object.partner_id.is_company and object.partner_id.child_ids: ${object.partner_id.child_ids[0].name} % else : ${object.partner_id.name} @@ -55,7 +55,7 @@ from ${object.company_id.name}. PO_${(object.name or '').replace('/','_')} ${object.partner_id.lang} Dear ${object.partner_id.name} +

Dear ${object.partner_id.name} % if object.partner_id.parent_id: (${object.partner_id.parent_id.name}) % endif diff --git a/addons/stock/data/stock_data.xml b/addons/stock/data/stock_data.xml index 078273fb32d..35f2c008729 100644 --- a/addons/stock/data/stock_data.xml +++ b/addons/stock/data/stock_data.xml @@ -145,7 +145,6 @@ watch your stock valuation, and track production lots (based on serial numbers). - WH diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py index 0f1c5f15419..283dc12ee5f 100644 --- a/addons/stock/models/stock_inventory.py +++ b/addons/stock/models/stock_inventory.py @@ -395,7 +395,8 @@ class InventoryLine(models.Model): if existings: raise UserError(_("You cannot have two inventory adjustements in state 'in Progess' with the same product" "(%s), same location(%s), same package, same owner and same lot. Please first validate" - "the first inventory adjustement with this product before creating another one.") % (res.product_id.name, res.location_id.name)) + "the first inventory adjustement with this product before creating another one.") % + (res.product_id.display_name, res.location_id.display_name)) return res def _get_quants(self): diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index ad1f0ea07e5..48dcf54d24f 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -21,7 +21,7 @@ class Warehouse(models.Model): # namedtuple used in helper methods generating values for routes Routing = namedtuple('Routing', ['from_loc', 'dest_loc', 'picking_type']) - name = fields.Char('Warehouse Name', index=True, required=True) + name = fields.Char('Warehouse Name', index=True, required=True, default=lambda self: self.env['res.company']._company_default_get('stock.inventory').name) active = fields.Boolean('Active', default=True) company_id = fields.Many2one( 'res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('stock.inventory'), diff --git a/addons/web/static/src/js/abstract_web_client.js b/addons/web/static/src/js/abstract_web_client.js index 1d9c270b6c3..8c82abf846c 100644 --- a/addons/web/static/src/js/abstract_web_client.js +++ b/addons/web/static/src/js/abstract_web_client.js @@ -120,12 +120,37 @@ var WebClient = Widget.extend({ // crash manager integration session.on('error', crash_manager, crash_manager.rpc_error); window.onerror = function (message, file, line, col, error) { - var traceback = error ? error.stack : ''; - crash_manager.show_error({ - type: _t("Odoo Client Error"), - message: message, - data: {debug: file + ':' + line + "\n" + _t('Traceback:') + "\n" + traceback} - }); + // Scripts injected in DOM (eg: google API's js files) won't return a clean error on window.onerror. + // The browser will just give you a 'Script error.' as message and nothing else for security issue. + // To enable onerror to work properly with CORS file, you should: + // 1. add crossorigin="anonymous" to your