diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-03-19 18:25:41 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-03-19 18:25:41 +0100 |
| commit | 82effcca83ddc7ebb69c68ff5a68ad401d3b9643 (patch) | |
| tree | 0af576b4ad82cb8d0896c6963abdeb0428b54fce | |
| parent | 0e898eae3506e1e4a6407e11097b58a60c6d7321 (diff) | |
| parent | caf6b4a7c7909349ab7c8f2e10bf86f5a9ff2a20 (diff) | |
[MERGE] forward port branch 10.0 up to caf6b4a7c79
24 files changed, 121 insertions, 36 deletions
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 @@ <field name="model_id">crm.opportunity.report</field> <field name="domain">[('probability', '=', 100)]</field> <field name="user_id" eval="False"/> - <field name="context">{'group_by': ['date_closed:month'],'col_group_by': ['create_date:month'], 'measures': ['__count']}</field> + <field name="context">{'group_by': ['date_closed:month'],'col_group_by': ['create_date:month'], 'measures': ['__count__']}</field> </record> <record id="filter_opportunity_opportunities_won_per_team" model="ir.filters"> <field name="name">Opportunities Won Per Team</field> <field name="model_id">crm.opportunity.report</field> <field name="domain">[('probability', '=', 100)]</field> <field name="user_id" eval="False"/> - <field name="context">{'group_by': ['team_id'], 'col_group_by': ['date_last_stage_update:month'], 'measures': ['expected revenue']}</field> + <field name="context">{'group_by': ['team_id'], 'col_group_by': ['date_last_stage_update:month'], 'measures': ['expected_revenue']}</field> </record> <record id="filter_opportunity_salesperson" model="ir.filters"> <field name="name">Leads By Salespersons</field> <field name="model_id">crm.opportunity.report</field> <field name="user_id" eval="False"/> - <field name="context">{'col_group_by': ['create_date:month'], 'group_by': ['user_id'], 'measures': ['__count']}</field> + <field name="context">{'col_group_by': ['create_date:month'], 'group_by': ['user_id'], 'measures': ['__count__']}</field> </record> <record id="filter_opportunity_country" model="ir.filters"> <field name="name">Won By Country</field> @@ -81,7 +81,7 @@ <field name="name">Expected Revenue by Team</field> <field name="model_id">crm.opportunity.report</field> <field name="user_id" eval="False"/> - <field name="context">{'group_by': ['create_date:month', 'team_id'], 'measures': ['expected_revenue', '__count']}</field> + <field name="context">{'group_by': ['create_date:month', 'team_id'], 'measures': ['expected_revenue', '__count__']}</field> </record> <record id="ir_filters_crm_opportunity_report_next_action" model="ir.filters"> <field name="name">Team Activities</field> 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 @@ <filter domain="[('state', '=', 'draft')]" string="To Submit" name="to_report" help="New Expense"/> <filter domain="[('state', '=', 'reported')]" string="Reported" name="submitted" help="Confirmed Expenses"/> <filter domain="[('state', '=', 'refused')]" string="Refused" name="refused" help="Refused Expenses"/> - <filter domain="[('state', '!=', 'cancel')]" string="Not Refused" name="uncancelled" help="Actual expense sheets, not the refused ones"/> + <filter domain="[('state', '!=', 'refused')]" string="Not Refused" name="uncancelled" help="Actual expense sheets, not the refused ones"/> <separator /> <filter string="My Team Expenses" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_expense.group_hr_expense_manager" help="Expenses of Your Team Member"/> <filter string="My Expenses" domain="[('employee_id.user_id', '=', uid)]"/> 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 @@ <field name="lang">${object.partner_id.lang}</field> <field name="body_html"><![CDATA[ <p>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}. <field name="report_name">PO_${(object.name or '').replace('/','_')}</field> <field name="lang">${object.partner_id.lang}</field> <field name="body_html"><![CDATA[ -<p>Dear ${object.partner_id.name} +<p>Dear ${object.partner_id.name} % if object.partner_id.parent_id: (<i>${object.partner_id.parent_id.name}</i>) % 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). <record id="warehouse0" model="stock.warehouse"> - <field model="res.company" name="name" search="[]" use="name"/> <field name="partner_id" ref="base.main_partner"/> <field name="code">WH</field> </record> 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 <script> tag loading the file + // 2. enabling 'Access-Control-Allow-Origin' on the server serving the file. + // Since in some case it wont be possible to to this, this handle should have the possibility to be + // handled by the script manipulating the injected file. For this, you will use window.onOriginError + // If it is not handled, we should display something clearer than the common crash_manager error dialog + // since it won't show anything except "Script error." + // This link will probably explain it better: https://blog.sentry.io/2016/05/17/what-is-script-error.html + if (!file && !line && !col) { + // Chrome and Opera set "Script error." on the `message` and hide the `error` + // Firefox handles the "Script error." directly. It sets the error thrown by the CORS file into `error` + if (window.onOriginError) { + window.onOriginError(); + delete window.onOriginError; + } else { + crash_manager.show_error({ + type: _t("Odoo Client Error"), + message: _t("Unknown CORS error"), + data: {debug: _t("An unknown CORS error occured. The error probably originates from a JavaScript file served from a different origin. (Opening your browser console might give you a hint on the error.)")}, + }); + } + } else { + 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}, + }); + } }; }, set_action_manager: function() { diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index ba7adfc5443..654efc01a76 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -457,7 +457,7 @@ return core.Class.extend({ this._stop_running_tour_timeout(); this.running_tour_timeout = setTimeout((function() { this._consume_tour(tour_name, _.str.sprintf("Tour %s failed at step %s", tour_name, step.trigger)); - }).bind(this), RUNNING_TOUR_TIMEOUT + this.running_step_delay); + }).bind(this), (step.timeout || RUNNING_TOUR_TIMEOUT) + this.running_step_delay); }, _stop_running_tour_timeout: function () { clearTimeout(this.running_tour_timeout); diff --git a/addons/website/static/src/js/website.backendDashboard.js b/addons/website/static/src/js/website.backendDashboard.js index 18307bba466..c3f2ef2ebfb 100644 --- a/addons/website/static/src/js/website.backendDashboard.js +++ b/addons/website/static/src/js/website.backendDashboard.js @@ -174,6 +174,10 @@ var Dashboard = Widget.extend(ControlPanelMixin, { $analytics_components.empty(); // 1. Authorize component var $analytics_auth = $('<div>').addClass('col-md-12'); + window.onOriginError = function () { + $analytics_components.find('.js_unauthorized_message').remove(); + self.display_unauthorized_message($analytics_components, 'not_initialized'); + }; gapi.analytics.auth.authorize({ container: $analytics_auth[0], clientid: client_id @@ -182,6 +186,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { self.handle_analytics_auth($analytics_components); gapi.analytics.auth.on('signIn', function() { + delete window.onOriginError; self.handle_analytics_auth($analytics_components); }); diff --git a/addons/website/static/src/xml/website.backend.xml b/addons/website/static/src/xml/website.backend.xml index ad7c4715378..d8db38684b7 100644 --- a/addons/website/static/src/xml/website.backend.xml +++ b/addons/website/static/src/xml/website.backend.xml @@ -38,6 +38,7 @@ <div t-name="website.unauthorized_analytics" class="col-xs-12 js_unauthorized_message mb16"> <span t-if="reason === 'not_connected'">You need to log in to your Google Account before: </span> <span t-if="reason === 'no_right'">You do not seem to have access to this Analytics Account.</span> + <span t-if="reason === 'not_initialized'">Google Analytics initialization failed. Maybe this domain is not whitelisted in your Google Analytics project for this client ID.</span> </div> <div t-name="website.ga_dialog_content"> diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 95e1528756e..a828a612f0e 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -193,7 +193,7 @@ class Website(models.Model): _inherit = 'website' pricelist_id = fields.Many2one('product.pricelist', compute='_compute_pricelist_id', string='Default Pricelist') - currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', string='Default Currency') + currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', related_sudo=False, string='Default Currency') salesperson_id = fields.Many2one('res.users', string='Salesperson') salesteam_id = fields.Many2one('crm.team', string='Sales Channel') pricelist_ids = fields.One2many('product.pricelist', compute="_compute_pricelist_ids", diff --git a/doc/_extensions/odoo_ext/layout.html b/doc/_extensions/odoo_ext/layout.html index 000019b32cf..183bf7599fc 100644 --- a/doc/_extensions/odoo_ext/layout.html +++ b/doc/_extensions/odoo_ext/layout.html @@ -290,29 +290,33 @@ <div class="col-xs-6 col-sm-4"> <span class="menu_title">Services</span> <ul> - <li><a href="http://www.odoo.com/pricing">Editions</a></li> - <li><a href="http://www.odoo.com/pricing-online">Cloud Pricing</a></li> - <li><a href="http://www.odoo.com/page/upgrade">Upgrade</a></li> + <li><a href="https://www.odoo.sh">Odoo Cloud Platform</a></li> + <li class="divider"></li> + <li><a href="http://www.odoo.com/help">Support</a></li> + <li><a href="https://upgrade.odoo.com">Upgrade</a></li> <li class="divider"></li> <li><a href="http://www.odoo.com/partners">Find a partner</a></li> <li><a href="http://www.odoo.com/page/become-a-partner">Become a partner</a></li> <li class="divider"></li> + <li><a href="http://training.odoo.com/courses/odoo-functional">Training Center</a></li> <li><a href="http://www.odoo.com/page/education-program">Education</a></li> <li class="divider"></li> <li><a href="http://www.odoo.com/page/security">Security</a></li> </ul> </div> - <div class="col-sm-4 mb64"> + <div class="col-xs-12 col-sm-4 mb64"> <span class="menu_title">About us</span> <ul> <li><a href="http://www.odoo.com/page/about-us">Our company</a></li> <li><a href="http://www.odoo.com/page/contactus">Contact</a></li> <li class="divider" /> <li><a href="http://www.odoo.com/event">Events</a></li> - <li><a href="http://www.odoo.com/blog/">Blog</a></li> + <li><a href="http://www.odoo.com/blog">Blog</a></li> <li><a href="http://www.odoo.com/blog/6">Customers</a></li> <li class="divider" /> <li><a href="http://www.odoo.com/jobs">Jobs</a></li> + <li class="divider" /> + <li><a href="http://www.odoo.com/page/legal">Legal</a> | <a href="http://www.odoo.com/privacy">Privacy</a></li> </ul> </div> </div> diff --git a/doc/cla/corporate/dajmi5.md b/doc/cla/corporate/dajmi5.md new file mode 100644 index 00000000000..e26ab73ef0d --- /dev/null +++ b/doc/cla/corporate/dajmi5.md @@ -0,0 +1,15 @@ +Croatia, 2017-10-31 + +DAJ MI 5 agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Davor Bojkić bole@dajmi5.com https://github.com/dajmi5 + +List of contributors: + +Davor Bojkić bole@dajmi5.com https://github.com/badbole
\ No newline at end of file diff --git a/doc/cla/individual/mpanarin.md b/doc/cla/individual/mpanarin.md new file mode 100644 index 00000000000..ea0a988978e --- /dev/null +++ b/doc/cla/individual/mpanarin.md @@ -0,0 +1,10 @@ +Ukraine, 2018-03-16 + +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, + +Panarin Mykhailo Ihorevich m.panarin@mobilunity.com https://github.com/mpanarin + diff --git a/doc/reference/orm.rst b/doc/reference/orm.rst index 00b284f9cb2..bec009411b7 100644 --- a/doc/reference/orm.rst +++ b/doc/reference/orm.rst @@ -630,6 +630,9 @@ Model Reference If set to ``False``, override :meth:`.init` to create the database table + + .. tip:: To create a model without any table, inherit + from ``odoo.models.AbstractModel`` .. attribute:: _table diff --git a/odoo/addons/base/res/res_currency_data.xml b/odoo/addons/base/res/res_currency_data.xml index e3088952ca0..f986c0a08b8 100644 --- a/odoo/addons/base/res/res_currency_data.xml +++ b/odoo/addons/base/res/res_currency_data.xml @@ -172,7 +172,7 @@ <record id="JPY" model="res.currency"> <field name="name">JPY</field> <field name="symbol">¥</field> - <field name="rounding">0.01</field> + <field name="rounding">1.00</field> <field name="active" eval="False"/> </record> |
