[MERGE] forward port branch 10.0 up to 5e6bb745e8

This commit is contained in:
Christophe Simonis
2018-03-06 12:28:30 +01:00
19 changed files with 130 additions and 32 deletions
+6
View File
@@ -9471,6 +9471,12 @@ msgstr ""
msgid "You cannot cancel an invoice which is partially paid. You need to unreconcile related payment entries first."
msgstr ""
#. module: account
#: code:addons/account/models/company.py:118
#, python-format
msgid "You cannot change the currency of the company since some journal items already exist"
msgstr ""
#. module: account
#: code:addons/account/models/account.py:138
#, python-format
+7 -1
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError, UserError
from datetime import timedelta
@@ -110,4 +110,10 @@ Best Regards,''')
company.reflect_code_prefix_change(company.cash_account_code_prefix, new_cash_code, digits)
if values.get('accounts_code_digits'):
company.reflect_code_digits_change(digits)
#forbid the change of currency_id if there are already some accounting entries existing
if 'currency_id' in values and values['currency_id'] != company.currency_id.id:
if self.env['account.move.line'].search([('company_id', '=', company.id)]):
raise UserError(_('You cannot change the currency of the company since some journal items already exist'))
return super(ResCompany, self).write(values)
+3 -1
View File
@@ -17,7 +17,9 @@ class TestPayment(AccountingTestCase):
self.currency_chf_id = self.env.ref("base.CHF").id
self.currency_usd_id = self.env.ref("base.USD").id
self.currency_eur_id = self.env.ref("base.EUR").id
self.env.ref('base.main_company').write({'currency_id': self.currency_eur_id})
company = self.env.ref('base.main_company')
self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [self.currency_eur_id, company.id])
self.product = self.env.ref("product.product_product_4")
self.payment_method_manual_in = self.env.ref("account.account_payment_method_manual_in")
self.payment_method_manual_out = self.env.ref("account.account_payment_method_manual_out")
+2 -1
View File
@@ -24,7 +24,8 @@ class TestReconciliation(AccountingTestCase):
self.currency_swiss_id = self.env.ref("base.CHF").id
self.currency_usd_id = self.env.ref("base.USD").id
self.currency_euro_id = self.env.ref("base.EUR").id
self.env.ref('base.main_company').write({'currency_id': self.currency_euro_id})
company = self.env.ref('base.main_company')
self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [self.currency_euro_id, company.id])
self.account_rcv = partner_agrolait.property_account_receivable_id or self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1)
self.account_rsa = partner_agrolait.property_account_payable_id or self.env['account.account'].search([('user_type_id', '=', self.env.ref('account.data_account_type_payable').id)], limit=1)
self.product = self.env.ref("product.product_product_4")
+14 -12
View File
@@ -912,6 +912,8 @@ class Lead(models.Model):
'nb_opportunities': 0,
}
today = fields.Date.from_string(fields.Date.context_today(self))
opportunities = self.search([('type', '=', 'opportunity'), ('user_id', '=', self._uid)])
for opp in opportunities:
@@ -919,28 +921,28 @@ class Lead(models.Model):
if opp.activity_date_deadline:
if opp.date_deadline:
date_deadline = fields.Date.from_string(opp.date_deadline)
if date_deadline == date.today():
if date_deadline == today:
result['closing']['today'] += 1
if date.today() <= date_deadline <= date.today() + timedelta(days=7):
if today <= date_deadline <= today + timedelta(days=7):
result['closing']['next_7_days'] += 1
if date_deadline < date.today() and not opp.date_closed:
if date_deadline < today and not opp.date_closed:
result['closing']['overdue'] += 1
# Next activities
for activity in opp.activity_ids:
date_deadline = fields.Date.from_string(activity.date_deadline)
if date_deadline == date.today():
if date_deadline == today:
result['activity']['today'] += 1
if date.today() <= date_deadline <= date.today() + timedelta(days=7):
if today <= date_deadline <= today + timedelta(days=7):
result['activity']['next_7_days'] += 1
if date_deadline < date.today() and not opp.date_closed:
if date_deadline < today and not opp.date_closed:
result['activity']['overdue'] += 1
# Won in Opportunities
if opp.date_closed and opp.stage_id.probability == 100:
date_closed = fields.Date.from_string(opp.date_closed)
if date.today().replace(day=1) <= date_closed <= date.today():
if today.replace(day=1) <= date_closed <= today:
if opp.planned_revenue:
result['won']['this_month'] += opp.planned_revenue
elif date.today() + relativedelta(months=-1, day=1) <= date_closed < date.today().replace(day=1):
elif today + relativedelta(months=-1, day=1) <= date_closed < today.replace(day=1):
if opp.planned_revenue:
result['won']['last_month'] += opp.planned_revenue
@@ -965,9 +967,9 @@ class Lead(models.Model):
for activity in activites_done:
if activity['date']:
date_act = fields.Date.from_string(activity['date'])
if date.today().replace(day=1) <= date_act <= date.today():
if today.replace(day=1) <= date_act <= today:
result['done']['this_month'] += 1
elif date.today() + relativedelta(months=-1, day=1) <= date_act < date.today().replace(day=1):
elif today + relativedelta(months=-1, day=1) <= date_act < today.replace(day=1):
result['done']['last_month'] += 1
# Meetings
@@ -982,9 +984,9 @@ class Lead(models.Model):
for meeting in meetings:
if meeting['start']:
start = datetime.strptime(meeting['start'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
if start == date.today():
if start == today:
result['meeting']['today'] += 1
if date.today() <= start <= date.today() + timedelta(days=7):
if today <= start <= today + timedelta(days=7):
result['meeting']['next_7_days'] += 1
result['done']['target'] = self.env.user.target_sales_done
@@ -8,7 +8,7 @@ SalesTeamDashboardView.include({
fetch_data: function() {
return new Model('crm.lead')
.call('retrieve_sales_dashboard', []);
.call('retrieve_sales_dashboard', [], {context: this.session.user_context || {}});
}
});
+1
View File
@@ -16,6 +16,7 @@ invoices from picking, the system is able to add and compute the shipping line.
'depends': ['sale_stock'],
'data': [
'security/ir.model.access.csv',
'security/delivery_security.xml',
'views/delivery_view.xml',
'views/partner_view.xml',
'views/product_template_view.xml',
+1
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import base_config_settings
import delivery_carrier
import delivery_price_rule
import product_template
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
class BaseConfigSettings(models.TransientModel):
_inherit = 'base.config.settings'
@api.multi
def set_auth_company_share_product(self):
self.ensure_one()
res = super(BaseConfigSettings, self).set_auth_company_share_product()
delivery_rule = self.env.ref('delivery.delivery_comp_rule', False)
if delivery_rule:
delivery_rule.write({'active': not bool(self.company_share_product)})
return res
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="delivery_comp_rule" model="ir.rule">
<field name="name">Delivery multi-company</field>
<field name="model_id" ref="model_delivery_carrier"/>
<field name="global" eval="True"/>
<field name="domain_force"> ['|',('product_id.company_id','=',user.company_id.id),('product_id.company_id','=',False)]</field>
<field name="active" eval="False"/>
</record>
</data>
</odoo>
+1 -2
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
@@ -31,7 +30,7 @@ class CrmLead(models.Model):
@api.model
def retrieve_sales_dashboard(self):
res = super(CrmLead, self).retrieve_sales_dashboard()
date_today = date.today()
date_today = fields.Date.from_string(fields.Date.context_today(self))
res['invoiced'] = {
'this_month': 0,
+1
View File
@@ -683,6 +683,7 @@ class SurveyLabel(models.Model):
value = fields.Char('Suggested value', translate=True, required=True)
quizz_mark = fields.Float('Score for this choice', help="A positive score indicates a correct choice; a negative or null score indicates a wrong answer")
@api.one
@api.constrains('question_id', 'question_id_2')
def _check_question_not_empty(self):
"""Ensure that field question_id XOR field question_id_2 is not null"""
+22
View File
@@ -15,6 +15,9 @@ var _t = core._t;
var ViewManager = Widget.extend(ControlPanelMixin, {
className: "o_view_manager_content",
custom_events: {
get_controller_context: '_onGetControllerContext',
},
/**
* Called each time the view manager is attached into the DOM
*/
@@ -474,6 +477,25 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
}
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
// ONLY FORWARDPORT THIS COMMIT UP TO SAAS-15
/**
* Handles a context request: provides to the caller the context of the
* active view.
*
* @private
* @param {OdooEvent} ev
* @param {function} ev.data.callback used to send the requested context
*/
_onGetControllerContext: function (ev) {
var controller = this.active_view && this.active_view.controller;
var context = controller && controller.get_context();
ev.data.callback(context);
},
});
return ViewManager;
@@ -976,6 +976,9 @@ var SelectCreateListView = ListView.extend({
* Search dialog (displays a list of records and permits to create a new one by switching to a form view)
*/
var SelectCreateDialog = ViewDialog.extend({
custom_events: {
get_controller_context: '_onGetControllerContext',
},
/**
* options:
* - initial_ids
@@ -1106,6 +1109,23 @@ var SelectCreateDialog = ViewDialog.extend({
return new FormViewDialog(this.__parentedParent, this.options).open();
},
on_view_list_loaded: function() {},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* Handles a context request: provides to the caller the context of the
* list view.
*
* @private
* @param {OdooEvent} ev
* @param {function} ev.data.callback used to send the requested context
*/
_onGetControllerContext: function (ev) {
var context = this.view_list.get_context();
ev.data.callback(context);
},
});
return {
+3 -2
View File
@@ -819,7 +819,7 @@ var PivotView = View.extend({
framework.blockUI();
var nbr_measures = this.active_measures.length,
headers = this.compute_headers(),
measure_row = nbr_measures > 1 ? _.last(headers) : [],
measure_row = nbr_measures >= 1 ? _.last(headers) : [],
rows = this.compute_rows(),
i, j, value;
headers[0].splice(0,1);
@@ -845,7 +845,8 @@ var PivotView = View.extend({
title: this.title,
};
if(table.measure_row.length + 1 > 256) {
c.show_message(_t("For Excel compatibility, data cannot be exported if there are more than 256 columns.\n\nTip: try to flip axis, filter further or reduce the number of measures."));
crash_manager.show_message(_t("For Excel compatibility, data cannot be exported if there are more than 256 columns.\n\nTip: try to flip axis, filter further or reduce the number of measures."));
framework.unblockUI();
return;
}
session.get_file({
@@ -93,13 +93,15 @@ return Widget.extend({
this.$inputs.first().focus();
return;
}
var search = this.searchview.build_search_data(),
view_manager = this.findAncestor(function (a) {
// HORRIBLE HACK. PLEASE SAVE ME FROM MYSELF (BUT IN A PAINLESS WAY IF POSSIBLE)
return 'active_view' in a;
}),
view_context = view_manager ? view_manager.active_view.controller.get_context() : {},
results = pyeval.sync_eval_domains_and_contexts({
var search = this.searchview.build_search_data();
// ONLY FORWARDPORT THIS COMMIT UP TO SAAS-15
var view_context;
this.trigger_up('get_controller_context', {
callback: function (ctx) {
view_context = ctx;
},
});
var results = pyeval.sync_eval_domains_and_contexts({
domains: search.domains,
contexts: search.contexts.concat(view_context || []),
group_by_seq: search.groupbys || [],
+1 -1
View File
@@ -351,7 +351,7 @@ class Slide(models.Model):
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
for record in self:
if record.datas and (not record.document_id or record.slide_type in ['document', 'presentation']):
record.embed_code = '<iframe src="%s/slides/embed/%s?page=1" allowFullScreen="true" height="%s" width="%s" frameborder="0"></iframe>' % (base_url, record.id, 315, 420)
record.embed_code = '<iframe src="%s/slides/embed/%s?page=1" class="o_wslides_iframe_viewer" allowFullScreen="true" height="%s" width="%s" frameborder="0"></iframe>' % (base_url, record.id, 315, 420)
elif record.slide_type == 'video' and record.document_id:
if not record.mime_type:
# embed youtube video
@@ -111,11 +111,12 @@ $(document).ready(function () {
},
});
$('iframe').ready(function() {
$('iframe.o_wslides_iframe_viewer').ready(function() {
// TODO : make it work. For now, once the iframe is loaded, the value of #page_count is
// still now set (the pdf is still loading)
var max_page = $('iframe').contents().find('#page_count').val();
new SlideSocialEmbed($(this), max_page).setElement($('.oe_slide_js_embed_code_widget'));
var $iframe = $(this);
var max_page = $iframe.contents().find('#page_count').val();
new SlideSocialEmbed($iframe, max_page).setElement($('.oe_slide_js_embed_code_widget'));
});
+5 -1
View File
@@ -17,7 +17,11 @@ class AutoVacuum(models.AbstractModel):
for mname in self.env:
model = self.env[mname]
if model.is_transient():
model._transient_vacuum(force=True)
try:
with cr.savepoint():
model._transient_vacuum(force=True)
except Exception as e:
_logger.warning("Failed to clean transient model %s\n%s", model, str(e))
@api.model
def _gc_user_logs(self):