[MERGE] forward port branch saas-11.3 up to 4aa153e65c
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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/
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user