[MERGE] forward port branch saas-11.3 up to 4aa153e65c

This commit is contained in:
Christophe Simonis
2019-01-18 10:58:41 +01:00
10 changed files with 174 additions and 21 deletions
+45 -1
View File
@@ -81,7 +81,7 @@
You can not log timesheets on this project since is linked to an inactive analytic account. Please change it, or reactivate the current one to timesheet on the project.
</div>
</group>
<field name="timesheet_ids" attrs="{'invisible': [('analytic_account_active', '=', False)]}" context="{'default_project_id': project_id, 'default_name':''}">
<field name="timesheet_ids" mode="tree,kanban" attrs="{'invisible': [('analytic_account_active', '=', False)]}" context="{'default_project_id': project_id, 'default_name':''}">
<tree editable="bottom" string="Timesheet Activities" default_order="date">
<field name="date"/>
<field name="user_id" invisible="1"/>
@@ -90,6 +90,50 @@
<field name="unit_amount" widget="timesheet_uom"/>
<field name="project_id" invisible="1"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="date"/>
<field name="user_id"/>
<field name="employee_id"/>
<field name="name"/>
<field name="unit_amount"/>
<field name="project_id"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
<div class="row">
<div class="col-xs-6">
<strong><span><t t-esc="record.employee_id.value"/></span></strong>
</div>
<div class="col-xs-6 pull-right text-right">
<strong><t t-esc="record.date.value"/></strong>
</div>
</div>
<div class="row">
<div class="col-xs-6 text-muted">
<span><t t-esc="record.name.value"/></span>
</div>
<div class="col-xs-6">
<span class="pull-right text-right">
<field name="unit_amount" widget="float_time"/>
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
<form string="Timesheet Activities">
<sheet>
<group>
<field name="date"/>
<field name="user_id" invisible="1"/>
<field name="employee_id" required="1"/>
<field name="name"/>
<field name="unit_amount" string="Duration" widget="float_time"/>
<field name="project_id" invisible="1"/>
</group>
</sheet>
</form>
</field>
<group attrs="{'invisible': [('analytic_account_active', '=', False)]}">
<group class="oe_subtotal_footer oe_right" name="project_hours">
+1 -1
View File
@@ -22,7 +22,7 @@ function _parseAndTransform(nodes, transformFunction) {
// Suggested URL Javascript regex of http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
// Adapted to make http(s):// not required if (and only if) www. is given. So `should.notmatch` does not match.
var urlRegexp = /\b(?:https?:\/\/\d{1,3}(?:\.\d{1,3}){3}|(?:https?:\/\/|(?:www\.))[-a-z0-9@:%._+~#=]{2,256}\.[a-z]{2,13})\b(?:[-a-z0-9@:%_+.~#?&'$//=]*)/gi;
var urlRegexp = /\b(?:https?:\/\/\d{1,3}(?:\.\d{1,3}){3}|(?:https?:\/\/|(?:www\.))[-a-z0-9@:%._+~#=]{2,256}\.[a-z]{2,13})\b(?:[-a-z0-9@:%_+.~#?&'$//=;]*)/gi;
function linkify(text, attrs) {
attrs = attrs || {};
if (attrs.target === undefined) {
+2 -2
View File
@@ -415,7 +415,7 @@ class ProductProduct(models.Model):
# Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
# Use `load=False` to not call `name_get` for the `product_tmpl_id`
self.sudo().read(['name', 'default_code', 'product_tmpl_id', 'attribute_value_ids'], load=False)
self.sudo().read(['name', 'default_code', 'product_tmpl_id', 'attribute_value_ids', 'attribute_line_ids'], load=False)
product_template_ids = self.sudo().mapped('product_tmpl_id').ids
@@ -432,7 +432,7 @@ class ProductProduct(models.Model):
supplier_info_by_template.setdefault(r.product_tmpl_id, []).append(r)
for product in self.sudo():
# display only the attributes with multiple possible values on the template
variable_attributes = product.attribute_value_ids.filtered(lambda v: len(v.attribute_id.value_ids) > 1).mapped('attribute_id')
variable_attributes = product.attribute_line_ids.filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id')
variant = product.attribute_value_ids._variant_name(variable_attributes)
name = variant and "%s (%s)" % (product.name, variant) or product.name
+4
View File
@@ -251,6 +251,10 @@ class Survey(http.Controller):
def print_survey(self, survey, token=None, **post):
'''Display an survey in printable view; if <token> is set, it will
grab the answers of the user_input_id that has <token>.'''
if survey.auth_required and request.env.user == request.website.user_id:
return request.render("survey.auth_required", {'survey': survey, 'token': token})
return request.render('survey.survey_print',
{'survey': survey,
'token': token,
@@ -321,7 +321,7 @@ var PivotModel = AbstractModel.extend({
};
}
return {
colGroupBys: this.data.main_col.groupbys,
colGroupBys: this.data.main_col.root.groupbys,
context: this.data.context,
domain: this.data.domain,
compare: this.data.compare,
@@ -331,7 +331,7 @@ var PivotModel = AbstractModel.extend({
mainColWidth: this.data.main_col.width,
measures: this.data.measures,
rows: !isRaw && this._computeRows(),
rowGroupBys: this.data.main_row.groupbys,
rowGroupBys: this.data.main_row.root.groupbys,
sortedColumn: this.data.sorted_column,
};
},
@@ -432,22 +432,19 @@ var PivotModel = AbstractModel.extend({
return this._loadData();
}
var old_row_root = this.data.main_row.root;
var old_col_root = this.data.main_col.root;
var old_row = this.data.main_row;
var old_col = this.data.main_col;
return this._loadData().then(function () {
var new_groupby_length;
if (!('groupBy' in params) && !('pivot_row_groupby' in (params.context || {}))) {
// we only update the row groupbys according to the old groupbys
// if we don't have the key 'groupBy' in params. In that case,
// we want to have the full open state for the groupbys.
self._updateTree(old_row_root, self.data.main_row.root);
new_groupby_length = self._getHeaderDepth(self.data.main_row.root) - 1;
self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
self._updateTree(old_row.root, self.data.main_row.root);
self._updateMainGroupBys(old_row, self.data.main_row);
}
self._updateTree(old_col_root, self.data.main_col.root);
new_groupby_length = self._getHeaderDepth(self.data.main_col.root) - 1;
self.data.main_row.groupbys = old_row_root.groupbys.slice(0, new_groupby_length);
self._updateTree(old_col.root, self.data.main_col.root);
self._updateMainGroupBys(old_col, self.data.main_col);
});
},
/**
@@ -1052,9 +1049,6 @@ var PivotModel = AbstractModel.extend({
}
}
this.data.main_row.groupbys = rowGroupBys;
this.data.main_col.groupbys = colGroupBys;
main_row_header.other_root = main_col_header;
main_col_header.other_root = main_row_header;
@@ -1123,6 +1117,19 @@ var PivotModel = AbstractModel.extend({
this._traverseTree(root.children[i], f, arg1, arg2, arg3);
}
},
/**
* Updates the groupBys that the main[row | col] holds
*
* @private
* @param {Object} old: The main abstract header before reload
* @param {Object} main: The main abstract header after reload
*
*/
_updateMainGroupBys: function (old, main) {
var new_groupby_length = this._getHeaderDepth(main.root) - 1;
var new_groupby_list = old.root.groupbys.slice(0, new_groupby_length);
main.root.groupbys = new_groupby_list;
},
/**
* @param {Object} old_tree
* @param {Object} new_tree
@@ -196,6 +196,10 @@ return Widget.extend({
icon: 'fa-star',
field: {
get_context: function () {
var filterContext = filter.context;
if (typeof filter.context === 'string') {
filterContext = pyeval.eval('context', filter.context);
}
var sortParsed = JSON.parse(filter.sort || "[]");
var orderedBy = [];
_.each(sortParsed, function (sort) {
@@ -205,7 +209,7 @@ return Widget.extend({
});
});
return _.defaults({}, filter.context, {orderedBy : orderedBy});
return _.defaults({}, filterContext, {orderedBy : orderedBy});
},
get_groupby: function () { return [filter.context]; },
// facet is not used
@@ -913,6 +913,80 @@ QUnit.module('Views', {
pivot.destroy();
});
QUnit.test('correctly remove pivot_ keys from the context', function (assert) {
assert.expect(5);
this.data.partner.fields.amount = {string: "Amount", type: "float"};
// Equivalent to loading with default filter
var pivot = createView({
View: PivotView,
model: "partner",
data: this.data,
arch: '<pivot>' +
'<field name="date" interval="day" type="col"/>' +
'<field name="amount" type="measure"/>' +
'</pivot>',
viewOptions: {
context: {
pivot_measures: ['foo'],
pivot_column_groupby: ['customer'],
pivot_row_groupby: ['product_id'],
},
},
});
// Equivalent to unload the filter
var reloadParams = {
context: {},
};
pivot.reload(reloadParams);
assert.deepEqual(pivot.getContext(), {
pivot_column_groupby: ['customer'],
pivot_measures: ['foo'],
pivot_row_groupby: ['product_id'],
}, "context should be correct");
// Let's get rid of the rows groupBy
pivot.$('tbody .o_pivot_header_cell_opened').click();
assert.deepEqual(pivot.getContext(), {
pivot_column_groupby: ['customer'],
pivot_measures: ['foo'],
pivot_row_groupby: [],
}, "context should be correct");
// And now, get rid of the col groupby
pivot.$('thead .o_pivot_header_cell_opened').click();
assert.deepEqual(pivot.getContext(), {
pivot_column_groupby: [],
pivot_measures: ['foo'],
pivot_row_groupby: [],
}, "context should be correct");
pivot.$('tbody .o_pivot_header_cell_closed').click();
pivot.$('.o_pivot_field_menu li[data-field=product_id] a').click();
assert.deepEqual(pivot.getContext(), {
pivot_column_groupby: [],
pivot_measures: ['foo'],
pivot_row_groupby: ['product_id'],
}, "context should be correct");
pivot.$('thead .o_pivot_header_cell_closed').click();
pivot.$('.o_pivot_field_menu li[data-field=customer] a').click();
assert.deepEqual(pivot.getContext(), {
pivot_column_groupby: ['customer'],
pivot_measures: ['foo'],
pivot_row_groupby: ['product_id'],
}, "context should be correct");
pivot.destroy();
});
QUnit.test('correctly uses pivot_ keys from the context', function (assert) {
assert.expect(7);
+11 -1
View File
@@ -417,7 +417,17 @@ class Web_Editor(http.Controller):
# Create a view to extend the template which adds the original file to link the new modified version instead
IrUiView = request.env["ir.ui.view"]
view_to_xpath = IrUiView.get_related_views(bundle_xmlid, bundles=True).filtered(lambda v: v.arch.find(url) >= 0)
def views_linking_url(view):
"""
Returns whether the view arch has some html link tag linked to the url.
(note: searching for the URL string is not enough as it could appear in a comment or an xpath expression.)
"""
return bool(etree.XML(view.arch).xpath("link[@href='{}']".format(url)))
view_to_xpath = IrUiView.get_related_views(bundle_xmlid, bundles=True).filtered(views_linking_url)
new_view = {
'name': custom_url,
'key': 'web_editor.scss_%s' % str(uuid.uuid4())[:6],
+9
View File
@@ -0,0 +1,9 @@
Ukraine, 2019-01-09
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,
Vyshnevska Iryna i.vyshnevska@mobilunity.com https://github.com/i-vyshnevska/
+2 -1
View File
@@ -459,7 +459,8 @@ class Module(models.Model):
modules_to_remove = self.mapped('name')
self.env['ir.model.data']._module_data_uninstall(modules_to_remove)
self._remove_copied_views()
self.write({'state': 'uninstalled', 'latest_version': False})
# we deactivate prefetching to not try to read a column that has been deleted
self.with_context(prefetch_fields=False).write({'state': 'uninstalled', 'latest_version': False})
return True
@api.multi