diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js index b0bb71abdbe..4b097c79d5b 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_action.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js @@ -32,6 +32,7 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, { change_name: '_onChangeName', close_statement: '_onCloseStatement', load_more: '_onLoadMore', + reload: 'reload', }, config: { // used to instantiate the model @@ -100,6 +101,21 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, { }); }, + reload: function() { + // On reload destroy all rendered line widget, reload data and then rerender widget + var self = this; + _.each(this.widgets, function(widget) { + widget.destroy(); + }) + this.widgets = []; + this.model.reload() + .then(function() { + self.$('.o_reconciliation_lines').html(''); + self._renderLines(); + self._openFirstLine(); + }); + }, + /** * append the renderer and instantiate the line renderers * @@ -199,7 +215,10 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, { widget.appendTo(self.$('.o_reconciliation_lines')); }); if (this.model.hasMoreLines() === false) { - this.renderer.hideLoadMoreButton(); + this.renderer.hideLoadMoreButton(true); + } + else { + this.renderer.hideLoadMoreButton(false); } }, diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js index bc16bd73afa..a192b2210af 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_model.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js @@ -344,22 +344,65 @@ var StatementModel = BasicModel.extend({ */ load: function (context) { var self = this; - var statement_ids = context.statement_ids; - if (!statement_ids) { + // var statement_ids = context.statement_ids; + this.statement_ids = context.statement_ids; + if (!this.statement_ids) { return $.when(); } this.context = context; - + return self.reload(); + + }, + /** + * Load more bank statement line + * + * @param {integer} qty quantity to load + * @returns {Deferred} + */ + loadMore: function(qty) { + if (qty === undefined) { + qty = this.defaultDisplayQty; + } + var ids = _.pluck(this.lines, 'id'); + ids = ids.splice(this.pagerIndex, qty); + this.pagerIndex += qty; + return this.loadData(ids, this._getExcludedIds()); + }, + /** + * RPC method to load informations on lines + * + * @param {Array} ids ids of bank statement line passed to rpc call + * @param {Array} excluded_ids list of move_line ids that needs to be excluded from search + * @returns {Deferred} + */ + loadData: function(ids, excluded_ids) { + var self = this; + return self._rpc({ + model: 'account.reconciliation.widget', + method: 'get_bank_statement_line_data', + args: [ids, excluded_ids], + context: self.context, + }) + .then(self._formatLine.bind(self)); + }, + /** + * Reload all data + */ + reload: function() { + var self = this; + self.alreadyDisplayed = []; + self.lines = {}; + self.pagerIndex = 0; var def_statement = this._rpc({ model: 'account.reconciliation.widget', method: 'get_bank_statement_data', - args: [statement_ids], + args: [self.statement_ids], }) .then(function (statement) { self.statement = statement; - self.bank_statement_id = statement_ids.length === 1 ? {id: statement_ids[0], display_name: statement.statement_name} : false; - self.valuenow = statement.value_min; - self.valuemax = statement.value_max; + self.bank_statement_id = self.statement_ids.length === 1 ? {id: self.statement_ids[0], display_name: statement.statement_name} : false; + self.valuenow = self.valuenow || statement.value_min; + self.valuemax = self.valuemax || statement.value_max; self.context.journal_id = statement.journal_id; _.each(statement.lines, function (res) { var handle = _.uniqueId('rline'); @@ -378,11 +421,11 @@ var StatementModel = BasicModel.extend({ }); }); var domainReconcile = []; - if (context && context.company_ids) { - domainReconcile.push(['company_id', 'in', context.company_ids]); + if (self.context && self.context.company_ids) { + domainReconcile.push(['company_id', 'in', self.context.company_ids]); } - if (context && context.active_model === 'account.journal' && context.active_ids) { - domainReconcile.push(['journal_id', 'in', [false].concat(context.active_ids)]); + if (self.context && self.context.active_model === 'account.journal' && self.context.active_ids) { + domainReconcile.push(['journal_id', 'in', [false].concat(self.context.active_ids)]); } var def_reconcileModel = this._rpc({ model: 'account.reconcile.model', @@ -428,40 +471,6 @@ var StatementModel = BasicModel.extend({ }) }); }, - /** - * Load more bank statement line - * - * @param {integer} qty quantity to load - * @returns {Deferred} - */ - loadMore: function(qty) { - if (qty === undefined) { - qty = this.defaultDisplayQty; - } - var ids = _.pluck(this.lines, 'id'); - ids = ids.splice(this.pagerIndex, qty); - this.pagerIndex += qty; - return this.loadData(ids, this._getExcludedIds()); - }, - /** - * RPC method to load informations on lines - * - * @param {Array} ids ids of bank statement line passed to rpc call - * @param {Array} excluded_ids list of move_line ids that needs to be excluded from search - * @returns {Deferred} - */ - loadData: function(ids, excluded_ids) { - var self = this; - return self._rpc({ - model: 'account.reconciliation.widget', - method: 'get_bank_statement_line_data', - args: [ids, excluded_ids], - context: self.context, - }) - .then(function(res){ - return self._formatLine(res['lines']); - }) - }, /** * Add lines into the propositions from the reconcile model * Can add 2 lines, and each with its taxes. The second line become editable diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index 1f2eaea2432..f0641eae409 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -82,8 +82,13 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, { /* * hide the button to load more statement line */ - hideLoadMoreButton: function () { - this.$('.js_load_more').hide(); + hideLoadMoreButton: function (show) { + if (!show) { + this.$('.js_load_more').show(); + } + else { + this.$('.js_load_more').hide(); + } }, showRainbowMan: function (state) { var dt = Date.now()-this.time; @@ -300,7 +305,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { } ) }; - self.fields.partner_id.appendTo(self.$('.accounting_view caption')); + self.fields.partner_id.insertAfter(self.$('.accounting_view caption .o_buttons')); }); $('') .appendTo(this.$('thead .cell_info_popover')) @@ -599,11 +604,15 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { */ _onCreateReconcileModel: function (event) { event.preventDefault(); + var self = this; this.do_action({ type: 'ir.actions.act_window', res_model: 'account.reconcile.model', views: [[false, 'form']], target: 'current' + }, + { + on_reverse_breadcrumb: function() {self.trigger_up('reload');}, }); }, _editAmount: function (event) { @@ -627,6 +636,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { */ _onEditReconcileModel: function (event) { event.preventDefault(); + var self = this; this.do_action({ type: 'ir.actions.act_window', res_model: 'account.reconcile.model', @@ -634,6 +644,9 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { view_type: "list", view_mode: "list", target: 'current' + }, + { + on_reverse_breadcrumb: function() {self.trigger_up('reload');}, }); }, /** diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index ec9faf38bd2..3c316a4ccea 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -39,6 +39,9 @@ This module contains all the common features of Sales Management and eCommerce. 'data/sale_demo.xml', 'data/product_product_demo.xml', ], + 'qweb': [ + "static/src/xml/account_reconciliation.xml", + ], 'uninstall_hook': "uninstall_hook", 'installable': True, 'auto_install': False diff --git a/addons/sale/models/__init__.py b/addons/sale/models/__init__.py index e2e2d1b5c66..de4ebb6bff9 100644 --- a/addons/sale/models/__init__.py +++ b/addons/sale/models/__init__.py @@ -3,6 +3,7 @@ from . import analytic from . import account_invoice +from . import account_reconciliation_widget from . import product_product from . import product_template from . import res_company diff --git a/addons/sale/models/account_reconciliation_widget.py b/addons/sale/models/account_reconciliation_widget.py new file mode 100644 index 00000000000..ff6612a0891 --- /dev/null +++ b/addons/sale/models/account_reconciliation_widget.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +from odoo import api, models + + +class AccountReconciliation(models.AbstractModel): + _inherit = 'account.reconciliation.widget' + + def _get_sales_order(self, res): + stl_ids = [l.get('st_line', {}).get('id') for l in res.get('lines', [])] + if not stl_ids: + return res + # Search if we can find a sale order line that match the statement reference + sql_query = """ + SELECT stl.id, array_agg(o.id) AS order_id + FROM sale_order o, + account_bank_statement_line stl + WHERE + lower(concat(o.name,o.reference)) ~ lower(stl.name) + AND stl.id IN %s + AND (stl.partner_id is null OR stl.partner_id = o.partner_id) + AND (o.invoice_status = 'to invoice' OR o.state = 'sent') + AND o.company_id = %s + GROUP BY stl.id + ORDER BY stl.id + """ + company_id = res.get('lines')[0].get('st_line', {}).get('company_id') + self.env.cr.execute(sql_query, (tuple(stl_ids), company_id)) + results = {} + for el in self.env.cr.dictfetchall(): + results[el.get('id')] = el.get('order_id') + for line in res.get('lines', []): + so_ids = results.get(line['st_line'].get('id')) + if so_ids: + line['sale_order_ids'] = so_ids + return res + + @api.model + def get_bank_statement_line_data(self, st_line_ids, excluded_ids=None): + res = super(AccountReconciliation, self).get_bank_statement_line_data(st_line_ids=st_line_ids, excluded_ids=excluded_ids) + res = self._get_sales_order(res) + return res diff --git a/addons/sale/static/src/js/account_reconciliation.js b/addons/sale/static/src/js/account_reconciliation.js new file mode 100644 index 00000000000..60d7d01459b --- /dev/null +++ b/addons/sale/static/src/js/account_reconciliation.js @@ -0,0 +1,68 @@ +odoo.define('sale.account_reconciliation', function (require) { +"use strict"; + +var ReconciliationRenderers = require('account.ReconciliationRenderer'); +var ReconciliationAction = require('account.ReconciliationClientAction'); +var LineRenderer = ReconciliationRenderers.LineRenderer; +var StatementAction = ReconciliationAction.StatementAction; +var core = require('web.core'); +var _t = core._t; + +LineRenderer.include({ + events: _.extend( + {}, + LineRenderer.prototype.events, + {'click .accounting_view caption .js_open_so': '_onOpenSaleOrder'} + ), + + _onOpenSaleOrder: function (event) { + event.preventDefault(); + this.trigger_up('open_sale_orders'); + }, +}); + +StatementAction.include({ + custom_events: _.extend( + {}, + StatementAction.prototype.custom_events, + {open_sale_orders: '_onOpenSaleOrders',} + ), + + _onOpenSaleOrders: function(event) { + var self = this; + var handle = event.target.handle; + var line = this.model.getLine(handle); + if (line.sale_order_ids && line.sale_order_ids.length > 1) { + // Open tree view + this.do_action({ + name: _t('Sale Orders'), + type: 'ir.actions.act_window', + res_model: 'sale.order', + views: [[false, 'list'], [false, 'form']], + view_type: "list", + view_mode: "list", + target: 'current', + domain: [['id', 'in', line.sale_order_ids]] + }, + { + on_reverse_breadcrumb: function() {self.trigger_up('reload');}, + }); + } + else if (line.sale_order_ids && line.sale_order_ids.length === 1) { + // Open form view + this.do_action({ + name: _t('Sale Order'), + type: 'ir.actions.act_window', + res_model: 'sale.order', + views: [[false, 'form']], + target: 'current', + res_id: line.sale_order_ids[0], + }, + { + on_reverse_breadcrumb: function() {self.trigger_up('reload');}, + }); + } + }, +}); + +}) \ No newline at end of file diff --git a/addons/sale/static/src/xml/account_reconciliation.xml b/addons/sale/static/src/xml/account_reconciliation.xml new file mode 100644 index 00000000000..14fe678cc4a --- /dev/null +++ b/addons/sale/static/src/xml/account_reconciliation.xml @@ -0,0 +1,15 @@ + + + + + + + +
+ There are uninvoiced sales orders matching the communication of the bank statement line +
+
+
+
+ +
\ No newline at end of file diff --git a/addons/sale/views/assets.xml b/addons/sale/views/assets.xml index 458b884f9d3..5ee94f6fdc2 100644 --- a/addons/sale/views/assets.xml +++ b/addons/sale/views/assets.xml @@ -13,6 +13,7 @@ +