[MERGE] forward port branch 11.0 up to 169f899e29

This commit is contained in:
Christophe Simonis
2018-02-12 13:15:06 +01:00
14 changed files with 129 additions and 45 deletions
+2 -1
View File
@@ -510,6 +510,7 @@ class account_payment(models.Model):
(transfer_credit_aml + transfer_debit_aml).reconcile()
rec.write({'state': 'posted', 'move_name': move.name})
return True
@api.multi
def action_draft(self):
@@ -524,7 +525,7 @@ class account_payment(models.Model):
if any(len(record.invoice_ids) != 1 for record in self):
# For multiple invoices, there is account.register.payments wizard
raise UserError(_("This method should only be called to process a single invoice's payment."))
self.post();
return self.post()
def _create_payment_entry(self, amount):
""" Create a journal entry corresponding to a payment, if the payment references invoice(s) they are reconciled.
@@ -61,7 +61,7 @@
<div class="o_setting_right_pane">
<label for="module_crm_phone_validation"/>
<div class="text-muted" id="module_crm_phone_validation">
Validate and normalize phone numbers.
Formats phone numbers
</div>
</div>
</div>
@@ -13,7 +13,6 @@ CRM Phone Numbers Validation
This module allows for validate and format phone numbers for leads and contacts.""",
'data': [
'views/res_config_settings_views.xml',
'views/crm_lead_views.xml'
],
'depends': [
'phone_validation',
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="crm_lead_opportunity_view_form" model="ir.ui.view">
<field name="name">crm.lead.form.opportunity.inherit.crm.phone.validation</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_form_view_oppor" />
<field name="arch" type="xml">
<xpath expr="//group[@name='Misc']" position="inside">
<field name="company_id" invisible="1"/>
</xpath>
</field>
</record>
</odoo>
@@ -135,7 +135,7 @@
<th>Net Imposable</th>
<th>Plafond s.s.</th>
<th></th>
<th>Coût Global Salarié</th>
<th>Total versé par l'employeur</th>
</tr>
</thead>
<tbody>
@@ -9,11 +9,12 @@ class PhoneValidationMixin(models.AbstractModel):
_name = 'phone.validation.mixin'
def _phone_get_country(self):
if 'country_id' in self:
if 'country_id' in self and self.country_id:
return self.country_id
return self.env.user.company_id.country_id
def _phone_get_always_international(self):
if 'company_id' in self and self.country_id:
if 'company_id' in self and self.company_id:
return self.company_id.phone_international_format == 'prefix'
return self.env.user.company_id.phone_international_format == 'prefix'
+10 -1
View File
@@ -227,6 +227,12 @@ class PosOrder(models.Model):
return InvoiceLine.sudo().create(inv_line)
def _create_account_move_line(self, session=None, move=None):
def _flatten_tax_and_children(taxes):
children = self.env['account.tax']
for tax in taxes:
children |= _flatten_tax_and_children(tax.children_tax_ids)
return taxes + children
# Tricky, via the workflow, we only have one id in the ids variable
"""Create a account move line of order grouped by products or not."""
IrProperty = self.env['ir.property']
@@ -303,6 +309,9 @@ class PosOrder(models.Model):
name = name + ' (' + line.notice + ')'
# Create a move for the line for the order line
# Just like for invoices, a group of taxes must be present on this base line
# As well as its children
base_line_tax_ids = _flatten_tax_and_children(line.tax_ids_after_fiscal_position).filtered(lambda tax: tax.type_tax_use in ['sale', 'none'])
insert_data('product', {
'name': name,
'quantity': line.qty,
@@ -311,7 +320,7 @@ class PosOrder(models.Model):
'analytic_account_id': self._prepare_analytic_account(line),
'credit': ((amount > 0) and amount) or 0.0,
'debit': ((amount < 0) and -amount) or 0.0,
'tax_ids': [(6, 0, line.tax_ids_after_fiscal_position.ids)],
'tax_ids': [(6, 0, base_line_tax_ids.ids)],
'partner_id': partner_id
})
+7 -11
View File
@@ -1646,19 +1646,15 @@ class TestSinglePicking(TestStockCommon):
class TestStockUOM(TestStockCommon):
def setUp(self):
with registry().cursor() as cr:
env = api.Environment(cr, 1, {})
dp = env.ref('product.decimal_product_uom')
self.old_digits = dp.digits
dp.digits = 7
super(TestStockUOM, self).setUp()
dp = self.env.ref('product.decimal_product_uom')
dp.digits = 7
def tearDown(self):
super(TestStockUOM, self).tearDown()
with self.registry.cursor() as cr:
env = api.Environment(cr, 1, {})
dp = env.ref('product.decimal_product_uom')
dp.digits = self.old_digits
# Trick: invoke the method 'precision_get' with the current environment.
# This fills in the cache of the method with the right value. If we
# don't do that, the registry will access the corresponding precision
# with a new cursor (LazyCursor), and get a different value!
self.assertEqual(dp.precision_get(dp.name), 7)
def test_pickings_transfer_with_different_uom_and_back_orders(self):
""" Picking transfer with diffrent unit of meassure. """
@@ -30,6 +30,7 @@ var BasicRenderer = AbstractRenderer.extend({
this.activeActions = params.activeActions;
this.viewType = params.viewType;
this.mode = params.mode || 'readonly';
this.widgets = [];
},
/**
* This method has two responsabilities: find every invalid fields in the
@@ -95,6 +96,9 @@ var BasicRenderer = AbstractRenderer.extend({
return this._render().then(_.constant([]));
}
// reset all widgets (from the <widget> tag) if any:
_.invoke(this.widgets, 'updateState', state);
var defs = [];
// Reset all the field widgets that are marked as changed and the ones
@@ -613,6 +617,8 @@ var BasicRenderer = AbstractRenderer.extend({
var Widget = widgetRegistry.get(node.attrs.name);
var widget = new Widget(this, record);
this.widgets.push(widget);
// Prepare widget rendering and save the related deferred
var def = widget._widgetRenderAndInsert(function () {});
if (def.state() === 'pending') {
@@ -71,6 +71,7 @@ var ListRenderer = BasicRenderer.extend({
*/
updateState: function (state, params) {
this._processColumns(params.columnInvisibleFields || {});
this.selection = [];
return this._super.apply(this, arguments);
},
@@ -202,9 +203,11 @@ var ListRenderer = BasicRenderer.extend({
var field = self.state.fields[column.attrs.name];
var value = aggregateValues[column.attrs.name].value;
var help = aggregateValues[column.attrs.name].help;
var formattedValue = field_utils.format[field.type](value, field, {
escape: true,
});
var formatFunc = field_utils.format[column.attrs.widget];
if (!formatFunc) {
formatFunc = field_utils.format[field.type];
}
var formattedValue = formatFunc(value, field, {escape: true});
$cell.addClass('o_list_number').attr('title', help).html(formattedValue);
}
return $cell;
@@ -6232,6 +6232,41 @@ QUnit.module('Views', {
delete widgetRegistry.map.test;
});
QUnit.test('basic support for widgets', function (assert) {
assert.expect(1);
var MyWidget = Widget.extend({
init: function (parent, dataPoint) {
this.data = dataPoint.data;
},
start: function () {
this.$el.text(this.data.foo + "!");
},
updateState: function (dataPoint) {
this.$el.text(dataPoint.data.foo + "!");
},
});
widgetRegistry.add('test', MyWidget);
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form string="Partners">' +
'<field name="foo"/>' +
'<widget name="test"/>' +
'</form>',
});
form.$('input[name="foo"]').val("I am alive").trigger('input');
assert.strictEqual(form.$('.o_widget').text(), 'I am alive!',
"widget should have been updated");
form.destroy();
delete widgetRegistry.map.test;
});
QUnit.test('bounce edit button in readonly mode', function (assert) {
assert.expect(3);
@@ -582,6 +582,40 @@ QUnit.module('Views', {
list.destroy();
});
QUnit.test('selection is reset on reload', function (assert) {
assert.expect(5);
var list = createView({
View: ListView,
model: 'foo',
data: this.data,
arch: '<tree>' +
'<field name="foo"/>' +
'<field name="int_field" sum="Sum"/>' +
'</tree>',
});
assert.strictEqual(list.$('tfoot td:nth(2)').text(), '32',
"total should be 32 (no record selected)");
// select first record
var $firstRowSelector = list.$('tbody .o_list_record_selector input').first();
$firstRowSelector.click();
assert.ok($firstRowSelector.is(':checked'), "first row should be selected");
assert.strictEqual(list.$('tfoot td:nth(2)').text(), '10',
"total should be 10 (first record selected)");
// reload
list.reload();
$firstRowSelector = list.$('tbody .o_list_record_selector input').first();
assert.notOk($firstRowSelector.is(':checked'),
"first row should no longer be selected");
assert.strictEqual(list.$('tfoot td:nth(2)').text(), '32',
"total should be 32 (no more record selected)");
list.destroy();
});
QUnit.test('aggregates are computed correctly', function (assert) {
assert.expect(4);
@@ -660,6 +694,25 @@ QUnit.module('Views', {
list.destroy();
});
QUnit.test('aggregates are formatted according to field widget', function (assert) {
assert.expect(1);
var list = createView({
View: ListView,
model: 'foo',
data: this.data,
arch: '<tree>' +
'<field name="foo"/>' +
'<field name="qux" widget="float_time" sum="Sum"/>' +
'</tree>',
});
assert.strictEqual(list.$('tfoot td:nth(2)').text(), '19:24',
"total should be formatted as a float_time");
list.destroy();
});
QUnit.test('groups can be sorted on aggregates', function (assert) {
assert.expect(10);
@@ -742,7 +742,7 @@ var ViewEditor = Widget.extend({
var text = data.text || '';
if (!isSelected) {
text = Array($elem.data('level') + 1).join('-') + ' ' + text;
text = Array(($elem.data('level') || 0) + 1).join('-') + ' ' + text;
}
var $div = $('<div/>', {
text: text,
@@ -4,8 +4,6 @@
from odoo.addons.website_form.controllers.main import WebsiteForm
from odoo.http import request, route
import json
class WebsiteForm(WebsiteForm):
@@ -32,17 +30,13 @@ class WebsiteForm(WebsiteForm):
else:
record = data.get('record', {})
phone_fields = self._get_phone_fields_to_validate()
country = self._get_country()
invalid_fields = {}
country = request.env['res.country'].browse(record.get('country_id'))
contact_country = country.exists() and country or self._get_country()
for phone_field in phone_fields:
if not record.get(phone_field):
continue
number = record[phone_field]
try:
request.env[model_name].phone_format(number, country)
except Exception as error:
invalid_fields[phone_field] = error.args[0]
if invalid_fields:
return json.dumps({'error_fields': invalid_fields})
fmt_number = request.env[model_name].phone_format(number, contact_country)
request.params.update({phone_field: fmt_number})
return super(WebsiteForm, self).website_form(model_name, **kwargs)