[MERGE] forward port branch 11.0 up to 4ea3838d89
This commit is contained in:
@@ -34,7 +34,7 @@ class DeliveryCarrier(models.Model):
|
||||
# Internals for shipping providers #
|
||||
# -------------------------------- #
|
||||
|
||||
name = fields.Char(required=True)
|
||||
name = fields.Char(required=True, translate=True)
|
||||
active = fields.Boolean(default=True)
|
||||
sequence = fields.Integer(help="Determine the display order", default=10)
|
||||
# This field will be overwritten by internal shipping providers by adding their own type (ex: 'fedex')
|
||||
|
||||
@@ -96,10 +96,9 @@ class SaleOrder(models.Model):
|
||||
taxes_ids = self.fiscal_position_id.map_tax(taxes, carrier.product_id, self.partner_id).ids
|
||||
|
||||
# Create the sales order line
|
||||
carrier_name = carrier.product_id.with_context(lang=self.partner_id.lang).name_get()[0][1]
|
||||
values = {
|
||||
'order_id': self.id,
|
||||
'name': carrier_name,
|
||||
'name': carrier.with_context(lang=self.partner_id.lang).name,
|
||||
'product_uom_qty': 1,
|
||||
'product_uom': carrier.product_id.uom_id.id,
|
||||
'product_id': carrier.product_id.id,
|
||||
|
||||
@@ -132,18 +132,6 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_salary_rule_category_tree_view" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.category.tree.view</field>
|
||||
<field name="model">hr.salary.rule.category</field>
|
||||
<field name="field_parent">children_ids</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Salary Rule Categories">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="parent_id" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_salary_rule_category_filter" model="ir.ui.view">
|
||||
<field name="name">hr.salary.rule.category.select</field>
|
||||
@@ -169,20 +157,6 @@
|
||||
sequence="11"
|
||||
groups="base.group_no_one"
|
||||
/>
|
||||
<record id="action_hr_salary_rule_category_tree_view" model="ir.actions.act_window">
|
||||
<field name="name">Salary Rule Categories Hierarchy</field>
|
||||
<field name="res_model">hr.salary.rule.category</field>
|
||||
<field name="view_type">tree</field>
|
||||
<field name="domain">[('parent_id','=',False)]</field>
|
||||
<field name="view_id" ref="hr_salary_rule_category_tree_view"/>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_hr_salary_rule_category_tree_view"
|
||||
action="action_hr_salary_rule_category_tree_view"
|
||||
parent="menu_hr_payroll_configuration"
|
||||
sequence="12"
|
||||
groups="base.group_no_one"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Contribution Register
|
||||
|
||||
@@ -11,6 +11,11 @@ from odoo.tools import consteq
|
||||
|
||||
class MassMailController(http.Controller):
|
||||
|
||||
@http.route(['/unsubscribe_from_list'], type='http', website=True, multilang=False, auth='public')
|
||||
def unsubscribe_placeholder_link(self, **post):
|
||||
"""Dummy route so placeholder is not prefixed by language, MUST have multilang=False"""
|
||||
raise werkzeug.exceptions.NotFound()
|
||||
|
||||
@http.route(['/mail/mailing/<int:mailing_id>/unsubscribe'], type='http', website=True, auth='public')
|
||||
def mailing(self, mailing_id, email=None, res_id=None, token="", **post):
|
||||
mailing = request.env['mail.mass_mailing'].sudo().browse(mailing_id)
|
||||
|
||||
@@ -29,12 +29,12 @@ class StockWarehouse(models.Model):
|
||||
return result
|
||||
|
||||
def _get_manufacture_route_id(self):
|
||||
manufacture_route_id = self.env.ref('mrp.route_warehouse0_manufacture').id
|
||||
if not manufacture_route_id:
|
||||
manufacture_route_id = self.env['stock.location.route'].search([('name', 'like', _('Manufacture'))], limit=1).id
|
||||
if not manufacture_route_id:
|
||||
manufacture_route = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False)
|
||||
if not manufacture_route:
|
||||
manufacture_route = self.env['stock.location.route'].search([('name', 'like', _('Manufacture'))], limit=1)
|
||||
if not manufacture_route:
|
||||
raise exceptions.UserError(_('Can\'t find any generic Manufacture route.'))
|
||||
return manufacture_route_id
|
||||
return manufacture_route.id
|
||||
|
||||
def _get_manufacture_pull_rules_values(self, route_values):
|
||||
if not self.manu_type_id:
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</page>
|
||||
<page string="Messages">
|
||||
<group>
|
||||
<field name="pre_msg"/>
|
||||
<field name="pre_msg" invisible="1"/>
|
||||
<field name="post_msg"/>
|
||||
<field name="pending_msg"/>
|
||||
<field name="done_msg"/>
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
<tbody>
|
||||
<tr style="width: 1in;">
|
||||
<td style="border: 2px solid black;text-align: center; vertical-align: middle;" class="col-xs-5">
|
||||
<img t-if="product.barcode" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('EAN13', product.barcode, 600, 150)" style="width:100%;height:20%;"/>
|
||||
<img t-if="product.barcode and len(product.barcode) == 13" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('EAN13', product.barcode, 600, 150)" style="width:100%;height:20%;"/>
|
||||
<img t-elif="product.barcode and len(product.barcode) == 8" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('EAN8', product.barcode, 600, 150)" style="width:100%;height:20%;"/>
|
||||
<img t-else="" t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s' % ('Code128', product.barcode, 600, 150)" style="width:100%;height:20%;"/>
|
||||
<span t-field="product.barcode"/>
|
||||
</td>
|
||||
<td style="border: 2px solid black; text-align: center;" class="col-xs-7">
|
||||
|
||||
@@ -875,6 +875,15 @@ var BasicModel = AbstractModel.extend({
|
||||
// x2Many case: the new record has been stored in _changes, as a
|
||||
// command so we remove the command(s) related to that record
|
||||
parent._changes = _.filter(parent._changes, function (change) {
|
||||
if (change.id === elementID &&
|
||||
change.operation === 'ADD' && // For now, only an ADD command increases limits
|
||||
parent.tempLimitIncrement) {
|
||||
// The record will be deleted from the _changes.
|
||||
// So we won't be passing into the logic of _applyX2ManyOperations anymore
|
||||
// implying that we have to cancel out the effects of an ADD command here
|
||||
parent.tempLimitIncrement--;
|
||||
parent.limit--;
|
||||
}
|
||||
return change.id !== elementID;
|
||||
});
|
||||
} else {
|
||||
@@ -3121,6 +3130,15 @@ var BasicModel = AbstractModel.extend({
|
||||
// when sent to the server, the x2manys values must be a list
|
||||
// of commands in a context, but the list of ids in a domain
|
||||
ids.toJSON = _generateX2ManyCommands.bind(null, fieldName);
|
||||
} else if (field.type === 'one2many') { // Ids are evaluated as a list of ids
|
||||
/* Filtering out virtual ids from the ids list
|
||||
* The server will crash if there are virtual ids in there
|
||||
* The webClient doesn't do literal id list comparison like ids == list
|
||||
* Only relevant in o2m: m2m does create actual records in db
|
||||
*/
|
||||
ids = _.filter(ids, function (id) {
|
||||
return typeof id !== 'string';
|
||||
});
|
||||
}
|
||||
context[fieldName] = ids;
|
||||
}
|
||||
|
||||
@@ -2551,6 +2551,117 @@ QUnit.module('relational_fields', {
|
||||
|
||||
QUnit.module('FieldOne2Many');
|
||||
|
||||
QUnit.test('O2M List with pager, decoration and default_order: add and cancel adding', function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
// The decoration on the list implies that its condition will be evaluated
|
||||
// against the data of the field (actual records *displayed*)
|
||||
// If one data is wrongly formed, it will crash
|
||||
// This test adds then cancels a record in a paged, ordered, and decorated list
|
||||
// That implies prefetching of records for sorting
|
||||
// and evaluation of the decoration against *visible records*
|
||||
|
||||
this.data.partner.records[0].p = [2,4];
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<field name="p">' +
|
||||
'<tree editable="bottom" limit="1" decoration-muted="foo != False" default_order="display_name">' +
|
||||
'<field name="foo" invisible="1"/>' +
|
||||
'<field name="display_name" />' +
|
||||
'</tree>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
});
|
||||
|
||||
form.$('.o_field_x2many_list .o_field_x2many_list_row_add a').click();
|
||||
|
||||
var $dataRows = form.$('.o_field_x2many_list .o_data_row');
|
||||
assert.equal($dataRows.length, 2,
|
||||
'There should be 2 rows');
|
||||
|
||||
var $expectedSelectedRow = $dataRows.eq(1);
|
||||
var $actualSelectedRow = form.$('.o_selected_row');
|
||||
assert.equal($actualSelectedRow[0], $expectedSelectedRow[0],
|
||||
'The selected row should be the new one');
|
||||
|
||||
// Cancel Creation
|
||||
var escapeKey = $.ui.keyCode.ESCAPE;
|
||||
$actualSelectedRow.find('input').trigger(
|
||||
$.Event('keydown', {which: escapeKey, keyCode: escapeKey}));
|
||||
|
||||
$dataRows = form.$('.o_field_x2many_list .o_data_row');
|
||||
assert.equal($dataRows.length, 1,
|
||||
'There should be 1 row');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('O2M with parented m2o and domain on parent.m2o', function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
/* records in an o2m can have a m2o pointing to themselves
|
||||
* in that case, a domain evaluation on that field followed by name_search
|
||||
* shouldn't send virtual_ids to the server
|
||||
*/
|
||||
|
||||
this.data.turtle.fields.parent_id = {string: "Parent", type: "many2one", relation: 'turtle'};
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:'<form string="Partners">' +
|
||||
'<field name="turtles">' +
|
||||
'<tree>' +
|
||||
'<field name="parent_id" />' +
|
||||
'</tree>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
archs: {
|
||||
'turtle,false,form': '<form><field name="parent_id" domain="[(\'id\', \'in\', parent.turtles)]"/></form>',
|
||||
},
|
||||
|
||||
mockRPC: function(route, args) {
|
||||
if (route === '/web/dataset/call_kw/turtle/name_search') {
|
||||
// We are going to pass twice here
|
||||
// First time, we really have nothing
|
||||
// Second time, a virtual_id has been created
|
||||
assert.deepEqual(args.kwargs.args, [['id', 'in', []]]);
|
||||
}
|
||||
return this._super(route, args);
|
||||
}
|
||||
});
|
||||
|
||||
form.$('.o_field_x2many_list[name=turtles] .o_field_x2many_list_row_add a').click();
|
||||
var $modal = $('.modal-content');
|
||||
|
||||
var $turtleParent = $modal.find('.o_field_many2one input');
|
||||
var $dropdown = $turtleParent.autocomplete('widget');
|
||||
|
||||
$turtleParent.click();
|
||||
|
||||
$dropdown.find('li.o_m2o_dropdown_option:contains(Create)').first().mouseenter().click();
|
||||
|
||||
$modal = $('.modal-content');
|
||||
|
||||
$modal.eq(1).find('.modal-footer .btn-primary').eq(0).click(); // Confirm new Record
|
||||
$modal.eq(0).find('.modal-footer .btn-primary').eq(1).click(); // Save & New
|
||||
|
||||
assert.equal(form.$('.o_data_row').length, 1,
|
||||
'The main record should have the new record in its o2m');
|
||||
|
||||
$modal = $('.modal-content');
|
||||
$modal.find('.o_field_many2one input').click();
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('one2many list editable with cell readonly modifier', function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
|
||||
@@ -290,9 +290,12 @@ var RTEWidget = Widget.extend({
|
||||
});
|
||||
|
||||
// start element observation
|
||||
$(document).on('content_changed', '.o_editable', function (event) {
|
||||
self.trigger_up('rte_change', {target: event.target});
|
||||
$(this).addClass('o_dirty');
|
||||
$(document).on('content_changed', '.o_editable', function (ev) {
|
||||
self.trigger_up('rte_change', {target: ev.target});
|
||||
if (!ev.__isDirtyHandled) {
|
||||
$(this).addClass('o_dirty');
|
||||
ev.__isDirtyHandled = true;
|
||||
}
|
||||
});
|
||||
|
||||
$('#wrapwrap, .o_editable').on('click.rte', '*', this, this._onClick.bind(this));
|
||||
@@ -408,6 +411,10 @@ var RTEWidget = Widget.extend({
|
||||
save: function (context) {
|
||||
var self = this;
|
||||
|
||||
$('.o_editable')
|
||||
.destroy()
|
||||
.removeClass('o_editable o_is_inline_editable');
|
||||
|
||||
var $dirty = $('.o_dirty');
|
||||
$dirty
|
||||
.removeAttr('contentEditable')
|
||||
|
||||
@@ -261,6 +261,14 @@
|
||||
<rng:optional><rng:attribute name="avatar_field" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="write_model" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="write_field" /></rng:optional>
|
||||
<rng:optional>
|
||||
<rng:attribute name="force_save">
|
||||
<rng:choice>
|
||||
<rng:value>1</rng:value>
|
||||
<rng:value>0</rng:value>
|
||||
</rng:choice>
|
||||
</rng:attribute>
|
||||
</rng:optional>
|
||||
<!-- Widget *static* options defined as an arbitrary JSON dict, with
|
||||
widget-dependent parameters. To be ignored if widget/client does
|
||||
not support them. -->
|
||||
|
||||
Reference in New Issue
Block a user