flectra/addons/account/static/src/js/reconciliation/reconciliation_action.js

379 lines
13 KiB
JavaScript

flectra.define('account.ReconciliationClientAction', function (require) {
"use strict";
var ReconciliationModel = require('account.ReconciliationModel');
var ReconciliationRenderer = require('account.ReconciliationRenderer');
var ControlPanelMixin = require('web.ControlPanelMixin');
var Widget = require('web.Widget');
var core = require('web.core');
/**
* Widget used as action for 'account.bank.statement' reconciliation
*/
var StatementAction = Widget.extend(ControlPanelMixin, {
title: core._t('Bank reconciliation'),
template: 'reconciliation',
custom_events: {
change_mode: '_onAction',
change_filter: '_onAction',
change_offset: '_onAction',
change_partner: '_onAction',
add_proposition: '_onAction',
search_balance_amount: '_onAction',
remove_proposition: '_onAction',
update_proposition: '_onAction',
create_proposition: '_onAction',
quick_create_proposition: '_onAction',
toggle_partial_reconcile: '_onAction',
auto_reconciliation: '_onValidate',
validate: '_onValidate',
validate_all_balanced: '_onValidate',
change_name: '_onChangeName',
close_statement: '_onCloseStatement',
load_more: '_onLoadMore',
},
config: {
// used to instanciate the model
Model: ReconciliationModel.StatementModel,
// used to instanciate the action interface
ActionRenderer: ReconciliationRenderer.StatementRenderer,
// used to instanciate each widget line
LineRenderer: ReconciliationRenderer.LineRenderer,
// used context params
params: ['statement_ids'],
// number of moves lines displayed in 'match' mode
limitMoveLines: 5,
},
/**
* @override
* @param {Object} params
* @param {Object} params.context
*
*/
init: function (parent, params) {
this._super.apply(this, arguments);
this.action_manager = parent;
this.params = params;
this.model = new this.config.Model(this, {
modelName: "account.bank.statement.line",
limitMoveLines: params.params && params.params.limitMoveLines || this.config.limitMoveLines,
});
this.widgets = [];
if (!this.action_manager) {
this.set_cp_bus(new Widget());
}
// Adding values from the context is necessary to put this information in the url via the action manager so that
// you can retrieve it if the person shares his url or presses f5
_.each(params.params, function (value, name) {
params.context[name] = name.indexOf('_ids') !== -1 ? _.map((value+'').split(), parseFloat) : value;
});
params.params = {};
_.each(this.config.params, function (name) {
if (params.context[name]) {
params.params[name] = name.indexOf('_ids') !== -1 && _.isArray(params.context[name]) ? params.context[name].join() : params.context[name];
}
});
},
/**
* instantiate the action renderer
*
* @override
*/
willStart: function () {
var self = this;
var def = this.model.load(this.params.context).then(this._super.bind(this));
return def.then(function () {
self.title = self.model.bank_statement_id ? self.model.bank_statement_id.display_name : self.title;
self.renderer = new self.config.ActionRenderer(self, self.model, {
'bank_statement_id': self.model.bank_statement_id,
'valuenow': self.model.valuenow,
'valuemax': self.model.valuemax,
'defaultDisplayQty': self.model.defaultDisplayQty,
'title': self.title,
});
});
},
/**
* append the renderer and instantiate the line renderers
*
* @override
*/
start: function () {
var self = this;
this.set("title", this.title);
var breadcrumbs = this.action_manager && this.action_manager.get_breadcrumbs() || [{ title: this.title, action: this }];
this.update_control_panel({breadcrumbs: breadcrumbs, search_view_hidden: true}, {clear: true});
this.renderer.prependTo(self.$('.o_form_sheet'));
this._renderLines();
this._openFirstLine();
},
/**
* update the control panel and breadcrumbs
*
* @override
*/
do_show: function () {
this._super.apply(this, arguments);
if (this.action_manager) {
var breadcrumbs = this.action_manager && this.action_manager.get_breadcrumbs() || [{ title: this.title, action: this }];
while (breadcrumbs.length) {
if (breadcrumbs[breadcrumbs.length-1].action.widget === this) {
break;
}
breadcrumbs.pop();
}
this.update_control_panel({breadcrumbs: breadcrumbs, search_view_hidden: true}, {clear: true});
this.action_manager.do_push_state({
action: this.params.tag,
active_id: this.params.res_id,
});
}
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
* @param {string} handle
* @returns {Widget} widget line
*/
_getWidget: function (handle) {
return _.find(this.widgets, function (widget) {return widget.handle===handle;});
},
/**
*
*/
_loadMore: function(qty) {
var self = this;
return this.model.loadMore(qty).then(function () {
self._renderLines();
});
},
/**
* sitch to 'match' the first available line
*
* @private
*/
_openFirstLine: function () {
var self = this;
var handle = _.compact(_.map(this.model.lines, function (line, handle) {
return line.reconciled ? null : handle;
}))[0];
if (handle) {
var line = this.model.getLine(handle);
this.model.changeMode(handle, 'match').always(function () {
self._getWidget(handle).update(line);
});
}
return handle;
},
/**
* render line widget and append to view
*
* @private
*/
_renderLines: function () {
var self = this;
var linesToDisplay = this.model.getStatementLines();
_.each(linesToDisplay, function (line, handle) {
var widget = new self.config.LineRenderer(self, self.model, line);
widget.handle = handle;
self.widgets.push(widget);
widget.appendTo(self.$('.o_reconciliation_lines'));
});
if (this.model.hasMoreLines() === false) {
this.renderer.hideLoadMoreButton();
}
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* dispatch on the camelcased event name to model method then update the
* line renderer with the new state. If the mode was switched from 'inactive'
* to 'create' or 'match', the other lines switch to 'inactive' mode
*
* @private
* @param {FlectraEvent} event
*/
_onAction: function (event) {
var self = this;
var handle = event.target.handle;
var line = this.model.getLine(handle);
var mode = line.mode;
this.model[_.str.camelize(event.name)](handle, event.data.data).always(function () {
self._getWidget(handle).update(line);
if (mode === 'inactive' && line.mode !== 'inactive') {
_.each(self.model.lines, function (line, _handle) {
if (line.mode !== 'inactive' && _handle !== handle) {
self.model.changeMode(_handle, 'inactive');
var widget = self._getWidget(_handle);
if (widget) {
widget.update(line);
}
}
});
}
});
},
/**
* call 'changeName' model method
*
* @private
* @param {FlectraEvent} event
*/
_onChangeName: function (event) {
var self = this;
var title = event.data.data;
this.model.changeName(title).then(function () {
self.title = title;
self.set("title", title);
self.renderer.update({
'valuenow': self.model.valuenow,
'valuemax': self.model.valuemax,
'title': title,
});
});
},
/**
* call 'closeStatement' model method
*
* @private
* @param {FlectraEvent} event
*/
_onCloseStatement: function (event) {
var self = this;
return this.model.closeStatement().then(function (result) {
self.do_action({
name: 'Bank Statements',
res_model: 'account.bank.statement',
res_id: result,
views: [[false, 'form']],
type: 'ir.actions.act_window',
view_type: 'form',
view_mode: 'form',
});
});
},
/**
* Load more statement and render them
*
* @param {FlectraEvent} event
*/
_onLoadMore: function (event) {
return this._loadMore(this.model.defaultDisplayQty);
},
/**
* call 'validate' or 'autoReconciliation' model method then destroy the
* validated lines and update the action renderer with the new status bar
* values and notifications then open the first available line
*
* @private
* @param {FlectraEvent} event
*/
_onValidate: function (event) {
var self = this;
var handle = event.target.handle;
var method = event.name.indexOf('auto_reconciliation') === -1 ? 'validate' : 'autoReconciliation';
this.model[method](handle).then(function (result) {
self.renderer.update({
'valuenow': self.model.valuenow,
'valuemax': self.model.valuemax,
'title': self.title,
'time': Date.now()-self.time,
'notifications': result.notifications,
'context': self.model.getContext(),
});
_.each(result.handles, function (handle) {
self._getWidget(handle).destroy();
var index = _.findIndex(self.widgets, function (widget) {return widget.handle===handle;});
self.widgets.splice(index, 1);
});
// Get number of widget and if less than constant and if there are more to laod, load until constant
if (self.widgets.length < self.model.defaultDisplayQty
&& self.model.valuemax - self.model.valuenow >= self.model.defaultDisplayQty) {
var toLoad = self.model.defaultDisplayQty - self.widgets.length;
self._loadMore(toLoad);
}
self._openFirstLine();
});
},
});
/**
* Widget used as action for 'account.move.line' and 'res.partner' for the
* manual reconciliation and mark data as reconciliate
*/
var ManualAction = StatementAction.extend({
title: core._t('Journal Items to Reconcile'),
config: {
Model: ReconciliationModel.ManualModel,
ActionRenderer: ReconciliationRenderer.ManualRenderer,
LineRenderer: ReconciliationRenderer.ManualLineRenderer,
params: ['company_ids', 'mode', 'partner_ids', 'account_ids'],
limitMoveLines: 10,
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* call 'validate' or 'autoReconciliation' model method then destroy the
* reconcilied lines, update the not reconcilied and update the action
* renderer with the new status bar values and notifications then open the
* first available line
*
* @private
* @param {FlectraEvent} event
*/
_onValidate: function (event) {
var self = this;
var handle = event.target.handle;
var method = event.name.indexOf('auto_reconciliation') === -1 ? 'validate' : 'autoReconciliation';
this.model[method](handle).then(function (result) {
_.each(result.reconciled, function (handle) {
self._getWidget(handle).destroy();
});
_.each(result.updated, function (handle) {
self._getWidget(handle).update(self.model.getLine(handle));
});
self.renderer.update({
valuenow: _.compact(_.invoke(self.widgets, 'isDestroyed')).length,
valuemax: self.widgets.length,
title: self.title,
time: Date.now()-self.time,
});
if(!_.any(result.updated, function (handle) {
return self.model.getLine(handle).mode !== 'inactive';
})) {
self._openFirstLine();
}
});
},
});
core.action_registry.add('bank_statement_reconciliation_view', StatementAction);
core.action_registry.add('manual_reconciliation_view', ManualAction);
return {
StatementAction: StatementAction,
ManualAction: ManualAction,
};
});