summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-08-01 17:23:03 +0200
committerChristophe Simonis <chs@odoo.com>2018-08-01 17:23:03 +0200
commit8d9366197e174d33fdf6962364a24ea40a1131b1 (patch)
treea900c27006510633f14827b00b80df8f01eeef1a /addons
parent8be798375eadefb6b66e3065a1af3f5eef9ab78a (diff)
parent691dd65e0ecca5ef1ab4fa1ff363562a9349c1d4 (diff)
[MERGE] forward port branch saas-11.3 up to 691dd65e0ec
Diffstat (limited to 'addons')
-rw-r--r--addons/account/models/account_move.py5
-rw-r--r--addons/account/models/account_payment.py1
-rw-r--r--addons/account/report/account_aged_partner_balance.py2
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_model.js18
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_renderer.js7
-rw-r--r--addons/account/static/tests/reconciliation_tests.js160
-rw-r--r--addons/account/views/account_view.xml14
-rw-r--r--addons/im_livechat/static/src/js/im_livechat.js4
-rw-r--r--addons/purchase_stock/models/stock.py2
-rw-r--r--addons/stock/views/product_views.xml1
-rw-r--r--addons/web/static/tests/helpers/test_utils.js5
-rw-r--r--addons/website_sale/models/website.py4
-rw-r--r--addons/website_sale_wishlist/views/website_sale_wishlist_template.xml2
13 files changed, 209 insertions, 16 deletions
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index c66b1efa82d..0144258ecb8 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -646,8 +646,11 @@ class AccountMoveLine(models.Model):
#compute the default credit/debit of the next line in case of a manual entry
balance = 0
for line in self._context['line_ids']:
- if line[2]:
+ if line[2]: # in case of command 0: add a record with values
balance += line[2].get('debit', 0) - line[2].get('credit', 0)
+ elif line[0] == 2: # line has been deleted
+ line_obj = self.browse(line[1])
+ balance -= line_obj.debit - line_obj.credit
if balance < 0:
rec.update({'debit': -balance})
if balance > 0:
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
index 0cbb74dd7a4..0da4a3da3fe 100644
--- a/addons/account/models/account_payment.py
+++ b/addons/account/models/account_payment.py
@@ -259,6 +259,7 @@ class account_register_payments(models.TransientModel):
'currency_id': self.currency_id.id,
'partner_id': invoices[0].commercial_partner_id.id,
'partner_type': MAP_INVOICE_TYPE_PARTNER_TYPE[invoices[0].type],
+ 'multi': False,
}
@api.multi
diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index 95a01bfd0c2..8748a03ff34 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -77,7 +77,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
partner_ids = [partner['partner_id'] for partner in partners if partner['partner_id']]
lines = dict((partner['partner_id'] or False, []) for partner in partners)
if not partner_ids:
- return [], [], []
+ return [], [], {}
# This dictionary will store the not due amount of all partners
undue_amounts = {}
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
index 87bd379879d..f42943f6027 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js
@@ -130,6 +130,20 @@ var StatementModel = BasicModel.extend({
var line = this.getLine(handle);
var prop = _.clone(_.find(line.mv_lines, {'id': mv_line_id}));
this._addProposition(line, prop);
+
+ // Check whether we have some propositions left
+ // If we don't, it means we are on an empty page
+ // so let's go back to the previous page
+ // Through the offset
+ var propLineIds = _.map(line.reconciliation_proposition, function(prop) {
+ return prop.id;
+ });
+ var leftOversProps = _.filter(line.mv_lines, function(mv_line) {
+ return propLineIds.indexOf(mv_line.id) === -1;
+ });
+ if (line.offset && !leftOversProps.length) {
+ line.offset -= line.limitMoveLines;
+ }
return $.when(this._computeLine(line), this._performMoveLine(handle));
},
/**
@@ -665,7 +679,7 @@ var StatementModel = BasicModel.extend({
return !isNaN(prop.id) && prop.already_paid;
}), 'id'),
"new_aml_dicts": _.map(_.filter(props, function (prop) {
- return isNaN(prop.id);
+ return isNaN(prop.id) && prop.display;
}), self._formatToProcessReconciliation.bind(self, line)),
};
@@ -1262,7 +1276,7 @@ var ManualModel = StatementModel.extend({
});
} else {
var mv_line_ids = _.pluck(_.filter(props, function (prop) {return !isNaN(prop.id);}), 'id');
- var new_mv_line_dicts = _.map(_.filter(props, function (prop) {return isNaN(prop.id);}), self._formatToProcessReconciliation.bind(self, line));
+ var new_mv_line_dicts = _.map(_.filter(props, function (prop) {return isNaN(prop.id) && prop.display;}), self._formatToProcessReconciliation.bind(self, line));
process_reconciliations.push({
id: null,
type: null,
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
index 7578f9c4d1d..8a4ac8b25e7 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
@@ -423,7 +423,8 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
// mv_lines
var $mv_lines = this.$('.match table tbody').empty();
- _.each(state.mv_lines.slice(0, state.limitMoveLines), function (line) {
+ var stateMvLines = state.mv_lines || [];
+ _.each(stateMvLines.slice(0, state.limitMoveLines), function (line) {
var $line = $(qweb.render("reconciliation.line.mv_line", {'line': line, 'state': state}));
if (!isNaN(line.id)) {
$('<span class="line_info_button fa fa-info-circle"/>')
@@ -432,9 +433,9 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
}
$mv_lines.append($line);
});
- this.$('.match .fa-chevron-right').toggleClass('disabled', state.mv_lines.length <= state.limitMoveLines);
+ this.$('.match .fa-chevron-right').toggleClass('disabled', stateMvLines.length <= state.limitMoveLines);
this.$('.match .fa-chevron-left').toggleClass('disabled', !state.offset);
- this.$('.match').css('max-height', !state.mv_lines.length && !state.filter.length ? '0px' : '');
+ this.$('.match').css('max-height', !stateMvLines.length && !state.filter.length ? '0px' : '');
// balance
this.$('.popover').remove();
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
index 4fb3f997368..93a8a1818b4 100644
--- a/addons/account/static/tests/reconciliation_tests.js
+++ b/addons/account/static/tests/reconciliation_tests.js
@@ -1720,5 +1720,165 @@ QUnit.module('account', {
clientAction.destroy();
});
+
+ QUnit.test('Manual Reconciliation: remove a prop to attain balance and reconcile', function (assert) {
+ assert.expect(5);
+
+ // tweak the data to fit our needs
+ this.params.data_for_manual_reconciliation_widget['[283, null, "", 0, 6]'] = _.extend({}, this.params.data_for_manual_reconciliation_widget['[null,null]']);
+ this.params.data_for_manual_reconciliation_widget['[283, null, "", 0, 6]'].accounts[0].reconciliation_proposition = [
+ {account_id: 283, account_type: "other", amount_currency_str: "", currency_id: false, date_maturity: "2017-03-18", date: "2017-02-16",
+ total_amount_str: "$ 500.00", partner_id: 8, account_name: "101000 Current Assets", name: "INV/2017/0987", partner_name: "Agrolait",
+ total_amount_currency_str: "", id: 999, credit: 0.0, journal_id: [1, "Customer Invoices"], amount_str: "$ 500.00", debit: 500.0,
+ account_code: "101000", ref: "", already_paid: false}
+ ];
+
+ var clientAction = new ReconciliationClientAction.ManualAction(null, this.params.options);
+ testUtils.addMockEnvironment(clientAction, {
+ data: this.params.data,
+ mockRPC: function (route, args) {
+ if (args.method === 'process_move_lines') {
+ assert.deepEqual(args.args,
+ [
+ [{id: null, type: null,
+ mv_line_ids: [399, 402],
+ new_mv_line_dicts: []}
+ ]
+ ], "should call process_move_lines without the new mv line dict");
+ }
+
+ return this._super(route, args);
+ },
+ session: {
+ currencies: {
+ 3: {
+ digits: [69, 2],
+ position: "before",
+ symbol: "$"
+ }
+ }
+ },
+ });
+
+ clientAction.appendTo($('#qunit-fixture'));
+
+ // The first reconciliation "line" is where it happens
+ var widget = clientAction.widgets[0];
+
+ // Add first prop
+ widget.$('.match .cell_account_code:first').trigger('click');
+ assert.notOk( widget.$('.cell_right .line_info_button').length,
+ "should not display the partial reconciliation alert");
+
+ // Add second prop
+ widget.$('.match .cell_account_code:first').trigger('click');
+
+ // Check that a create form is here
+ var writeOffCreate = widget.$('div.create');
+
+ assert.equal(writeOffCreate.length, 1,
+ 'A write-off creation should be present');
+
+ assert.equal(writeOffCreate.find('input[name=amount]').val(), 500,
+ 'The right amount should be proposed for the write-off');
+
+ // remove the first line, the other two will balance one another
+ widget.$('tr[data-line-id="999"] td:first').click()
+
+ var $buttonReconcile = widget.$('button.o_reconcile:not(hidden)');
+ assert.equal($buttonReconcile.length, 1,
+ 'The reconcile button must be visible');
+
+ $buttonReconcile.click();
+
+ clientAction.destroy();
+ });
+
+ QUnit.test('Manual Reconciliation: No lines for account', function (assert) {
+ assert.expect(2);
+
+ var clientAction = new ReconciliationClientAction.ManualAction(null, this.params.options);
+ testUtils.addMockEnvironment(clientAction, {
+ data: this.params.data,
+ session: {
+ currencies: {
+ 3: {
+ digits: [69, 2],
+ position: "before",
+ symbol: "$"
+ }
+ }
+ },
+ });
+
+ clientAction.appendTo($('#qunit-fixture'));
+
+ // The second reconciliation "line" is where it happens
+ var widget = clientAction.widgets[1];
+
+ var emptyLine = widget.$('tr.mv_line');
+
+ assert.notOk('data-line-id' in emptyLine.getAttributes(),
+ 'Empty line should be empty');
+
+ emptyLine.find('td:first').click();
+
+ // Check that a create form is here
+ var writeOffCreate = widget.$('div.create .create_account_id');
+
+ assert.equal(writeOffCreate.length, 1,
+ 'A write-off creation should be present');
+
+ clientAction.destroy();
+ });
+
+ QUnit.test('Automatic Reconciliation: Don\'t loose pager when adding last line of a page', function (assert) {
+ assert.expect(2);
+
+ function standard_partner (list, partner_id) {
+ _.each(list, function (item) {
+ item.partner_id = partner_id;
+ });
+ };
+
+ this.params.options.params.limitMoveLines = 1;
+ standard_partner(this.params.mv_lines['[5,"",0,6]'], 8);
+
+ // Overlap of those requests is normal
+ // the pager is implemented as:
+ // if there is at least 1 more line than my limit, the pager is active
+ this.params.mv_lines['[5,"",0,2]'] = this.params.mv_lines['[5,"",0,6]'].slice(0,2);
+ this.params.mv_lines['[5,"",1,2]'] = this.params.mv_lines['[5,"",0,6]'].slice(1,2);
+
+ var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
+ testUtils.addMockEnvironment(clientAction, {
+ data: this.params.data,
+ session: {
+ currencies: {
+ 3: {
+ digits: [69, 2],
+ position: "before",
+ symbol: "$"
+ }
+ }
+ },
+ });
+ clientAction.appendTo($('#qunit-fixture'));
+
+ var widget = clientAction.widgets[0];
+
+ var $nextPage = widget.$('.match .match_controls .fa-chevron-right:not(disabled)');
+ assert.ok($nextPage.length,
+ 'We should have a next page');
+ $nextPage.click();
+
+ // Add the second's page record
+ widget.$('.match tr[data-line-id=112] td:first').click();
+
+ assert.ok(widget.$('.match tr[data-line-id=109]').length,
+ 'the record of the first page must be proposed');
+
+ clientAction.destroy();
+ });
});
});
diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml
index 996eff10cd2..141388fbfd2 100644
--- a/addons/account/views/account_view.xml
+++ b/addons/account/views/account_view.xml
@@ -320,7 +320,7 @@
<group>
<group>
<field name="company_partner_id" invisible="1"/>
- <field name="bank_account_id"/>
+ <field name="bank_account_id" context="{'default_partner_id': company_partner_id, 'form_view_ref': 'account.view_company_partner_bank_form'}"/>
<field name="bank_id"/>
</group>
<group >
@@ -364,6 +364,18 @@
</field>
</record>
+ <record id="view_company_partner_bank_form" model="ir.ui.view">
+ <field name="name">company.res.partner.bank.form</field>
+ <field name="model">res.partner.bank</field>
+ <field name="inherit_id" ref="base.view_partner_bank_form"/>
+ <field name="mode">primary</field>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='partner_id']" position="attributes">
+ <attribute name="invisible">1</attribute>
+ </xpath>
+ </field>
+ </record>
+
<record id="view_account_bank_journal_tree" model="ir.ui.view">
<field name="name">account.bank.journal.tree</field>
<field name="model">account.journal</field>
diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js
index acdf3460ecc..b2b944cc631 100644
--- a/addons/im_livechat/static/src/js/im_livechat.js
+++ b/addons/im_livechat/static/src/js/im_livechat.js
@@ -2,6 +2,7 @@ odoo.define('im_livechat.im_livechat', function (require) {
"use strict";
var bus = require('bus.bus').bus;
+var concurrency = require('web.concurrency');
var config = require('web.config');
var core = require('web.core');
var session = require('web.session');
@@ -313,6 +314,7 @@ var Feedback = Widget.extend({
this.channel_uuid = channel_uuid;
this.server_origin = session.origin;
this.rating = undefined;
+ this.dp = new concurrency.DropPrevious();
},
on_click_smiley: function (ev) {
@@ -348,7 +350,7 @@ var Feedback = Widget.extend({
rate: this.rating,
reason : options.reason
};
- return session.rpc('/im_livechat/feedback', args).then(function () {
+ this.dp.add(session.rpc('/im_livechat/feedback', args)).then(function () {
if (options.close) {
var emoji = RATING_TO_EMOJI[self.rating] || "??" ;
var content = _.str.sprintf(_t("Rating: %s"), emoji);
diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py
index a108f0bec2a..b3d336ceddc 100644
--- a/addons/purchase_stock/models/stock.py
+++ b/addons/purchase_stock/models/stock.py
@@ -215,6 +215,6 @@ class PushedFlow(models.Model):
_inherit = "stock.location.path"
def _prepare_move_copy_values(self, move_to_copy, new_date):
- res = super()._prepare_move_copy_values(move_to_copy, new_date)
+ res = super(PushedFlow, self)._prepare_move_copy_values(move_to_copy, new_date)
res['purchase_line_id'] = None
return res
diff --git a/addons/stock/views/product_views.xml b/addons/stock/views/product_views.xml
index f59ae7baf5c..5b58670e5ab 100644
--- a/addons/stock/views/product_views.xml
+++ b/addons/stock/views/product_views.xml
@@ -11,7 +11,6 @@
<group string="Logistics">
<field name="route_ids" widget="many2many_tags" groups="stock.group_adv_location"/>
<div colspan="2" attrs="{'invisible': [('parent_id', '=', False)]}">
- The following routes will apply to the products in this category taking into account parent categories:
<field name="total_route_ids" nolabel="1" widget="many2many_tags"/>
</div>
<field name="removal_strategy_id" options="{'no_create': True}"/>
diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js
index 6c05076ef98..4720d166a4c 100644
--- a/addons/web/static/tests/helpers/test_utils.js
+++ b/addons/web/static/tests/helpers/test_utils.js
@@ -547,10 +547,11 @@ function dragAndDrop($el, $to, options) {
var toOffset = $to.offset();
toOffset.top += $to.outerHeight()/2;
toOffset.left += $to.outerWidth()/2;
+ var vertical_offset = (toOffset.top < elementCenter.top) ? -1 : 1;
if (position === 'top') {
- toOffset.top -= $to.outerHeight()/2;
+ toOffset.top -= $to.outerHeight()/2 + vertical_offset;
} else if (position === 'bottom') {
- toOffset.top += $to.outerHeight()/2;
+ toOffset.top += $to.outerHeight()/2 - vertical_offset;
} else if (position === 'left') {
toOffset.left -= $to.outerWidth()/2;
} else if (position === 'right') {
diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py
index 13594353abf..64c6bc05ab9 100644
--- a/addons/website_sale/models/website.py
+++ b/addons/website_sale/models/website.py
@@ -60,8 +60,8 @@ class Website(models.Model):
if not pricelists: # no pricelist for this country, or no GeoIP
pricelists |= all_pl.filtered(lambda pl: not show_visible or pl.selectable or pl.id in (current_pl, order_pl))
- else:
- pricelists |= all_pl.filtered(lambda pl: not show_visible and pl.sudo().code)
+ if not show_visible and not country_code:
+ pricelists |= all_pl.filtered(lambda pl: pl.sudo().code)
# This method is cached, must not return records! See also #8795
return pricelists.ids
diff --git a/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml b/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml
index 6be0378297c..3ee63021073 100644
--- a/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml
+++ b/addons/website_sale_wishlist/views/website_sale_wishlist_template.xml
@@ -69,7 +69,7 @@
<del class='text-danger'><t t-esc='wish.price' t-options="{'widget': 'monetary', 'display_currency': wish.currency_id}"/></del>
<br/>
</t>
- <t t-esc='wish.price_new' t-options="{'widget': 'monetary', 'display_currency': wish.currency_id}"/>
+ <t t-esc="wish.product_id.website_price" t-options="{'widget': 'monetary', 'display_currency': website.pricelist_id.currency_id}"/>
</td>
<td class='text-center td-wish-btn'>
<input name="product_id" t-att-value="wish.product_id.id" type="hidden"/>