summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-09-12 19:37:17 +0200
committerChristophe Simonis <chs@odoo.com>2018-09-12 19:37:17 +0200
commitb1999a0e876f1666d899f95fa6bdc23c6e8ec60f (patch)
treed8ef317e02379e604751fec12f4862f01c617a52
parent87b3ca13bf6edbb94ad69ec231e45a5ff296fd0e (diff)
parentdb0e84f3e364919baf7bb32ba0d44f10a0f76187 (diff)
[MERGE] forward port branch 11.0 up to db0e84f3e36
-rw-r--r--addons/account/models/account_invoice.py35
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_model.js12
-rw-r--r--addons/crm/models/crm_lead.py2
-rw-r--r--addons/hr_expense/models/hr_expense.py4
-rw-r--r--addons/mail/models/mail_thread.py4
-rw-r--r--addons/sale_timesheet/models/account_invoice.py16
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_model.js6
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js3
-rw-r--r--addons/web/static/tests/views/kanban_tests.js37
-rw-r--r--addons/web/static/tests/views/list_tests.js70
10 files changed, 48 insertions, 141 deletions
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index b0afefb527a..4baac419a5f 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1357,18 +1357,7 @@ class AccountInvoice(models.Model):
new_invoices += refund_invoice
return new_invoices
- @api.multi
- def pay_and_reconcile(self, pay_journal, pay_amount=None, date=None, writeoff_acc=None):
- """ Create and post an account.payment for the invoice self, which creates a journal entry that reconciles the invoice.
-
- :param pay_journal: journal in which the payment entry will be created
- :param pay_amount: amount of the payment to register, defaults to the residual of the invoice
- :param date: payment date, defaults to fields.Date.context_today(self)
- :param writeoff_acc: account in which to create a writeoff if pay_amount < self.residual, so that the invoice is fully paid
- """
- if isinstance(pay_journal, pycompat.integer_types):
- pay_journal = self.env['account.journal'].browse([pay_journal])
- assert len(self) == 1, "Can only pay one invoice at a time."
+ def _prepare_payment_vals(self, pay_journal, pay_amount=None, date=None, writeoff_acc=None, communication=None):
payment_type = self.type in ('out_invoice', 'in_refund') and 'inbound' or 'outbound'
if payment_type == 'inbound':
payment_method = self.env.ref('account.account_payment_method_manual_in')
@@ -1377,9 +1366,10 @@ class AccountInvoice(models.Model):
payment_method = self.env.ref('account.account_payment_method_manual_out')
journal_payment_methods = pay_journal.outbound_payment_method_ids
- communication = self.type in ('in_invoice', 'in_refund') and self.reference or self.number
- if self.origin:
- communication = '%s (%s)' % (communication, self.origin)
+ if not communication:
+ communication = self.type in ('in_invoice', 'in_refund') and self.reference or self.number
+ if self.origin:
+ communication = '%s (%s)' % (communication, self.origin)
payment_vals = {
'invoice_ids': [(6, 0, self.ids)],
@@ -1394,7 +1384,22 @@ class AccountInvoice(models.Model):
'payment_difference_handling': writeoff_acc and 'reconcile' or 'open',
'writeoff_account_id': writeoff_acc and writeoff_acc.id or False,
}
+ return payment_vals
+
+ @api.multi
+ def pay_and_reconcile(self, pay_journal, pay_amount=None, date=None, writeoff_acc=None):
+ """ Create and post an account.payment for the invoice self, which creates a journal entry that reconciles the invoice.
+
+ :param pay_journal: journal in which the payment entry will be created
+ :param pay_amount: amount of the payment to register, defaults to the residual of the invoice
+ :param date: payment date, defaults to fields.Date.context_today(self)
+ :param writeoff_acc: account in which to create a writeoff if pay_amount < self.residual, so that the invoice is fully paid
+ """
+ if isinstance(pay_journal, pycompat.integer_types):
+ pay_journal = self.env['account.journal'].browse([pay_journal])
+ assert len(self) == 1, "Can only pay one invoice at a time."
+ payment_vals = self._prepare_payment_vals(pay_journal, pay_amount=pay_amount, date=date, writeoff_acc=writeoff_acc)
payment = self.env['account.payment'].create(payment_vals)
payment.post()
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
index 5d0dc13105a..62da2abe007 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js
@@ -1199,7 +1199,15 @@ var ManualModel = StatementModel.extend({
self.accounts = _.object(self.account_ids, _.pluck(accounts, 'code'));
});
- return def_account.then(function () {
+ var def_reconcileModel = this._rpc({
+ model: 'account.reconcile.model',
+ method: 'search_read',
+ })
+ .then(function (reconcileModels) {
+ self.reconcileModels = reconcileModels;
+ });
+
+ return $.when(def_reconcileModel, def_account).then(function () {
switch(context.mode) {
case 'customers':
case 'suppliers':
@@ -1400,7 +1408,7 @@ var ManualModel = StatementModel.extend({
offset: 0,
limitMoveLines: this.limitMoveLines,
filter: "",
- reconcileModels: [],
+ reconcileModels: this.reconcileModels,
account_id: this._formatNameGet([data.account_id, data.account_name]),
st_line: data,
visible: true
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index 8afdeb7613e..2f97a786f9e 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -932,7 +932,7 @@ class Lead(models.Model):
email = '%s@%s' % (alias_record.alias_name, alias_record.alias_domain)
email_link = "<a href='mailto:%s'>%s</a>" % (email, email)
sub_title = _('or send an email to %s') % (email_link)
- return '<p class="o_view_nocontent_smiling_face">%s</p><p>%s</p>' % (help_title, sub_title)
+ return '<p class="o_view_nocontent_smiling_face">%s</p><p class="oe_view_nocontent_alias">%s</p>' % (help_title, sub_title)
@api.multi
def log_meeting(self, meeting_subject, meeting_date, duration):
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index 41b6d534d57..60fe5487083 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -130,14 +130,14 @@ class HrExpense(models.Model):
@api.model
def get_empty_list_help(self, help_message):
- if help_message:
+ if help_message and "oe_view_nocontent_smiling_face" not in help_message:
use_mailgateway = self.env['ir.config_parameter'].sudo().get_param('hr_expense.use_mailgateway')
alias_record = use_mailgateway and self.env.ref('hr_expense.mail_alias_expense') or False
if alias_record and alias_record.alias_domain and alias_record.alias_name:
link = "<a id='o_mail_test' href='mailto:%(email)s?subject=Lunch%%20with%%20customer%%3A%%20%%2412.32'>%(email)s</a>" % {
'email': '%s@%s' % (alias_record.alias_name, alias_record.alias_domain)
}
- return '<p class="oe_view_nocontent_smiling_face">%s</p><p>%s</p>' % (
+ return '<p class="oe_view_nocontent_smiling_face">%s</p><p class="oe_view_nocontent_alias">%s</p>' % (
_('Add a new expense,'),
_('or send receipts by email to %s.') % (link),)
return super(HrExpense, self).get_empty_list_help(help_message)
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 49ef44e35ae..350d4c77fb8 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -339,7 +339,9 @@ class MailThread(models.AbstractModel):
'email_link': email_link
}
}
- return "%(static_help)s<p>%(dyn_help)s</p>" % {
+ # do not add alias two times if it was added previously
+ if "oe_view_nocontent_alias" not in help:
+ return "%(static_help)s<p class="oe_view_nocontent_alias">%(dyn_help)s</p>" % {
'static_help': help,
'dyn_help': _("Create a new %(document)s by sending an email to %(email_link)s") % {
'document': document_name,
diff --git a/addons/sale_timesheet/models/account_invoice.py b/addons/sale_timesheet/models/account_invoice.py
index d2104cf8163..37aa03093a2 100644
--- a/addons/sale_timesheet/models/account_invoice.py
+++ b/addons/sale_timesheet/models/account_invoice.py
@@ -51,15 +51,19 @@ class AccountInvoice(models.Model):
self._compute_timesheet_revenue()
return result
+ def _get_compute_timesheet_revenue_domain(self, so_line_ids):
+ return [
+ ('so_line', 'in', so_line_ids),
+ ('project_id', '!=', False),
+ ('timesheet_invoice_id', '=', False),
+ ('timesheet_invoice_type', 'in', ['billable_time', 'billable_fixed'])
+ ]
+
def _compute_timesheet_revenue(self):
for invoice in self:
for invoice_line in invoice.invoice_line_ids.filtered(lambda line: line.product_id.type == 'service').sorted(key=lambda inv_line: (inv_line.invoice_id, inv_line.id)):
- uninvoiced_timesheet_lines = self.env['account.analytic.line'].sudo().search([
- ('so_line', 'in', invoice_line.sale_line_ids.ids),
- ('project_id', '!=', False),
- ('timesheet_invoice_id', '=', False),
- ('timesheet_invoice_type', 'in', ['billable_time', 'billable_fixed'])
- ])
+ domain = self._get_compute_timesheet_revenue_domain(invoice_line.sale_line_ids.ids)
+ uninvoiced_timesheet_lines = self.env['account.analytic.line'].sudo().search(domain)
# NOTE JEM : changing quantity (or unit price) of invoice line does not impact the revenue calculation. (FP specs)
if uninvoiced_timesheet_lines:
diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js
index 5d606324023..0328ff1e0e6 100644
--- a/addons/web/static/src/js/views/kanban/kanban_model.js
+++ b/addons/web/static/src/js/views/kanban/kanban_model.js
@@ -61,15 +61,11 @@ var KanbanModel = BasicModel.extend({
if (!groupByField || groupByField.type !== 'many2one') {
return $.Deferred().reject(); // only supported when grouped on m2o
}
- // ignore action default which may be for another model
- var context = _.pick(parent.context, function (value, key) {
- return key.slice(0,8) !== 'default_';
- });
return this._rpc({
model: groupByField.relation,
method: 'name_create',
args: [name],
- context: context, // todo: combine with view context
+ context: parent.context, // todo: combine with view context
})
.then(function (result) {
var newGroup = self._makeDataPoint({
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index 2388b3a3707..a72cd7008eb 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -193,6 +193,7 @@ var ListRenderer = BasicRenderer.extend({
reject = columnInvisibleFields[c.attrs.name];
}
if (!reject && c.attrs.widget === 'handle') {
+ self.hasHandle = true;
self.handleField = c.attrs.name;
}
return reject;
@@ -658,8 +659,6 @@ var ListRenderer = BasicRenderer.extend({
this._computeAggregates();
$table.toggleClass('o_list_view_grouped', is_grouped);
$table.toggleClass('o_list_view_ungrouped', !is_grouped);
- this.hasHandle = this.state.orderedBy.length === 0 ||
- this.state.orderedBy[0].name === this.handleField;
if (is_grouped) {
$table
.append(this._renderHeader(true))
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index 49d65629c00..101f484bdd1 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -1110,43 +1110,6 @@ QUnit.module('Views', {
kanban.destroy();
});
- QUnit.test('m2o group record created does not use action defaults', function (assert) {
- assert.expect(1);
-
- var kanban = createView({
- View: KanbanView,
- model: 'partner',
- data: this.data,
- arch: '<kanban class="o_kanban_test" on_create="quick_create">' +
- '<templates><t t-name="kanban-box">' +
- '<div>' +
- '<field name="foo"/>' +
- '</div>' +
- '</t></templates>' +
- '</kanban>',
- groupBy: ['product_id'],
- mockRPC: function (route, args) {
- if (args.method === 'name_create') {
- assert.notOk(_.has(args.kwargs.context, 'default_name'),
- "default_* should be removed from context");
- }
- return this._super.apply(this, arguments);
- },
- viewOptions: {
- context: {
- default_name: 'default name partner',
- },
- },
- });
- kanban.renderButtons();
-
- kanban.$('.o_column_quick_create input').val('new product');
- kanban.$('.o_column_quick_create button.o_kanban_add').click();
-
- kanban.destroy();
- });
-
-
QUnit.test('quick create record fail in grouped', function (assert) {
assert.expect(7);
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 980b94e644c..79efc03f52e 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -3178,76 +3178,6 @@ QUnit.module('Views', {
list.destroy();
});
- QUnit.test('editable list, handle widget locks and unlocks on sort', function (assert) {
- assert.expect(6);
-
- // we need another sortable field to lock/unlock the handle
- this.data.foo.fields.amount.sortable = true;
- // resequence makes sense on a sequence field, not on arbitrary fields
- this.data.foo.records[0].int_field = 0;
- this.data.foo.records[1].int_field = 1;
- this.data.foo.records[2].int_field = 2;
- this.data.foo.records[3].int_field = 3;
-
- var list = createView({
- View: ListView,
- model: 'foo',
- data: this.data,
- arch: '<tree editable="top" default_order="int_field">' +
- '<field name="int_field" widget="handle"/>' +
- '<field name="amount" widget="float"/>' +
- '</tree>',
- });
-
- assert.strictEqual(list.$('tbody').text(), '1200.00500.00300.000.00',
- "default should be sorted by int_field");
-
- // Drag and drop the fourth line in second position
- testUtils.dragAndDrop(
- list.$('.ui-sortable-handle').eq(3),
- list.$('tbody tr').first(),
- {position: 'bottom'}
- );
-
- // Handle should be unlocked at this point
- assert.strictEqual(list.$('tbody').text(), '1200.000.00500.00300.00',
- "drag and drop should have succeeded, as the handle is unlocked");
-
- // Sorting by a field different for int_field should lock the handle
- list.$('.o_column_sortable').eq(1).click();
-
- assert.strictEqual(list.$('tbody').text(), '0.00300.00500.001200.00',
- "should have been sorted by amount");
-
- // Drag and drop the fourth line in second position (not)
- testUtils.dragAndDrop(
- list.$('.ui-sortable-handle').eq(3),
- list.$('tbody tr').first(),
- {position: 'bottom'}
- );
-
- assert.strictEqual(list.$('tbody').text(), '0.00300.00500.001200.00',
- "drag and drop should have failed as the handle is locked");
-
- // Sorting by int_field should unlock the handle
- list.$('.o_column_sortable').eq(0).click();
-
- assert.strictEqual(list.$('tbody').text(), '1200.000.00500.00300.00',
- "records should be ordered as per the previous resequence");
-
- // Drag and drop the fourth line in second position
- testUtils.dragAndDrop(
- list.$('.ui-sortable-handle').eq(3),
- list.$('tbody tr').first(),
- {position: 'bottom'}
- );
-
- assert.strictEqual(list.$('tbody').text(), '1200.00300.000.00500.00',
- "drag and drop should have worked as the handle is unlocked");
-
- list.destroy();
- });
-
QUnit.test('editable list with handle widget with slow network', function (assert) {
assert.expect(15);