Permalink
Browse files

[IMP] sale: notify matching SO in reconciliation widget

We now display a message informing the user that the statement line is matching a sent/to_invoice SO. The user can thus decide to follow the link and confirm the SO, create the invoice and come back on the reconciliation widget to reconcile the prepayment with the validated invoice (automatically detected).

Was task #1879015
  • Loading branch information...
csnauwaert authored and qdp-odoo committed Sep 18, 2018
1 parent 1a5d781 commit aa8ce30d232e83c2e931e2ebfd3042018af37b75
@@ -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
@@ -97,6 +98,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
*
@@ -196,7 +212,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);
}
},
@@ -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
@@ -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;
@@ -302,7 +307,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'));
});
$('<span class="line_info_button fa fa-info-circle"/>')
.appendTo(this.$('thead .cell_info_popover'))
@@ -618,11 +623,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');},
});
},
/**
@@ -631,13 +640,17 @@ 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',
views: [[false, 'list'], [false, 'form']],
view_type: "list",
view_mode: "list",
target: 'current'
},
{
on_reverse_breadcrumb: function() {self.trigger_up('reload');},
});
},
/**
@@ -39,6 +39,9 @@
'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
@@ -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
@@ -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
@@ -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');},
});
}
},
});
})
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="reconciliation.line" t-extend="reconciliation.line">
<t t-jquery="caption" t-operation="append">
<t t-if="state.sale_order_ids &amp;&amp; state.sale_order_ids.length > 0">
<div class="alert alert-info">
There are <strong><a href="#" class="js_open_so">uninvoiced sales orders</a></strong> matching the communication of the bank statement line
</div>
</t>
</t>
</t>
</templates>
@@ -13,6 +13,7 @@
<script type="text/javascript" src="/sale/static/src/js/product_configurator_controller.js"></script>
<script type="text/javascript" src="/sale/static/src/js/product_configurator_view.js"></script>
<script type="text/javascript" src="/sale/static/src/js/product_configurator_modal.js"></script>
<script type="text/javascript" src="/sale/static/src/js/account_reconciliation.js"></script>
</xpath>
</template>
<template id="assets_frontend_inherit_sale" inherit_id="web.assets_frontend" name="Sale frontend assets">

0 comments on commit aa8ce30

Please sign in to comment.