summaryrefslogtreecommitdiff
path: root/addons/account/static
diff options
context:
space:
mode:
authorwan <wan@odoo.com>2019-10-09 13:56:00 +0000
committerwan <wan@odoo.com>2019-10-18 12:53:41 +0000
commit9ba8734f15e1a292ca27b1a026e8366a91b2a8c9 (patch)
tree490e30cb5b674cd9ee82240e8be92e29f192d476 /addons/account/static
parent48e563f0bfdc179852ed5ac101b8f46e866b83a5 (diff)
[MOV] account: reconciliation widget
Task 2034073 The reconciliation widget is now entirely part of the enterprise version as it is an advanced accounting feature closes odoo/odoo#38424 Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
Diffstat (limited to 'addons/account/static')
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_action.js499
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_model.js1866
-rw-r--r--addons/account/static/src/js/reconciliation/reconciliation_renderer.js924
-rw-r--r--addons/account/static/src/xml/account_reconciliation.xml400
-rw-r--r--addons/account/static/tests/reconciliation_tests.js2093
-rw-r--r--addons/account/static/tests/tours/reconciliation.js108
6 files changed, 0 insertions, 5890 deletions
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js
deleted file mode 100644
index 3c7969ed1b4..00000000000
--- a/addons/account/static/src/js/reconciliation/reconciliation_action.js
+++ /dev/null
@@ -1,499 +0,0 @@
-odoo.define('account.ReconciliationClientAction', function (require) {
-"use strict";
-
-var AbstractAction = require('web.AbstractAction');
-var ReconciliationModel = require('account.ReconciliationModel');
-var ReconciliationRenderer = require('account.ReconciliationRenderer');
-var core = require('web.core');
-var QWeb = core.qweb;
-
-
-/**
- * Widget used as action for 'account.bank.statement' reconciliation
- */
-var StatementAction = AbstractAction.extend({
- hasControlPanel: true,
- withSearchBar: true,
- loadControlPanel: true,
- title: core._t('Bank Reconciliation'),
- contentTemplate: 'reconciliation',
- custom_events: {
- change_mode: '_onAction',
- change_filter: '_onAction',
- change_offset: '_onAction',
- change_partner: '_onAction',
- add_proposition: '_onAction',
- remove_proposition: '_onAction',
- update_proposition: '_onAction',
- create_proposition: '_onAction',
- getPartialAmount: '_onActionPartialAmount',
- quick_create_proposition: '_onAction',
- partial_reconcile: '_onAction',
- validate: '_onValidate',
- close_statement: '_onCloseStatement',
- load_more: '_onLoadMore',
- reload: 'reload',
- search: '_onSearch',
- navigation_move:'_onNavigationMove',
- },
- config: _.extend({}, AbstractAction.prototype.config, {
- // used to instantiate the model
- Model: ReconciliationModel.StatementModel,
- // used to instantiate the action interface
- ActionRenderer: ReconciliationRenderer.StatementRenderer,
- // used to instantiate each widget line
- LineRenderer: ReconciliationRenderer.LineRenderer,
- // used context params
- params: ['statement_line_ids'],
- // number of statements/partners/accounts to display
- defaultDisplayQty: 10,
- // number of moves lines displayed in 'match' mode
- limitMoveLines: 15,
- }),
-
- _onNavigationMove: function (ev) {
- var non_reconciled_keys = _.keys(_.pick(this.model.lines, function(value, key, object) {return !value.reconciled}));
- var currentIndex = _.indexOf(non_reconciled_keys, ev.data.handle);
- var widget = false;
- switch (ev.data.direction) {
- case 'up':
- ev.stopPropagation();
- widget = this._getWidget(non_reconciled_keys[currentIndex-1]);
- break;
- case 'down':
- ev.stopPropagation();
- widget = this._getWidget(non_reconciled_keys[currentIndex+1]);
- break;
- case 'validate':
- ev.stopPropagation();
- widget = this._getWidget(non_reconciled_keys[currentIndex]);
- widget.$('caption .o_buttons button:visible').click();
- break;
- }
- if (widget) widget.$el.focus();
- },
-
- /**
- * @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.controlPanelParams.modelName = 'account.bank.statement.line';
- this.model = new this.config.Model(this, {
- modelName: "account.reconciliation.widget",
- defaultDisplayQty: params.params && params.params.defaultDisplayQty || this.config.defaultDisplayQty,
- limitMoveLines: params.params && params.params.limitMoveLines || this.config.limitMoveLines,
- });
- this.widgets = [];
- // 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] = 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 () {
- if (!self.model.context || !self.model.context.active_id) {
- self.model.context = {'active_id': self.params.context.active_id};
- }
- if (self.params.context.journal_id) {
- self.model.context.active_id = self.params.context.journal_id;
- }
- if (self.model.context.active_id) {
- var promise = self._rpc({
- model: 'account.journal',
- method: 'read',
- args: [self.model.context.active_id, ['display_name']],
- });
- } else {
- var promise = Promise.resolve();
- }
- return promise.then(function (result) {
- var title = result && result[0] ? result[0]['display_name'] : self.params.display_name || ''
- self._setTitle(title);
- self.renderer = new self.config.ActionRenderer(self, self.model, {
- 'bank_statement_line_id': self.model.bank_statement_line_id,
- 'valuenow': self.model.valuenow,
- 'valuemax': self.model.valuemax,
- 'defaultDisplayQty': self.model.defaultDisplayQty,
- 'title': title,
- });
- });
- });
- },
-
- reload: function() {
- // On reload destroy all rendered line widget, reload data and then rerender widget
- var self = this;
-
- self.$('.o_reconciliation_lines').addClass('d-none'); // prevent the browser from recomputing css after each destroy for HUGE perf improvement on a lot of lines
- _.each(this.widgets, function(widget) {
- widget.destroy();
- });
- this.widgets = [];
- self.$('.o_reconciliation_lines').removeClass('d-none');
- return this.model.reload().then(function() {
- return self._renderLinesOrRainbow();
- });
- },
-
- _renderLinesOrRainbow: function() {
- var self = this;
- return self._renderLines().then(function() {
- var initialState = self.renderer._initialState;
- var valuenow = self.model.statement ? self.model.statement.value_min : initialState.valuenow;
- var valuemax = self.model.statement ? self.model.statement.value_max : initialState.valuemax;
- // No more lines to reconcile, trigger the rainbowman.
- if(valuenow === valuemax){
- initialState.valuenow = valuenow;
- initialState.context = self.model.getContext();
- self.renderer.showRainbowMan(initialState);
- self.remove_cp();
- }else{
- // Create a notification if some lines have been reconciled automatically.
- if(initialState.valuenow > 0)
- self.renderer._renderNotifications(self.model.statement.notifications);
- self._openFirstLine();
- self.renderer.$('[data-toggle="tooltip"]').tooltip();
- self.do_show();
- }
- });
- },
-
- /**
- * append the renderer and instantiate the line renderers
- *
- * @override
- */
- start: function () {
- var self = this;
- var args = arguments;
- var sup = this._super;
-
- return this.renderer.prependTo(self.$('.o_form_sheet')).then(function() {
- return self._renderLinesOrRainbow().then(function() {
- self.do_show();
- return sup.apply(self, args);
- });
- });
- },
-
- /**
- * update the control panel and breadcrumbs
- *
- * @override
- */
- do_show: function () {
- this._super.apply(this, arguments);
- if (this.action_manager) {
- this.$pager = $(QWeb.render('reconciliation.control.pager', {widget: this.renderer}));
- this.updateControlPanel({
- clear: true,
- cp_content: {
- $pager: this.$pager,
- },
- });
- this.renderer.$progress = this.$pager;
- $(this.renderer.$progress).parent().css('width', '100%').css('padding-left', '0');
- }
- },
-
- remove_cp: function() {
- this.updateControlPanel({
- clear: true,
- });
- },
-
- //--------------------------------------------------------------------------
- // 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 () {
- return 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, 'default').then(function () {
- self._getWidget(handle).update(line);
- }).guardedCatch(function(){
- self._getWidget(handle).update(line);
- }).then(function() {
- self._getWidget(handle).$el.focus();
- }
- );
- }
- return handle;
- },
-
- _forceUpdate: function() {
- var self = this;
- _.each(this.model.lines, function(handle) {
- var widget = self._getWidget(handle['handle']);
- if (widget && handle.need_update) {
- widget.update(handle);
- widget.need_update = false;
- }
- })
- },
- /**
- * render line widget and append to view
- *
- * @private
- */
- _renderLines: function () {
- var self = this;
- var linesToDisplay = this.model.getStatementLines();
- var linePromises = [];
- _.each(linesToDisplay, function (line, handle) {
- var widget = new self.config.LineRenderer(self, self.model, line);
- widget.handle = handle;
- self.widgets.push(widget);
- linePromises.push(widget.appendTo(self.$('.o_reconciliation_lines')));
- });
- if (this.model.hasMoreLines() === false) {
- this.renderer.hideLoadMoreButton(true);
- }
- else {
- this.renderer.hideLoadMoreButton(false);
- }
- return Promise.all(linePromises);
- },
-
- //--------------------------------------------------------------------------
- // 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_rp' or 'match_other', the other lines switch to
- * 'inactive' mode
- *
- * @private
- * @param {OdooEvent} event
- */
- _onAction: function (event) {
- var self = this;
- var handle = event.target.handle;
- var current_line = this.model.getLine(handle);
- this.model[_.str.camelize(event.name)](handle, event.data.data).then(function () {
- var widget = self._getWidget(handle);
- if (widget) {
- widget.update(current_line);
- }
- if (current_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);
- }
- }
- });
- }
- });
- },
-
- /**
- * @private
- * @param {OdooEvent} ev
- */
- _onSearch: function (ev) {
- var self = this;
- ev.stopPropagation();
- this.model.domain = ev.data.domain;
- this.model.display_context = 'search';
- self.reload().then(function() {
- self.renderer._updateProgressBar({
- 'valuenow': self.model.valuenow,
- 'valuemax': self.model.valuemax,
- });
- });
- },
-
- _onActionPartialAmount: function(event) {
- var self = this;
- var handle = event.target.handle;
- var line = this.model.getLine(handle);
- var amount = this.model.getPartialReconcileAmount(handle, event.data);
- self._getWidget(handle).updatePartialAmount(event.data.data, amount);
- },
-
- /**
- * call 'closeStatement' model method
- *
- * @private
- * @param {OdooEvent} 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.line',
- res_id: result,
- views: [[false, 'form']],
- type: 'ir.actions.act_window',
- view_mode: 'form',
- });
- $('.o_reward').remove();
- });
- },
- /**
- * Load more statement and render them
- *
- * @param {OdooEvent} event
- */
- _onLoadMore: function (event) {
- return this._loadMore(this.model.defaultDisplayQty);
- },
- /**
- * call 'validate' 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 {OdooEvent} event
- */
- _onValidate: function (event) {
- var self = this;
- var handle = event.target.handle;
- this.model.validate(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(),
- });
- self._forceUpdate();
- _.each(result.handles, function (handle) {
- var widget = self._getWidget(handle);
- if (widget) {
- widget.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'),
- withSearchBar: false,
- config: _.extend({}, StatementAction.prototype.config, {
- Model: ReconciliationModel.ManualModel,
- ActionRenderer: ReconciliationRenderer.ManualRenderer,
- LineRenderer: ReconciliationRenderer.ManualLineRenderer,
- params: ['company_ids', 'mode', 'partner_ids', 'account_ids'],
- defaultDisplayQty: 30,
- limitMoveLines: 15,
- }),
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * call 'validate' 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 {OdooEvent} event
- */
- _onValidate: function (event) {
- var self = this;
- var handle = event.target.handle;
- var method = 'validate';
- 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,
-};
-});
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js
deleted file mode 100644
index d76f9941153..00000000000
--- a/addons/account/static/src/js/reconciliation/reconciliation_model.js
+++ /dev/null
@@ -1,1866 +0,0 @@
-odoo.define('account.ReconciliationModel', function (require) {
-"use strict";
-
-var BasicModel = require('web.BasicModel');
-var field_utils = require('web.field_utils');
-var utils = require('web.utils');
-var session = require('web.session');
-var WarningDialog = require('web.CrashManager').WarningDialog;
-var core = require('web.core');
-var _t = core._t;
-
-
-/**
- * Model use to fetch, format and update 'account.reconciliation.widget',
- * datas allowing reconciliation
- *
- * The statement internal structure::
- *
- * {
- * valuenow: integer
- * valuenow: valuemax
- * [bank_statement_line_id]: {
- * id: integer
- * display_name: string
- * }
- * reconcileModels: [object]
- * accounts: {id: code}
- * }
- *
- * The internal structure of each line is::
- *
- * {
- * balance: {
- * type: number - show/hide action button
- * amount: number - real amount
- * amount_str: string - formated amount
- * account_code: string
- * },
- * st_line: {
- * partner_id: integer
- * partner_name: string
- * }
- * mode: string ('inactive', 'match_rp', 'match_other', 'create')
- * reconciliation_proposition: {
- * id: number|string
- * partial_amount: number
- * invalid: boolean - through the invalid line (without account, label...)
- * is_tax: boolean
- * account_code: string
- * date: string
- * date_maturity: string
- * label: string
- * amount: number - real amount
- * amount_str: string - formated amount
- * [already_paid]: boolean
- * [partner_id]: integer
- * [partner_name]: string
- * [account_code]: string
- * [journal_id]: {
- * id: integer
- * display_name: string
- * }
- * [ref]: string
- * [is_partially_reconciled]: boolean
- * [to_check]: boolean
- * [amount_currency_str]: string|false (amount in record currency)
- * }
- * mv_lines_match_rp: object - idem than reconciliation_proposition
- * mv_lines_match_other: object - idem than reconciliation_proposition
- * offset: integer
- * limitMoveLines: integer
- * filter: string
- * [createForm]: {
- * account_id: {
- * id: integer
- * display_name: string
- * }
- * tax_ids: {
- * id: integer
- * display_name: string
- * }
- * analytic_account_id: {
- * id: integer
- * display_name: string
- * }
- * analytic_tag_ids: {
- * }
- * label: string
- * amount: number,
- * [journal_id]: {
- * id: integer
- * display_name: string
- * }
- * }
- * }
- */
-var StatementModel = BasicModel.extend({
- avoidCreate: false,
- quickCreateFields: ['account_id', 'amount', 'analytic_account_id', 'label', 'tax_ids', 'force_tax_included', 'analytic_tag_ids', 'to_check'],
-
- // overridden in ManualModel
- modes: ['create', 'match_rp', 'match_other'],
-
- /**
- * @override
- *
- * @param {Widget} parent
- * @param {object} options
- */
- init: function (parent, options) {
- this._super.apply(this, arguments);
- this.reconcileModels = [];
- this.lines = {};
- this.valuenow = 0;
- this.valuemax = 0;
- this.alreadyDisplayed = [];
- this.domain = [];
- this.defaultDisplayQty = options && options.defaultDisplayQty || 10;
- this.limitMoveLines = options && options.limitMoveLines || 15;
- this.display_context = 'init';
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * add a reconciliation proposition from the matched lines
- * We also display a warning if the user tries to add 2 line with different
- * account type
- *
- * @param {string} handle
- * @param {number} mv_line_id
- * @returns {Promise}
- */
- addProposition: function (handle, mv_line_id) {
- var self = this;
- var line = this.getLine(handle);
- var prop = _.clone(_.find(line['mv_lines_'+line.mode], {'id': mv_line_id}));
- this._addProposition(line, prop);
- line.limit_override = (line.offset + 1) + this.limitMoveLines;
-
- // remove all non valid lines
- line.reconciliation_proposition = _.filter(line.reconciliation_proposition, function (prop) {return !prop.invalid;});
-
- // Onchange the partner if not already set on the statement line.
- if(!line.st_line.partner_id && line.reconciliation_proposition
- && line.reconciliation_proposition.length == 1 && prop.partner_id && line.type === undefined){
- return this.changePartner(handle, {'id': prop.partner_id, 'display_name': prop.partner_name}, true)
- .then(function () {
- return Promise.all([self._computeLine(line), self._performMoveLine(handle, 'match_rp'), self._performMoveLine(handle, 'match_other')]);
- });
- }
-
- return Promise.all([this._computeLine(line), this._performMoveLine(handle, 'match_rp'), self._performMoveLine(handle, 'match_other')]);
- },
- /**
- * change the filter for the target line and fetch the new matched lines
- *
- * @param {string} handle
- * @param {string} filter
- * @returns {Promise}
- */
- changeFilter: function (handle, filter) {
- var line = this.getLine(handle);
- line['filter_'+line.mode] = filter;
- line.offset = 0;
- return this._performMoveLine(handle, line.mode);
- },
- /**
- * change the mode line ('inactive', 'match_rp', 'match_other', 'create'),
- * and fetch the new matched lines or prepare to create a new line
- *
- * ``match_rp``
- * display the matched lines from receivable/payable accounts, the user
- * can select the lines to apply there as proposition
- * ``match_other``
- * display the other matched lines, the user can select the lines to apply
- * there as proposition
- * ``create``
- * display fields and quick create button to create a new proposition
- * for the reconciliation
- *
- * @param {string} handle
- * @param {'inactive' | 'match_rp' | 'create'} mode
- * @returns {Promise}
- */
- changeMode: function (handle, mode) {
- var self = this;
- var line = this.getLine(handle);
- if (mode === 'default') {
- var match_requests = self.modes.filter(x => x.startsWith('match')).map(x => this._performMoveLine(handle, x))
- return Promise.all(match_requests).then(function() {
- return self.changeMode(handle, self._getDefaultMode(handle));
- });
- }
- if (mode === 'next') {
- var available_modes = self._getAvailableModes(handle)
- mode = available_modes[(available_modes.indexOf(line.mode) + 1) % available_modes.length];
- }
- line.mode = mode;
- if (['match_rp', 'match_other'].includes(line.mode)) {
- if (!(line['mv_lines_' + line.mode] && line['mv_lines_' + line.mode].length)) {
- return this._performMoveLine(handle, line.mode);
- } else {
- return this._formatMoveLine(handle, line.mode, line['mv_lines_' + line.mode]);
- }
- }
- if (line.mode === 'create') {
- return this.createProposition(handle);
- }
- return Promise.resolve();
- },
- /**
- * change the offset for the matched lines, and fetch the new matched lines
- *
- * @param {string} handle
- * @param {number} offset
- * @returns {Promise}
- */
- changeOffset: function (handle, offset) {
- var line = this.getLine(handle);
- line.offset += (offset > 0 ? 1 : -1) * this.limitMoveLines;
- return this._performMoveLine(handle, line.mode);
- },
- /**
- * change the partner on the line and fetch the new matched lines
- *
- * @param {string} handle
- * @param {bool} preserveMode
- * @param {Object} partner
- * @param {string} partner.display_name
- * @param {number} partner.id
- * @returns {Promise}
- */
- changePartner: function (handle, partner, preserveMode) {
- var self = this;
- var line = this.getLine(handle);
- line.st_line.partner_id = partner && partner.id;
- line.st_line.partner_name = partner && partner.display_name || '';
- return Promise.resolve(partner && this._changePartner(handle, partner.id))
- .then(function() {
- if(line.st_line.partner_id){
- _.each(line.reconciliation_proposition, function(prop){
- if(prop.partner_id != line.st_line.partner_id){
- line.reconciliation_proposition = [];
- return false;
- }
- });
- }
- return self._computeLine(line);
- })
- .then(function () {
- return self.changeMode(handle, preserveMode ? line.mode : 'default', true);
- })
-
- },
- /**
- * close the statement
- * @returns {Promise<number>} resolves to the res_id of the closed statements
- */
- closeStatement: function () {
- var self = this;
- return this._rpc({
- model: 'account.bank.statement.line',
- method: 'button_confirm_bank',
- args: [self.bank_statement_line_id.id],
- })
- .then(function () {
- return self.bank_statement_line_id.id;
- });
- },
- /**
- *
- * then open the first available line
- *
- * @param {string} handle
- * @returns {Promise}
- */
- createProposition: function (handle) {
- var line = this.getLine(handle);
- var prop = _.filter(line.reconciliation_proposition, '__focus');
- prop = this._formatQuickCreate(line);
- line.reconciliation_proposition.push(prop);
- line.createForm = _.pick(prop, this.quickCreateFields);
- return this._computeLine(line);
- },
- /**
- * Return context information and journal_id
- * @returns {Object} context
- */
- getContext: function () {
- return this.context;
- },
- /**
- * Return the lines that needs to be displayed by the widget
- *
- * @returns {Object} lines that are loaded and not yet displayed
- */
- getStatementLines: function () {
- var self = this;
- var linesToDisplay = _.pick(this.lines, function(value, key, object) {
- if (value.visible === true && self.alreadyDisplayed.indexOf(key) === -1) {
- self.alreadyDisplayed.push(key);
- return object;
- }
- });
- return linesToDisplay;
- },
- /**
- * Return a boolean telling if load button needs to be displayed or not
- * overridden in ManualModel
- *
- * @returns {boolean} true if load more button needs to be displayed
- */
- hasMoreLines: function () {
- var notDisplayed = _.filter(this.lines, function(line) { return !line.visible; });
- if (notDisplayed.length > 0) {
- return true;
- }
- return false;
- },
- /**
- * get the line data for this handle
- *
- * @param {Object} handle
- * @returns {Object}
- */
- getLine: function (handle) {
- return this.lines[handle];
- },
- /**
- * load data from
- *
- * - 'account.bank.statement' fetch the line id and bank_statement_id info
- * - 'account.reconcile.model' fetch all reconcile model (for quick add)
- * - 'account.account' fetch all account code
- * - 'account.reconciliation.widget' fetch each line data
- *
- * overridden in ManualModel
- * @param {Object} context
- * @param {number[]} context.statement_line_ids
- * @returns {Promise}
- */
- load: function (context) {
- var self = this;
- this.context = context;
- this.statement_line_ids = context.statement_line_ids;
- if (this.statement_line_ids === undefined) {
- // This could be undefined if the user pressed F5, take everything as fallback instead of rainbowman
- return self._rpc({
- model: 'account.bank.statement.line',
- method: 'search_read',
- fields: ['id'],
- domain: [['journal_id', '=?', context.active_id]],
- }).then(function (result) {
- self.statement_line_ids = result.map(r => r.id);
- return self.reload()
- })
- } else {
- return self.reload();
- }
-
- },
- /**
- * Load more bank statement line
- *
- * @param {integer} qty quantity to load
- * @returns {Promise}
- */
- 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
- * overridden in ManualModel
- *
- * @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 {Promise}
- */
- 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',
- kwargs: {"bank_statement_line_ids":self.statement_line_ids, "srch_domain":self.domain},
- context: self.context,
- })
- .then(function (statement) {
- self.statement = statement;
- self.bank_statement_line_id = self.statement_line_ids.length === 1 ? {id: self.statement_line_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');
- self.lines[handle] = {
- id: res.st_line.id,
- partner_id: res.st_line.partner_id,
- handle: handle,
- reconciled: false,
- mode: 'inactive',
- mv_lines_match_rp: [],
- mv_lines_match_other: [],
- offset: 0,
- filter_match_rp: "",
- filter_match_other: "",
- reconciliation_proposition: [],
- reconcileModels: [],
- };
- });
- });
- var domainReconcile = [];
- if (self.context && self.context.company_ids) {
- domainReconcile.push(['company_id', 'in', self.context.company_ids]);
- }
- if (self.context && self.context.active_model === 'account.journal' && self.context.active_ids) {
- domainReconcile.push(['match_journal_ids', 'in', [false].concat(self.context.active_ids)]);
- }
- var def_reconcileModel = this._loadReconciliationModel({domainReconcile: domainReconcile});
- var def_account = this._rpc({
- model: 'account.account',
- method: 'search_read',
- fields: ['code'],
- })
- .then(function (accounts) {
- self.accounts = _.object(_.pluck(accounts, 'id'), _.pluck(accounts, 'code'));
- });
- var def_taxes = self._loadTaxes();
- return Promise.all([def_statement, def_reconcileModel, def_account, def_taxes]).then(function () {
- _.each(self.lines, function (line) {
- line.reconcileModels = self.reconcileModels;
- });
- var ids = _.pluck(self.lines, 'id');
- ids = ids.splice(0, self.defaultDisplayQty);
- self.pagerIndex = ids.length;
- return self._formatLine(self.statement.lines);
- });
- },
- _readAnalyticTags: function (params) {
- var self = this;
- this.analyticTags = {};
- if (!params || !params.res_ids || !params.res_ids.length) {
- return $.when();
- }
- var fields = (params && params.fields || []).concat(['id', 'display_name']);
- return this._rpc({
- model: 'account.analytic.tag',
- method: 'read',
- args: [
- params.res_ids,
- fields,
- ],
- }).then(function (tags) {
- for (var i=0; i<tags.length; i++) {
- var tag = tags[i];
- self.analyticTags[tag.id] = tag;
- }
- });
- },
- _loadReconciliationModel: function (params) {
- var self = this;
- return this._rpc({
- model: 'account.reconcile.model',
- method: 'search_read',
- domain: params.domainReconcile || [],
- })
- .then(function (reconcileModels) {
- var analyticTagIds = [];
- for (var i=0; i<reconcileModels.length; i++) {
- var modelTags = reconcileModels[i].analytic_tag_ids || [];
- for (var j=0; j<modelTags.length; j++) {
- if (analyticTagIds.indexOf(modelTags[j]) === -1) {
- analyticTagIds.push(modelTags[j]);
- }
- }
- }
- return self._readAnalyticTags({res_ids: analyticTagIds}).then(function () {
- for (var i=0; i<reconcileModels.length; i++) {
- var recModel = reconcileModels[i];
- var analyticTagData = [];
- var modelTags = reconcileModels[i].analytic_tag_ids || [];
- for (var j=0; j<modelTags.length; j++) {
- var tagId = modelTags[j];
- analyticTagData.push([tagId, self.analyticTags[tagId].display_name])
- }
- recModel.analytic_tag_ids = analyticTagData;
- }
- self.reconcileModels = reconcileModels;
- });
- });
- },
- _loadTaxes: function(){
- var self = this;
- self.taxes = {};
- return this._rpc({
- model: 'account.tax',
- method: 'search_read',
- fields: ['price_include', 'name'],
- }).then(function (taxes) {
- _.each(taxes, function(tax){
- self.taxes[tax.id] = {
- price_include: tax.price_include,
- display_name: tax.name,
- };
- });
- return taxes;
- });
- },
- /**
- * Add lines into the propositions from the reconcile model
- * Can add 2 lines, and each with its taxes. The second line become editable
- * in the create mode.
- *
- * @see 'updateProposition' method for more informations about the
- * 'amount_type'
- *
- * @param {string} handle
- * @param {integer} reconcileModelId
- * @returns {Promise}
- */
- quickCreateProposition: function (handle, reconcileModelId) {
- var self = this;
- var line = this.getLine(handle);
- var reconcileModel = _.find(this.reconcileModels, function (r) {return r.id === reconcileModelId;});
- var fields = ['account_id', 'amount', 'amount_type', 'analytic_account_id', 'journal_id', 'label', 'force_tax_included', 'tax_ids', 'analytic_tag_ids', 'to_check', 'amount_from_label_regex', 'decimal_separator'];
- this._blurProposition(handle);
- var focus = this._formatQuickCreate(line, _.pick(reconcileModel, fields));
- focus.reconcileModelId = reconcileModelId;
- line.reconciliation_proposition.push(focus);
- var defs = [];
- if (reconcileModel.has_second_line) {
- defs.push(self._computeLine(line).then(function() {
- var second = {};
- _.each(fields, function (key) {
- second[key] = ("second_"+key) in reconcileModel ? reconcileModel["second_"+key] : reconcileModel[key];
- });
- var second_focus = self._formatQuickCreate(line, second);
- second_focus.reconcileModelId = reconcileModelId;
- line.reconciliation_proposition.push(second_focus);
- self._computeReconcileModels(handle, reconcileModelId);
- }))
- }
- return Promise.all(defs).then(function() {
- line.createForm = _.pick(focus, self.quickCreateFields);
- return self._computeLine(line);
- })
- },
- /**
- * Remove a proposition and switch to an active mode ('create' or 'match_rp' or 'match_other')
- * overridden in ManualModel
- *
- * @param {string} handle
- * @param {number} id (move line id)
- * @returns {Promise}
- */
- removeProposition: function (handle, id) {
- var self = this;
- var line = this.getLine(handle);
- var defs = [];
- // new limit = previous limit + 1, the one put back
- line.limit_override = (line.offset + 1) + this.limitMoveLines;
- var prop = _.find(line.reconciliation_proposition, {'id' : id});
- if (prop) {
- line.reconciliation_proposition = _.filter(line.reconciliation_proposition, function (p) {
- return p.id !== prop.id && p.id !== prop.link && p.link !== prop.id && (!p.link || p.link !== prop.link);
- });
- if (prop['reconcileModelId'] === undefined) {
- if (['receivable', 'payable', 'liquidity'].includes(prop.account_type)) {
- line.mv_lines_match_rp.unshift(prop);
- } else {
- line.mv_lines_match_other.unshift(prop);
- }
- }
-
- // No proposition left and then, reset the st_line partner.
- if(line.reconciliation_proposition.length == 0 && line.st_line.has_no_partner)
- defs.push(self.changePartner(line.handle));
- }
- line.mode = (id || line.mode !== "create") && isNaN(id) ? 'create' : 'match_rp';
- defs.push(this._computeLine(line));
- return Promise.all(defs).then(function() {
- return self.changeMode(handle, line.mode, true);
- })
- },
- getPartialReconcileAmount: function(handle, data) {
- var line = this.getLine(handle);
- var prop = _.find(line.reconciliation_proposition, {'id': data.data});
- if (prop) {
- var amount = prop.partial_amount || prop.amount;
- // Check if we can get a partial amount that would directly set balance to zero
- var partial = Math.abs(line.balance.amount + amount);
- if (Math.abs(line.balance.amount) >= Math.abs(amount)) {
- return Math.abs(amount);
- }
- if (partial <= Math.abs(prop.amount) && partial >= 0) {
- return partial;
- }
- return Math.abs(amount);
- }
- },
- /**
- * Force the partial reconciliation to display the reconciliate button.
- *
- * @param {string} handle
- * @returns {Promise}
- */
- partialReconcile: function(handle, data) {
- var line = this.getLine(handle);
- var prop = _.find(line.reconciliation_proposition, {'id' : data.mvLineId});
- if (prop) {
- var amount = data.amount;
- try {
- amount = field_utils.parse.float(data.amount);
- }
- catch (err) {
- amount = NaN;
- }
- // Amount can't be greater than line.amount and can not be negative and must be a number
- // the amount we receive will be a string, so take sign of previous line amount in consideration in order to put
- // the amount in the correct left or right column
- if (amount >= Math.abs(prop.amount) || amount <= 0 || isNaN(amount)) {
- delete prop.partial_amount_str;
- delete prop.partial_amount;
- if (isNaN(amount) || amount < 0) {
- this.do_warn(_.str.sprintf(_t('The amount %s is not a valid partial amount'), data.amount));
- }
- return this._computeLine(line);
- }
- else {
- var format_options = { currency_id: line.st_line.currency_id };
- prop.partial_amount = (prop.amount > 0 ? 1 : -1)*amount;
- prop.partial_amount_str = field_utils.format.monetary(Math.abs(prop.partial_amount), {}, format_options);
- }
- }
- return this._computeLine(line);
- },
- /**
- * Change the value of the editable proposition line or create a new one.
- *
- * If the editable line comes from a reconcile model with 2 lines
- * and their 'amount_type' is "percent"
- * and their total equals 100% (this doesn't take into account the taxes
- * who can be included or not)
- * Then the total is recomputed to have 100%.
- *
- * @param {string} handle
- * @param {*} values
- * @returns {Promise}
- */
- updateProposition: function (handle, values) {
- var self = this;
- var line = this.getLine(handle);
- var prop = _.last(_.filter(line.reconciliation_proposition, '__focus'));
- if ('to_check' in values && values.to_check === false) {
- // check if we have another line with to_check and if yes don't change value of this proposition
- prop.to_check = line.reconciliation_proposition.some(function(rec_prop, index) {
- return rec_prop.id !== prop.id && rec_prop.to_check;
- });
- }
- if (!prop) {
- prop = this._formatQuickCreate(line);
- line.reconciliation_proposition.push(prop);
- }
- _.each(values, function (value, fieldName) {
- if (fieldName === 'analytic_tag_ids') {
- switch (value.operation) {
- case "ADD_M2M":
- if (!_.findWhere(prop.analytic_tag_ids, {id: value.ids.id})) {
- prop.analytic_tag_ids.push(value.ids);
- }
- break;
- case "FORGET":
- var id = self.localData[value.ids[0]].ref;
- prop.analytic_tag_ids = _.filter(prop.analytic_tag_ids, function (val) {
- return val.id !== id;
- });
- break;
- }
- }
- else if (fieldName === 'tax_ids') {
- switch(value.operation) {
- case "ADD_M2M":
- prop.__tax_to_recompute = true;
- if (!_.findWhere(prop.tax_ids, {id: value.ids.id})) {
- value.ids.price_include = self.taxes[value.ids.id] ? self.taxes[value.ids.id].price_include : false;
- prop.tax_ids.push(value.ids);
- }
- break;
- case "FORGET":
- prop.__tax_to_recompute = true;
- var id = self.localData[value.ids[0]].ref;
- prop.tax_ids = _.filter(prop.tax_ids, function (val) {
- return val.id !== id;
- });
- break;
- }
- }
- else {
- prop[fieldName] = values[fieldName];
- }
- });
- if ('account_id' in values) {
- prop.account_code = prop.account_id ? this.accounts[prop.account_id.id] : '';
- }
- if ('amount' in values) {
- prop.base_amount = values.amount;
- if (prop.reconcileModelId) {
- this._computeReconcileModels(handle, prop.reconcileModelId);
- }
- }
- if ('force_tax_included' in values || 'amount' in values || 'account_id' in values) {
- prop.__tax_to_recompute = true;
- }
- line.createForm = _.pick(prop, this.quickCreateFields);
- // If you check/uncheck the force_tax_included box, reset the createForm amount.
- if(prop.base_amount)
- line.createForm.amount = prop.base_amount;
- if (prop.tax_ids.length !== 1 ) {
- // When we have 0 or more than 1 taxes, reset the base_amount and force_tax_included, otherwise weird behavior can happen
- prop.amount = prop.base_amount;
- line.createForm.force_tax_included = false;
- }
- return this._computeLine(line);
- },
- /**
- * Format the value and send it to 'account.reconciliation.widget' model
- * Update the number of validated lines
- * overridden in ManualModel
- *
- * @param {(string|string[])} handle
- * @returns {Promise<Object>} resolved with an object who contains
- * 'handles' key
- */
- validate: function (handle) {
- var self = this;
- this.display_context = 'validate';
- var handles = [];
- if (handle) {
- handles = [handle];
- } else {
- _.each(this.lines, function (line, handle) {
- if (!line.reconciled && line.balance && !line.balance.amount && line.reconciliation_proposition.length) {
- handles.push(handle);
- }
- });
- }
- var ids = [];
- var values = [];
- var handlesPromises = [];
- _.each(handles, function (handle) {
- var line = self.getLine(handle);
- var props = _.filter(line.reconciliation_proposition, function (prop) {return !prop.invalid;});
- var computeLinePromise;
- if (props.length === 0) {
- // Usability: if user has not chosen any lines and click validate, it has the same behavior
- // as creating a write-off of the same amount.
- props.push(self._formatQuickCreate(line, {
- account_id: [line.st_line.open_balance_account_id, self.accounts[line.st_line.open_balance_account_id]],
- }));
- // update balance of line otherwise it won't be to zero and another line will be added
- line.reconciliation_proposition.push(props[0]);
- computeLinePromise = self._computeLine(line);
- }
- ids.push(line.id);
- handlesPromises.push(Promise.resolve(computeLinePromise).then(function() {
- var values_dict = {
- "partner_id": line.st_line.partner_id,
- "counterpart_aml_dicts": _.map(_.filter(props, function (prop) {
- return !isNaN(prop.id) && !prop.already_paid;
- }), self._formatToProcessReconciliation.bind(self, line)),
- "payment_aml_ids": _.pluck(_.filter(props, function (prop) {
- return !isNaN(prop.id) && prop.already_paid;
- }), 'id'),
- "new_aml_dicts": _.map(_.filter(props, function (prop) {
- return isNaN(prop.id) && prop.display;
- }), self._formatToProcessReconciliation.bind(self, line)),
- "to_check": line.to_check,
- };
-
- // If the lines are not fully balanced, create an unreconciled amount.
- // line.st_line.currency_id is never false here because its equivalent to
- // statement_line.currency_id or statement_line.journal_id.currency_id or statement_line.journal_id.company_id.currency_id (Python-side).
- // see: get_statement_line_for_reconciliation_widget method in account/models/account_bank_statement.py for more details
- var currency = session.get_currency(line.st_line.currency_id);
- var balance = line.balance.amount;
- if (!utils.float_is_zero(balance, currency.digits[1])) {
- var unreconciled_amount_dict = {
- 'account_id': line.st_line.open_balance_account_id,
- 'credit': balance > 0 ? balance : 0,
- 'debit': balance < 0 ? -balance : 0,
- 'name': line.st_line.name + ' : ' + _t("Open balance"),
- };
- values_dict['new_aml_dicts'].push(unreconciled_amount_dict);
- }
- values.push(values_dict);
- line.reconciled = true;
- self.valuenow++;
- }));
-
- _.each(self.lines, function(other_line) {
- if (other_line != line) {
- var filtered_prop = other_line.reconciliation_proposition.filter(p => !line.reconciliation_proposition.map(l => l.id).includes(p.id));
- if (filtered_prop.length != other_line.reconciliation_proposition.length) {
- other_line.need_update = true;
- other_line.reconciliation_proposition = filtered_prop;
- }
- self._computeLine(line);
- }
- })
- });
-
- return Promise.all(handlesPromises).then(function() {
- return self._rpc({
- model: 'account.reconciliation.widget',
- method: 'process_bank_statement_line',
- args: [ids, values],
- context: self.context,
- })
- .then(self._validatePostProcess.bind(self))
- .then(function () {
- return {handles: handles};
- });
- });
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * add a line proposition after checking receivable and payable accounts constraint
- *
- * @private
- * @param {Object} line
- * @param {Object} prop
- */
- _addProposition: function (line, prop) {
- line.reconciliation_proposition.push(prop);
- },
- /**
- * stop the editable proposition line and remove it if it's invalid then
- * compute the line
- *
- * See :func:`_computeLine`
- *
- * @private
- * @param {string} handle
- * @returns {Promise}
- */
- _blurProposition: function (handle) {
- var line = this.getLine(handle);
- line.reconciliation_proposition = _.filter(line.reconciliation_proposition, function (l) {
- l.__focus = false;
- return !l.invalid;
- });
- },
- /**
- * When changing partner, read property_account_receivable and payable
- * of that partner because the counterpart account might cahnge depending
- * on the partner
- *
- * @private
- * @param {string} handle
- * @param {integer} partner_id
- * @returns {Promise}
- */
- _changePartner: function (handle, partner_id) {
- var self = this;
- return this._rpc({
- model: 'res.partner',
- method: 'read',
- args: [partner_id, ["property_account_receivable_id", "property_account_payable_id"]],
- }).then(function (result) {
- if (result.length > 0) {
- var line = self.getLine(handle);
- self.lines[handle].st_line.open_balance_account_id = line.balance.amount < 0 ? result[0]['property_account_payable_id'][0] : result[0]['property_account_receivable_id'][0];
- }
- });
- },
- /**
- * Calculates the balance; format each proposition amount_str and mark as
- * invalid the line with empty account_id, amount or label
- * Check the taxes server side for each updated propositions with tax_ids
- * extended by ManualModel
- *
- * @private
- * @param {Object} line
- * @returns {Promise}
- */
- _computeLine: function (line) {
- //balance_type
- var self = this;
-
- // compute taxes
- var tax_defs = [];
- var reconciliation_proposition = [];
- var formatOptions = {
- currency_id: line.st_line.currency_id,
- };
- line.to_check = false;
- _.each(line.reconciliation_proposition, function (prop) {
- if (prop.to_check) {
- // If one of the proposition is to_check, set the global to_check flag to true
- line.to_check = true;
- }
- if (prop.is_tax) {
- if (!_.find(line.reconciliation_proposition, {'id': prop.link}).__tax_to_recompute) {
- reconciliation_proposition.push(prop);
- }
- return;
- }
- if (!prop.already_paid && parseInt(prop.id)) {
- prop.is_move_line = true;
- }
- reconciliation_proposition.push(prop);
-
- if (prop.tax_ids && prop.tax_ids.length && prop.__tax_to_recompute && prop.base_amount) {
- reconciliation_proposition = _.filter(reconciliation_proposition, function (p) {
- return !p.is_tax || p.link !== prop.id;
- });
- var args = [prop.tax_ids.map(function(el){return el.id;}), prop.base_amount, formatOptions.currency_id];
- var add_context = {'round': true};
- if(prop.tax_ids.length === 1 && line.createForm.force_tax_included)
- add_context.force_price_include = true;
- tax_defs.push(self._rpc({
- model: 'account.tax',
- method: 'json_friendly_compute_all',
- args: args,
- context: $.extend({}, self.context || {}, add_context),
- })
- .then(function (result) {
- _.each(result.taxes, function(tax){
- var tax_prop = self._formatQuickCreate(line, {
- 'link': prop.id,
- 'tax_ids': [tax.id],
- 'amount': tax.amount,
- 'label': prop.label ? prop.label + " " + tax.name : tax.name,
- 'date': prop.date,
- 'account_id': tax.account_id ? [tax.account_id, null] : prop.account_id,
- 'analytic': tax.analytic,
- 'is_tax': true,
- '__focus': false
- });
-
- prop.tax_exigible = tax.tax_exigibility === 'on_payment' ? true : undefined;
- prop.amount = tax.base;
- prop.amount_str = field_utils.format.monetary(Math.abs(prop.amount), {}, formatOptions);
- prop.invalid = !self._isValid(prop);
-
- tax_prop.amount_str = field_utils.format.monetary(Math.abs(tax_prop.amount), {}, formatOptions);
- tax_prop.invalid = prop.invalid;
-
- reconciliation_proposition.push(tax_prop);
- });
- }));
- } else {
- prop.amount_str = field_utils.format.monetary(Math.abs(prop.amount), {}, formatOptions);
- prop.display = self._isDisplayedProposition(prop);
- prop.invalid = !self._isValid(prop);
- }
- });
-
- return Promise.all(tax_defs).then(function () {
- _.each(reconciliation_proposition, function (prop) {
- prop.__tax_to_recompute = false;
- });
- line.reconciliation_proposition = reconciliation_proposition;
-
- var amount_currency = 0;
- var total = line.st_line.amount || 0;
- var isOtherCurrencyId = _.uniq(_.pluck(_.reject(reconciliation_proposition, 'invalid'), 'currency_id'));
- isOtherCurrencyId = isOtherCurrencyId.length === 1 && !total && isOtherCurrencyId[0] !== formatOptions.currency_id ? isOtherCurrencyId[0] : false;
-
- _.each(reconciliation_proposition, function (prop) {
- if (!prop.invalid) {
- total -= prop.partial_amount || prop.amount;
- if (isOtherCurrencyId) {
- amount_currency -= (prop.amount < 0 ? -1 : 1) * Math.abs(prop.amount_currency);
- }
- }
- });
- var company_currency = session.get_currency(line.st_line.currency_id);
- var company_precision = company_currency && company_currency.digits[1] || 2;
- total = utils.round_decimals(total, company_precision) || 0;
- if(isOtherCurrencyId){
- var other_currency = session.get_currency(isOtherCurrencyId);
- var other_precision = other_currency && other_currency.digits[1] || 2;
- amount_currency = utils.round_decimals(amount_currency, other_precision);
- }
- line.balance = {
- amount: total,
- amount_str: field_utils.format.monetary(Math.abs(total), {}, formatOptions),
- currency_id: isOtherCurrencyId,
- amount_currency: isOtherCurrencyId ? amount_currency : total,
- amount_currency_str: isOtherCurrencyId ? field_utils.format.monetary(Math.abs(amount_currency), {}, {
- currency_id: isOtherCurrencyId
- }) : false,
- account_code: self.accounts[line.st_line.open_balance_account_id],
- };
- line.balance.show_balance = line.balance.amount_currency != 0;
- line.balance.type = line.balance.amount_currency ? (line.st_line.partner_id ? 0 : -1) : 1;
- });
- },
- /**
- *
- *
- * @private
- * @param {string} handle
- * @param {integer} reconcileModelId
- */
- _computeReconcileModels: function (handle, reconcileModelId) {
- var line = this.getLine(handle);
- // if quick create with 2 lines who use 100%, change the both values in same time
- var props = _.filter(line.reconciliation_proposition, {'reconcileModelId': reconcileModelId, '__focus': true});
- if (props.length === 2 && props[0].percent && props[1].percent) {
- if (props[0].percent + props[1].percent === 100) {
- props[0].base_amount = props[0].amount = line.st_line.amount - props[1].base_amount;
- props[0].__tax_to_recompute = true;
- }
- }
- },
- /**
- * format a name_get into an object {id, display_name}, idempotent
- *
- * @private
- * @param {Object|Array} [value] data or name_get
- */
- _formatNameGet: function (value) {
- return value ? (value.id ? value : {'id': value[0], 'display_name': value[1]}) : false;
- },
- _formatMany2ManyTags: function (value) {
- var res = [];
- for (var i=0, len=value.length; i<len; i++) {
- res[i] = {'id': value[i][0], 'display_name': value[i][1]};
- }
- return res;
- },
- _formatMany2ManyTagsTax: function(value) {
- var res = [];
- for (var i=0; i<value.length; i++) {
- res.push({id: value[i], display_name: this.taxes[value[i]] ? this.taxes[value[i]].display_name : ''});
- }
- return res;
- },
- /**
- * Format each propositions (amount, label, account_id)
- * extended in ManualModel
- *
- * @private
- * @param {Object} line
- * @param {Object[]} props
- */
- _formatLineProposition: function (line, props) {
- var self = this;
- if (props.length) {
- _.each(props, function (prop) {
- prop.amount = prop.debit || -prop.credit;
- prop.label = prop.name;
- prop.account_id = self._formatNameGet(prop.account_id || line.account_id);
- prop.is_partially_reconciled = prop.amount_str !== prop.total_amount_str;
- prop.to_check = !!prop.to_check;
- });
- }
- },
- /**
- * Format each server lines and propositions and compute all lines
- * overridden in ManualModel
- *
- * @see '_computeLine'
- *
- * @private
- * @param {Object[]} lines
- * @returns {Promise}
- */
- _formatLine: function (lines) {
- var self = this;
- var defs = [];
- _.each(lines, function (data) {
- var line = _.find(self.lines, function (l) {
- return l.id === data.st_line.id;
- });
- line.visible = true;
- line.limitMoveLines = self.limitMoveLines;
- _.extend(line, data);
- self._formatLineProposition(line, line.reconciliation_proposition);
- if (!line.reconciliation_proposition.length) {
- delete line.reconciliation_proposition;
- }
-
- // No partner set on st_line and all matching amls have the same one: set it on the st_line.
- defs.push(
- self._computeLine(line)
- .then(function(){
- if(!line.st_line.partner_id && line.reconciliation_proposition.length > 0){
- var hasDifferentPartners = function(prop){
- return !prop.partner_id || prop.partner_id != line.reconciliation_proposition[0].partner_id;
- };
-
- if(!_.any(line.reconciliation_proposition, hasDifferentPartners)){
- return self.changePartner(line.handle, {
- 'id': line.reconciliation_proposition[0].partner_id,
- 'display_name': line.reconciliation_proposition[0].partner_name,
- }, true);
- }
- }else if(!line.st_line.partner_id && line.partner_id && line.partner_name){
- return self.changePartner(line.handle, {
- 'id': line.partner_id,
- 'display_name': line.partner_name,
- }, true);
- }
- return true;
- })
- .then(function(){
- return data.write_off ? self.quickCreateProposition(line.handle, data.model_id) : true;
- })
- .then(function() {
- // If still no partner set, take the one from context, if it exists
- if (!line.st_line.partner_id && self.context.partner_id && self.context.partner_name) {
- return self.changePartner(line.handle, {
- 'id': self.context.partner_id,
- 'display_name': self.context.partner_name,
- }, true);
- }
- return true;
- })
- );
- });
- return Promise.all(defs);
- },
- /**
- * Format the server value then compute the line
- * overridden in ManualModel
- *
- * @see '_computeLine'
- *
- * @private
- * @param {string} handle
- * @param {Object[]} mv_lines
- * @returns {Promise}
- */
- _formatMoveLine: function (handle, mode, mv_lines) {
- var self = this;
- var line = this.getLine(handle);
- if (line.offset === 0 || line.limit_override) {
- line['mv_lines_'+mode] = mv_lines;
- delete line.limit_override;
- } else {
- line['mv_lines_'+mode] = line['mv_lines_'+mode].concat(mv_lines);
- }
- this._formatLineProposition(line, mv_lines);
-
- if ((line.mode == 'match_other' || line.mode == "match_rp") && !line['mv_lines_'+mode].length && !line['filter_'+mode].length) {
- line.mode = self._getDefaultMode(handle);
- if (line.mode !== 'match_rp' && line.mode !== 'match_other' && line.mode !== 'inactive') {
- return this._computeLine(line).then(function () {
- return self.createProposition(handle);
- });
- }
- } else {
- return this._computeLine(line);
- }
- },
- /**
- * overridden in ManualModel
- */
- _getDefaultMode: function(handle) {
- var line = this.getLine(handle);
- if (line.balance.amount === 0
- && (!line.st_line.mv_lines_match_rp || line.st_line.mv_lines_match_rp.length === 0)
- && (!line.st_line.mv_lines_match_other || line.st_line.mv_lines_match_other.length === 0)) {
- return 'inactive';
- }
- if (line.mv_lines_match_rp && line.mv_lines_match_rp.length) {
- return 'match_rp';
- }
- if (line.mv_lines_match_other && line.mv_lines_match_other.length) {
- return 'match_other';
- }
- return 'create';
- },
- _getAvailableModes: function(handle) {
- var line = this.getLine(handle);
- var modes = []
- if (line.mv_lines_match_rp && line.mv_lines_match_rp.length) {
- modes.push('match_rp')
- }
- if (line.mv_lines_match_other && line.mv_lines_match_other.length) {
- modes.push('match_other')
- }
- modes.push('create')
- return modes
- },
- /**
- * Apply default values for the proposition, format datas and format the
- * base_amount with the decimal number from the currency
- * extended in ManualModel
- *
- * @private
- * @param {Object} line
- * @param {Object} values
- * @returns {Object}
- */
- _formatQuickCreate: function (line, values) {
- values = values || {};
- var today = new moment().utc().format();
- var account = this._formatNameGet(values.account_id);
- var formatOptions = {
- currency_id: line.st_line.currency_id,
- };
- var amount;
- switch(values.amount_type) {
- case 'percentage':
- amount = line.balance.amount * values.amount / 100;
- break;
- case 'regex':
- var matching = line.st_line.name.match(new RegExp(values.amount_from_label_regex))
- amount = 0;
- if (matching && matching.length == 2) {
- matching = matching[1].replace(new RegExp('\\D' + values.decimal_separator, 'g'), '');
- matching = matching.replace(values.decimal_separator, '.');
- amount = parseFloat(matching) || 0;
- amount = line.balance.amount > 0 ? amount : -amount;
- }
- break;
- case 'fixed':
- amount = values.amount;
- break;
- default:
- amount = values.amount !== undefined ? values.amount : line.balance.amount;
- }
-
-
- var prop = {
- 'id': _.uniqueId('createLine'),
- 'label': values.label || line.st_line.name,
- 'account_id': account,
- 'account_code': account ? this.accounts[account.id] : '',
- 'analytic_account_id': this._formatNameGet(values.analytic_account_id),
- 'analytic_tag_ids': this._formatMany2ManyTags(values.analytic_tag_ids || []),
- 'journal_id': this._formatNameGet(values.journal_id),
- 'tax_ids': this._formatMany2ManyTagsTax(values.tax_ids || []),
- 'debit': 0,
- 'credit': 0,
- 'date': values.date ? values.date : field_utils.parse.date(today, {}, {isUTC: true}),
- 'force_tax_included': values.force_tax_included || false,
- 'base_amount': amount,
- 'percent': values.amount_type === "percentage" ? values.amount : null,
- 'link': values.link,
- 'display': true,
- 'invalid': true,
- 'to_check': !!values.to_check,
- '__tax_to_recompute': true,
- 'is_tax': values.is_tax,
- '__focus': '__focus' in values ? values.__focus : true,
- };
- if (prop.base_amount) {
- // Call to format and parse needed to round the value to the currency precision
- var sign = prop.base_amount < 0 ? -1 : 1;
- var amount = field_utils.format.monetary(Math.abs(prop.base_amount), {}, formatOptions);
- prop.base_amount = sign * field_utils.parse.monetary(amount, {}, formatOptions);
- }
-
- prop.amount = prop.base_amount;
- return prop;
- },
- /**
- * Return list of account_move_line that has been selected and needs to be removed
- * from other calls.
- *
- * @private
- * @returns {Array} list of excluded ids
- */
- _getExcludedIds: function () {
- var excludedIds = [];
- _.each(this.lines, function(line) {
- if (line.reconciliation_proposition) {
- _.each(line.reconciliation_proposition, function(prop) {
- if (parseInt(prop['id'])) {
- excludedIds.push(prop['id']);
- }
- });
- }
- });
- return excludedIds;
- },
- /**
- * Defined whether the line is to be displayed or not. Here, we only display
- * the line if it comes from the server or if an account is defined when it
- * is created
- * extended in ManualModel
- *
- * @private
- * @param {object} prop
- * @returns {Boolean}
- */
- _isDisplayedProposition: function (prop) {
- return !isNaN(prop.id) || !!prop.account_id;
- },
- /**
- * extended in ManualModel
- * @private
- * @param {object} prop
- * @returns {Boolean}
- */
- _isValid: function (prop) {
- return !isNaN(prop.id) || prop.account_id && prop.amount && prop.label && !!prop.label.length;
- },
- /**
- * Fetch 'account.reconciliation.widget' propositions.
- * overridden in ManualModel
- *
- * @see '_formatMoveLine'
- *
- * @private
- * @param {string} handle
- * @returns {Promise}
- */
- _performMoveLine: function (handle, mode) {
- var line = this.getLine(handle);
- var excluded_ids = _.map(line.reconciliation_proposition, function (prop) {
- return _.isNumber(prop.id) ? prop.id : null;
- }).filter(id => id != null);
- var filter = line['filter_'+mode] || "";
- var limit = this.limitMoveLines;
- var offset = line.offset;
- if (line.limit_override) {
- // If we have a limit_override, it means we are either adding/removing
- // a line from the matching table, hence keep same number of displayed
- // proposition below by setting offset to 0 and limit to the current
- // number of proposition loaded
- offset = 0;
- limit = line.limit_override;
- }
- return this._rpc({
- model: 'account.reconciliation.widget',
- method: 'get_move_lines_for_bank_statement_line',
- args: [line.id, line.st_line.partner_id, excluded_ids, filter, offset, limit, mode === 'match_rp' ? 'rp' : 'other'],
- context: this.context,
- })
- .then(this._formatMoveLine.bind(this, handle, mode));
- },
- /**
- * format the proposition to send information server side
- * extended in ManualModel
- *
- * @private
- * @param {object} line
- * @param {object} prop
- * @returns {object}
- */
- _formatToProcessReconciliation: function (line, prop) {
- var amount = -prop.amount;
- if (prop.partial_amount) {
- amount = -prop.partial_amount;
- }
-
- var result = {
- name : prop.label,
- debit : amount > 0 ? amount : 0,
- credit : amount < 0 ? -amount : 0,
- tax_exigible: prop.tax_exigible,
- analytic_tag_ids: [[6, null, _.pluck(prop.analytic_tag_ids, 'id')]]
- };
- if (!isNaN(prop.id)) {
- result.counterpart_aml_id = prop.id;
- } else {
- result.account_id = prop.account_id.id;
- if (prop.journal_id) {
- result.journal_id = prop.journal_id.id;
- }
- }
- if (!isNaN(prop.id)) result.counterpart_aml_id = prop.id;
- if (prop.analytic_account_id) result.analytic_account_id = prop.analytic_account_id.id;
- if (prop.tax_ids && prop.tax_ids.length && !prop.is_tax) result.tax_ids = [[6, null, _.pluck(prop.tax_ids, 'id')]];
- if (prop.tax_ids && prop.tax_ids.length && prop.is_tax) result.tax_line_id = prop.tax_ids[0].id;
- if (prop.reconcileModelId) result.reconcile_model_id = prop.reconcileModelId
- return result;
- },
- /**
- * Hook to handle return values of the validate's line process.
- *
- * @private
- * @param {Object} data
- * @param {Object[]} data.moves list of processed account.move
- * @returns {Deferred}
- */
- _validatePostProcess: function (data) {
- var self = this;
- return Promise.resolve();
- },
-});
-
-
-/**
- * Model use to fetch, format and update 'account.move.line' and 'res.partner'
- * datas allowing manual reconciliation
- */
-var ManualModel = StatementModel.extend({
- quickCreateFields: ['account_id', 'journal_id', 'amount', 'analytic_account_id', 'label', 'tax_ids', 'force_tax_included', 'analytic_tag_ids', 'date', 'to_check'],
-
- modes: ['create', 'match'],
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Return a boolean telling if load button needs to be displayed or not
- *
- * @returns {boolean} true if load more button needs to be displayed
- */
- hasMoreLines: function () {
- if (this.manualLines.length > this.pagerIndex) {
- return true;
- }
- return false;
- },
- /**
- * load data from
- * - 'account.reconciliation.widget' fetch the lines to reconciliate
- * - 'account.account' fetch all account code
- *
- * @param {Object} context
- * @param {string} [context.mode] 'customers', 'suppliers' or 'accounts'
- * @param {integer[]} [context.company_ids]
- * @param {integer[]} [context.partner_ids] used for 'customers' and
- * 'suppliers' mode
- * @returns {Promise}
- */
- load: function (context) {
- var self = this;
- this.context = context;
-
- var domain_account_id = [];
- if (context && context.company_ids) {
- domain_account_id.push(['company_id', 'in', context.company_ids]);
- }
-
- var def_account = this._rpc({
- model: 'account.account',
- method: 'search_read',
- domain: domain_account_id,
- fields: ['code'],
- })
- .then(function (accounts) {
- self.account_ids = _.pluck(accounts, 'id');
- self.accounts = _.object(self.account_ids, _.pluck(accounts, 'code'));
- });
-
- var domainReconcile = [];
- var company_ids = context && context.company_ids || [session.company_id];
- if (company_ids) {
- domainReconcile.push(['company_id', 'in', company_ids]);
- }
- var def_reconcileModel = this._loadReconciliationModel({domainReconcile: domainReconcile});
- var def_taxes = this._loadTaxes();
-
- return Promise.all([def_reconcileModel, def_account, def_taxes]).then(function () {
- switch(context.mode) {
- case 'customers':
- case 'suppliers':
- var mode = context.mode === 'customers' ? 'receivable' : 'payable';
- var args = ['partner', context.partner_ids || null, mode];
- return self._rpc({
- model: 'account.reconciliation.widget',
- method: 'get_data_for_manual_reconciliation',
- args: args,
- context: context,
- })
- .then(function (result) {
- self.manualLines = result;
- self.valuenow = 0;
- self.valuemax = Object.keys(self.manualLines).length;
- var lines = self.manualLines.splice(0, self.defaultDisplayQty);
- self.pagerIndex = lines.length;
- return self.loadData(lines);
- });
- case 'accounts':
- return self._rpc({
- model: 'account.reconciliation.widget',
- method: 'get_data_for_manual_reconciliation',
- args: ['account', context.account_ids || self.account_ids],
- context: context,
- })
- .then(function (result) {
- self.manualLines = result;
- self.valuenow = 0;
- self.valuemax = Object.keys(self.manualLines).length;
- var lines = self.manualLines.splice(0, self.defaultDisplayQty);
- self.pagerIndex = lines.length;
- return self.loadData(lines);
- });
- default:
- var partner_ids = context.partner_ids || null;
- var account_ids = context.account_ids || self.account_ids || null;
- return self._rpc({
- model: 'account.reconciliation.widget',
- method: 'get_all_data_for_manual_reconciliation',
- args: [partner_ids, account_ids],
- context: context,
- })
- .then(function (result) {
- // Flatten the result
- self.manualLines = [].concat(result.accounts, result.customers, result.suppliers);
- self.valuenow = 0;
- self.valuemax = Object.keys(self.manualLines).length;
- var lines = self.manualLines.splice(0, self.defaultDisplayQty);
- self.pagerIndex = lines.length;
- return self.loadData(lines);
- });
- }
- });
- },
- /**
- * Load more partners/accounts
- * overridden in ManualModel
- *
- * @param {integer} qty quantity to load
- * @returns {Promise}
- */
- loadMore: function(qty) {
- if (qty === undefined) {
- qty = this.defaultDisplayQty;
- }
- var lines = this.manualLines.splice(this.pagerIndex, qty);
- this.pagerIndex += qty;
- return this.loadData(lines);
- },
- /**
- * Method to load informations on lines
- *
- * @param {Array} lines manualLines to load
- * @returns {Promise}
- */
- loadData: function(lines) {
- var self = this;
- var defs = [];
- _.each(lines, function (l) {
- defs.push(self._formatLine(l.mode, l));
- });
- return Promise.all(defs);
-
- },
- /**
- * Mark the account or the partner as reconciled
- *
- * @param {(string|string[])} handle
- * @returns {Promise<Array>} resolved with the handle array
- */
- validate: function (handle) {
- var self = this;
- var handles = [];
- if (handle) {
- handles = [handle];
- } else {
- _.each(this.lines, function (line, handle) {
- if (!line.reconciled && !line.balance.amount && line.reconciliation_proposition.length) {
- handles.push(handle);
- }
- });
- }
-
- var def = Promise.resolve();
- var process_reconciliations = [];
- var reconciled = [];
- _.each(handles, function (handle) {
- var line = self.getLine(handle);
- if(line.reconciled) {
- return;
- }
- var props = line.reconciliation_proposition;
- if (!props.length) {
- self.valuenow++;
- reconciled.push(handle);
- line.reconciled = true;
- process_reconciliations.push({
- id: line.type === 'accounts' ? line.account_id : line.partner_id,
- type: line.type,
- mv_line_ids: [],
- new_mv_line_dicts: [],
- });
- } 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) && prop.display;}), self._formatToProcessReconciliation.bind(self, line));
- process_reconciliations.push({
- id: null,
- type: null,
- mv_line_ids: mv_line_ids,
- new_mv_line_dicts: new_mv_line_dicts
- });
- }
- line.reconciliation_proposition = [];
- });
- if (process_reconciliations.length) {
- def = self._rpc({
- model: 'account.reconciliation.widget',
- method: 'process_move_lines',
- args: [process_reconciliations],
- });
- }
-
- return def.then(function() {
- var defs = [];
- var account_ids = [];
- var partner_ids = [];
- _.each(handles, function (handle) {
- var line = self.getLine(handle);
- if (line.reconciled) {
- return;
- }
- line.filter = "";
- line.offset = 0;
- defs.push(self._performMoveLine(handle, 'match').then(function () {
- if(!line.mv_lines_match.length) {
- self.valuenow++;
- reconciled.push(handle);
- line.reconciled = true;
- if (line.type === 'accounts') {
- account_ids.push(line.account_id.id);
- } else {
- partner_ids.push(line.partner_id.id);
- }
- }
- }));
- });
- return Promise.all(defs).then(function () {
- if (partner_ids.length) {
- self._rpc({
- model: 'res.partner',
- method: 'mark_as_reconciled',
- args: [partner_ids],
- });
- }
- return {reconciled: reconciled, updated: _.difference(handles, reconciled)};
- });
- });
- },
- removeProposition: function (handle, id) {
- var self = this;
- var line = this.getLine(handle);
- var defs = [];
- // new limit = previous limit + 1, the one put back
- var prop = _.find(line.reconciliation_proposition, {'id' : id});
- if (prop) {
- line.reconciliation_proposition = _.filter(line.reconciliation_proposition, function (p) {
- return p.id !== prop.id && p.id !== prop.link && p.link !== prop.id && (!p.link || p.link !== prop.link);
- });
- line.mv_lines_match.unshift(prop);
-
- // No proposition left and then, reset the st_line partner.
- if(line.reconciliation_proposition.length == 0 && line.st_line.has_no_partner)
- defs.push(self.changePartner(line.handle));
- }
- line.mode = (id || line.mode !== "create") && isNaN(id) ? 'create' : 'match';
- defs.push(this._computeLine(line));
- return Promise.all(defs).then(function() {
- return self.changeMode(handle, line.mode, true);
- })
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * override change the balance type to display or not the reconcile button
- *
- * @override
- * @private
- * @param {Object} line
- * @returns {Promise}
- */
- _computeLine: function (line) {
- return this._super(line).then(function () {
- var props = _.reject(line.reconciliation_proposition, 'invalid');
- _.each(line.reconciliation_proposition, function(p) {
- delete p.is_move_line;
- });
- line.balance.type = -1;
- if (!line.balance.amount_currency && props.length) {
- line.balance.type = 1;
- } else if(_.any(props, function (prop) {return prop.amount > 0;}) &&
- _.any(props, function (prop) {return prop.amount < 0;})) {
- line.balance.type = 0;
- }
- });
- },
- /**
- * Format each server lines and propositions and compute all lines
- *
- * @see '_computeLine'
- *
- * @private
- * @param {'customers' | 'suppliers' | 'accounts'} type
- * @param {Object} data
- * @returns {Promise}
- */
- _formatLine: function (type, data) {
- var line = this.lines[_.uniqueId('rline')] = _.extend(data, {
- type: type,
- reconciled: false,
- mode: 'inactive',
- offset: 0,
- limitMoveLines: this.limitMoveLines,
- filter: "",
- reconcileModels: this.reconcileModels,
- account_id: this._formatNameGet([data.account_id, data.account_name]),
- st_line: data,
- visible: true
- });
- this._formatLineProposition(line, line.reconciliation_proposition);
- if (!line.reconciliation_proposition.length) {
- delete line.reconciliation_proposition;
- }
- return this._computeLine(line);
- },
- /**
- * override to add journal_id
- *
- * @override
- * @private
- * @param {Object} line
- * @param {Object} props
- */
- _formatLineProposition: function (line, props) {
- var self = this;
- this._super(line, props);
- if (props.length) {
- _.each(props, function (prop) {
- var tmp_value = prop.debit || prop.credit;
- prop.credit = prop.credit !== 0 ? 0 : tmp_value;
- prop.debit = prop.debit !== 0 ? 0 : tmp_value;
- prop.amount = -prop.amount;
- prop.journal_id = self._formatNameGet(prop.journal_id || line.journal_id);
- prop.to_check = !!prop.to_check;
- });
- }
- },
- /**
- * override to add journal_id on tax_created_line
- *
- * @private
- * @param {Object} line
- * @param {Object} values
- * @returns {Object}
- */
- _formatQuickCreate: function (line, values) {
- // Add journal to created line
- if (values && values.journal_id === undefined && line && line.createForm && line.createForm.journal_id) {
- values.journal_id = line.createForm.journal_id;
- }
- return this._super(line, values);
- },
- /**
- * @override
- * @param {object} prop
- * @returns {Boolean}
- */
- _isDisplayedProposition: function (prop) {
- return !!prop.journal_id && this._super(prop);
- },
- /**
- * @override
- * @param {object} prop
- * @returns {Boolean}
- */
- _isValid: function (prop) {
- return prop.journal_id && this._super(prop);
- },
- /**
- * Fetch 'account.move.line' propositions.
- *
- * @see '_formatMoveLine'
- *
- * @override
- * @private
- * @param {string} handle
- * @returns {Promise}
- */
- _performMoveLine: function (handle, mode) {
- var line = this.getLine(handle);
- var excluded_ids = _.map(line.reconciliation_proposition, function (prop) {
- return _.isNumber(prop.id) ? prop.id : null;
- }).filter(id => id != null);
- var filter = line.filter || "";
- var limit = this.limitMoveLines;
- var offset = line.offset;
- if (line.limit_override) {
- limit = line.limit_override;
- offset = 0;
- }
- var args = [line.account_id.id, line.partner_id, excluded_ids, filter, offset, limit];
- return this._rpc({
- model: 'account.reconciliation.widget',
- method: 'get_move_lines_for_manual_reconciliation',
- args: args,
- context: this.context,
- })
- .then(this._formatMoveLine.bind(this, handle, ''));
- },
-
- _formatToProcessReconciliation: function (line, prop) {
- var result = this._super(line, prop);
- result['date'] = prop.date;
- return result;
- },
- _getDefaultMode: function(handle) {
- var line = this.getLine(handle);
- if (line.balance.amount === 0 && (!line.st_line.mv_lines_match || line.st_line.mv_lines_match.length === 0)) {
- return 'inactive';
- }
- return line.mv_lines_match.length > 0 ? 'match' : 'create';
- },
- _formatMoveLine: function (handle, mode, mv_lines) {
- var self = this;
- var line = this.getLine(handle);
- if (line.offset === 0 || line.limit_override) {
- line.mv_lines_match = mv_lines;
- delete line.limit_override;
- } else {
- line.mv_lines_match = line.mv_lines_match.concat(mv_lines);
- }
- this._formatLineProposition(line, mv_lines);
-
- if (line.mode !== 'create' && !line.mv_lines_match.length && !line.filter.length) {
- line.mode = this.avoidCreate || !line.balance.amount ? 'inactive' : 'create';
- if (line.mode === 'create') {
- return this._computeLine(line).then(function () {
- return self.createProposition(handle);
- });
- }
- } else {
- return this._computeLine(line);
- }
- },
-});
-
-return {
- StatementModel: StatementModel,
- ManualModel: ManualModel,
-};
-});
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
deleted file mode 100644
index d8291ddbef7..00000000000
--- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
+++ /dev/null
@@ -1,924 +0,0 @@
-odoo.define('account.ReconciliationRenderer', function (require) {
-"use strict";
-
-var Widget = require('web.Widget');
-var FieldManagerMixin = require('web.FieldManagerMixin');
-var relational_fields = require('web.relational_fields');
-var basic_fields = require('web.basic_fields');
-var core = require('web.core');
-var time = require('web.time');
-var session = require('web.session');
-var qweb = core.qweb;
-var _t = core._t;
-
-
-/**
- * rendering of the bank statement action contains progress bar, title and
- * auto reconciliation button
- */
-var StatementRenderer = Widget.extend(FieldManagerMixin, {
- template: 'reconciliation.statement',
- events: {
- 'click *[rel="do_action"]': '_onDoAction',
- 'click button.js_load_more': '_onLoadMore',
- },
- /**
- * @override
- */
- init: function (parent, model, state) {
- this._super(parent);
- this.model = model;
- this._initialState = state;
- },
- /**
- * display iniial state and create the name statement field
- *
- * @override
- */
- start: function () {
- var self = this;
- var defs = [this._super.apply(this, arguments)];
- this.time = Date.now();
- this.$progress = $('');
-
- return Promise.all(defs);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
- /*
- * hide the button to load more statement line
- */
- hideLoadMoreButton: function (show) {
- if (!show) {
- this.$('.js_load_more').show();
- }
- else {
- this.$('.js_load_more').hide();
- }
- },
- showRainbowMan: function (state) {
- if (this.model.display_context !== 'validate') {
- return
- }
- var dt = Date.now()-this.time;
- var $done = $(qweb.render("reconciliation.done", {
- 'duration': moment(dt).utc().format(time.getLangTimeFormat()),
- 'number': state.valuenow,
- 'timePerTransaction': Math.round(dt/1000/state.valuemax),
- 'context': state.context,
- }));
- $done.find('*').addClass('o_reward_subcontent');
- $done.find('.button_close_statement').click(this._onCloseBankStatement.bind(this));
- $done.find('.button_back_to_statement').click(this._onGoToBankStatement.bind(this));
- // display rainbowman after full reconciliation
- if (session.show_effect) {
- this.trigger_up('show_effect', {
- type: 'rainbow_man',
- fadeout: 'no',
- message: $done,
- });
- this.$el.css('min-height', '450px');
- } else {
- $done.appendTo(this.$el);
- }
- },
- /**
- * update the statement rendering
- *
- * @param {object} state - statement data
- * @param {integer} state.valuenow - for the progress bar
- * @param {integer} state.valuemax - for the progress bar
- * @param {string} state.title - for the progress bar
- * @param {[object]} [state.notifications]
- */
- update: function (state) {
- var self = this;
- this._updateProgressBar(state);
-
- if (state.valuenow === state.valuemax && !this.$('.done_message').length) {
- this.showRainbowMan(state);
- }
-
- if (state.notifications) {
- this._renderNotifications(state.notifications);
- }
- },
- _updateProgressBar: function(state) {
- this.$progress.find('.valuenow').text(state.valuenow);
- this.$progress.find('.valuemax').text(state.valuemax);
- this.$progress.find('.progress-bar')
- .attr('aria-valuenow', state.valuenow)
- .attr('aria-valuemax', state.valuemax)
- .css('width', (state.valuenow/state.valuemax*100) + '%');
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
- /**
- * render the notifications
- *
- * @param {[object]} notifications
- */
- _renderNotifications: function(notifications) {
- this.$(".notification_area").empty();
- for (var i=0; i<notifications.length; i++) {
- var $notification = $(qweb.render("reconciliation.notification", notifications[i])).hide();
- $notification.appendTo(this.$(".notification_area")).slideDown(300);
- }
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @private
- * Click on close bank statement button, this will
- * close and then open form view of bank statement
- * @param {MouseEvent} event
- */
- _onCloseBankStatement: function (e) {
- this.trigger_up('close_statement');
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _onDoAction: function(e) {
- e.preventDefault();
- var name = e.currentTarget.dataset.action_name;
- var model = e.currentTarget.dataset.model;
- if (e.currentTarget.dataset.ids) {
- var ids = e.currentTarget.dataset.ids.split(",").map(Number);
- var domain = [['id', 'in', ids]];
- } else {
- var domain = e.currentTarget.dataset.domain;
- }
- var context = e.currentTarget.dataset.context;
- var tag = e.currentTarget.dataset.tag;
- if (tag) {
- this.do_action({
- type: 'ir.actions.client',
- tag: tag,
- context: context,
- })
- } else {
- this.do_action({
- name: name,
- res_model: model,
- domain: domain,
- context: context,
- views: [[false, 'list'], [false, 'form']],
- type: 'ir.actions.act_window',
- view_mode: "list"
- });
- }
- },
- /**
- * Open the list view for account.bank.statement model
- * @private
- * @param {MouseEvent} event
- */
- _onGoToBankStatement: function (e) {
- var journalId = $(e.target).attr('data_journal_id');
- if (journalId) {
- journalId = parseInt(journalId);
- }
- $('.o_reward').remove();
- this.do_action({
- name: 'Bank Statements',
- res_model: 'account.bank.statement',
- views: [[false, 'list'], [false, 'form']],
- type: 'ir.actions.act_window',
- context: {search_default_journal_id: journalId, 'journal_type':'bank'},
- view_mode: 'form',
- });
- },
- /**
- * Load more statement lines for reconciliation
- * @private
- * @param {MouseEvent} event
- */
- _onLoadMore: function (e) {
- this.trigger_up('load_more');
- },
-});
-
-
-/**
- * rendering of the bank statement line, contains line data, proposition and
- * view for 'match' and 'create' mode
- */
-var LineRenderer = Widget.extend(FieldManagerMixin, {
- template: "reconciliation.line",
- events: {
- 'click .accounting_view caption .o_buttons button': '_onValidate',
- 'click .accounting_view tfoot': '_onChangeTab',
- 'click': '_onTogglePanel',
- 'click .o_field_widget': '_onStopPropagation',
- 'keydown .o_input, .edit_amount_input': '_onStopPropagation',
- 'click .o_notebook li a': '_onChangeTab',
- 'click .cell': '_onEditAmount',
- 'change input.filter': '_onFilterChange',
- 'click .match .load-more a': '_onLoadMore',
- 'click .match .mv_line td': '_onSelectMoveLine',
- 'click .accounting_view tbody .mv_line td': '_onSelectProposition',
- 'click .o_reconcile_models button': '_onQuickCreateProposition',
- 'click .create .add_line': '_onCreateProposition',
- 'click .reconcile_model_create': '_onCreateReconcileModel',
- 'click .reconcile_model_edit': '_onEditReconcileModel',
- 'keyup input': '_onInputKeyup',
- 'blur input': '_onInputKeyup',
- 'keydown': '_onKeydown',
- },
- custom_events: _.extend({}, FieldManagerMixin.custom_events, {
- 'field_changed': '_onFieldChanged',
- }),
- _avoidFieldUpdate: {},
- MV_LINE_DEBOUNCE: 200,
-
- _onKeydown: function (ev) {
- switch (ev.which) {
- case $.ui.keyCode.ENTER:
- this.trigger_up('navigation_move', {direction: 'validate', handle: this.handle});
- break;
- case $.ui.keyCode.UP:
- ev.stopPropagation();
- ev.preventDefault();
- this.trigger_up('navigation_move', {direction: 'up', handle: this.handle});
- break;
- case $.ui.keyCode.DOWN:
- ev.stopPropagation();
- ev.preventDefault();
- this.trigger_up('navigation_move', {direction: 'down', handle: this.handle});
- break;
- }
- },
-
- /**
- * create partner_id field in editable mode
- *
- * @override
- */
- init: function (parent, model, state) {
- this._super(parent);
- FieldManagerMixin.init.call(this);
-
- this.model = model;
- this._initialState = state;
- if (this.MV_LINE_DEBOUNCE) {
- this._onSelectMoveLine = _.debounce(this._onSelectMoveLine, this.MV_LINE_DEBOUNCE, true);
- } else {
- this._onSelectMoveLine = this._onSelectMoveLine;
- }
- },
- /**
- * @override
- */
- start: function () {
- var self = this;
- var def1 = this._makePartnerRecord(this._initialState.st_line.partner_id, this._initialState.st_line.partner_name).then(function (recordID) {
- self.fields = {
- partner_id : new relational_fields.FieldMany2One(self,
- 'partner_id',
- self.model.get(recordID), {
- mode: 'edit',
- attrs: {
- placeholder: self._initialState.st_line.communication_partner_name || _t('Select Partner'),
- }
- }
- )
- };
- 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'))
- .attr("data-content", qweb.render('reconciliation.line.statement_line.details', {'state': this._initialState}));
- this.$el.popover({
- 'selector': '.line_info_button',
- 'placement': 'left',
- 'container': this.$el,
- 'html': true,
- // disable bootstrap sanitizer because we use a table that has been
- // rendered using qweb.render so it is safe and also because sanitizer escape table by default.
- 'sanitize': false,
- 'trigger': 'hover',
- 'animation': false,
- 'toggle': 'popover'
- });
- var def2 = this._super.apply(this, arguments);
- return Promise.all([def1, def2]);
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * update the statement line rendering
- *
- * @param {object} state - statement line
- */
- update: function (state) {
- var self = this;
- // isValid
- var to_check_checked = !!(state.to_check);
- this.$('caption .o_buttons button.o_validate').toggleClass('d-none', !!state.balance.type && !to_check_checked);
- this.$('caption .o_buttons button.o_reconcile').toggleClass('d-none', state.balance.type <= 0 || to_check_checked);
- this.$('caption .o_buttons .o_no_valid').toggleClass('d-none', state.balance.type >= 0);
- self.$('caption .o_buttons button.o_validate').toggleClass('text-warning', to_check_checked);
-
- // partner_id
- this._makePartnerRecord(state.st_line.partner_id, state.st_line.partner_name).then(function (recordID) {
- self.fields.partner_id.reset(self.model.get(recordID));
- self.$el.attr('data-partner', state.st_line.partner_id);
- });
-
- // mode
- this.$el.data('mode', state.mode).attr('data-mode', state.mode);
- this.$('.o_notebook li a').attr('aria-selected', false);
- this.$('.o_notebook li a').removeClass('active');
- this.$('.o_notebook .tab-content .tab-pane').removeClass('active');
- this.$('.o_notebook li a[href*="notebook_page_' + state.mode + '"]').attr('aria-selected', true);
- this.$('.o_notebook li a[href*="notebook_page_' + state.mode + '"]').addClass('active');
- this.$('.o_notebook .tab-content .tab-pane[id*="notebook_page_' + state.mode + '"]').addClass('active');
- this.$('.create, .match').each(function () {
- $(this).removeAttr('style');
- });
-
- // reconciliation_proposition
- var $props = this.$('.accounting_view tbody').empty();
-
- // Search propositions that could be a partial credit/debit.
- var props = [];
- var balance = state.balance.amount_currency;
- _.each(state.reconciliation_proposition, function (prop) {
- if (prop.display) {
- props.push(prop);
- }
- });
-
- _.each(props, function (line) {
- var $line = $(qweb.render("reconciliation.line.mv_line", {'line': line, 'state': state, 'proposition': true}));
- if (!isNaN(line.id)) {
- $('<span class="line_info_button fa fa-info-circle"/>')
- .appendTo($line.find('.cell_info_popover'))
- .attr("data-content", qweb.render('reconciliation.line.mv_line.details', {'line': line}));
- }
- $props.append($line);
- });
-
- // mv_lines
- var matching_modes = self.model.modes.filter(x => x.startsWith('match'));
- for (let i = 0; i < matching_modes.length; i++) {
- var stateMvLines = state['mv_lines_'+matching_modes[i]] || [];
- var recs_count = stateMvLines.length > 0 ? stateMvLines[0].recs_count : 0;
- var remaining = recs_count - stateMvLines.length;
- var $mv_lines = this.$('div[id*="notebook_page_' + matching_modes[i] + '"] .match table tbody').empty();
- this.$('.o_notebook li a[href*="notebook_page_' + matching_modes[i] + '"]').parent().toggleClass('d-none', stateMvLines.length === 0 && !state['filter_'+matching_modes[i]]);
-
- _.each(stateMvLines, 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"/>')
- .appendTo($line.find('.cell_info_popover'))
- .attr("data-content", qweb.render('reconciliation.line.mv_line.details', {'line': line}));
- }
- $mv_lines.append($line);
- });
- this.$('div[id*="notebook_page_' + matching_modes[i] + '"] .match div.load-more').toggle(remaining > 0);
- this.$('div[id*="notebook_page_' + matching_modes[i] + '"] .match div.load-more span').text(remaining);
- }
-
- // balance
- this.$('.popover').remove();
- this.$('table tfoot').html(qweb.render("reconciliation.line.balance", {'state': state}));
-
- // create form
- if (state.createForm) {
- var createPromise;
- if (!this.fields.account_id) {
- createPromise = this._renderCreate(state);
- }
- Promise.resolve(createPromise).then(function(){
- var data = self.model.get(self.handleCreateRecord).data;
- return self.model.notifyChanges(self.handleCreateRecord, state.createForm)
- .then(function () {
- // FIXME can't it directly written REPLACE_WITH ids=state.createForm.analytic_tag_ids
- return self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'REPLACE_WITH', ids: []}})
- })
- .then(function (){
- var defs = [];
- _.each(state.createForm.analytic_tag_ids, function (tag) {
- defs.push(self.model.notifyChanges(self.handleCreateRecord, {analytic_tag_ids: {operation: 'ADD_M2M', ids: tag}}));
- });
- return Promise.all(defs);
- })
- .then(function () {
- return self.model.notifyChanges(self.handleCreateRecord, {tax_ids: {operation: 'REPLACE_WITH', ids: []}})
- })
- .then(function (){
- var defs = [];
- _.each(state.createForm.tax_ids, function (tag) {
- defs.push(self.model.notifyChanges(self.handleCreateRecord, {tax_ids: {operation: 'ADD_M2M', ids: tag}}));
- });
- return Promise.all(defs);
- })
- .then(function () {
- var record = self.model.get(self.handleCreateRecord);
- _.each(self.fields, function (field, fieldName) {
- if (self._avoidFieldUpdate[fieldName]) return;
- if (fieldName === "partner_id") return;
- if ((data[fieldName] || state.createForm[fieldName]) && !_.isEqual(state.createForm[fieldName], data[fieldName])) {
- field.reset(record);
- }
- if (fieldName === 'tax_ids') {
- if (!state.createForm[fieldName].length || state.createForm[fieldName].length > 1) {
- $('.create_force_tax_included').addClass('d-none');
- }
- else {
- $('.create_force_tax_included').removeClass('d-none');
- var price_include = state.createForm[fieldName][0].price_include;
- var force_tax_included = state.createForm[fieldName][0].force_tax_included;
- self.$('.create_force_tax_included input').prop('checked', force_tax_included);
- self.$('.create_force_tax_included input').prop('disabled', price_include);
- }
- }
- });
- if (state.to_check) {
- // Set the to_check field to true if global to_check is set
- self.$('.create_to_check input').prop('checked', state.to_check).change();
- }
- return true;
- });
- });
- }
- this.$('.create .add_line').toggle(!!state.balance.amount_currency);
- },
-
- updatePartialAmount: function(line_id, amount) {
- var $line = this.$('.mv_line[data-line-id='+line_id+']');
- $line.find('.edit_amount').addClass('d-none');
- $line.find('.edit_amount_input').removeClass('d-none');
- $line.find('.edit_amount_input').focus();
- $line.find('.edit_amount_input').val(amount.toFixed(2));
- $line.find('.line_amount').addClass('d-none');
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- * @param {jQueryElement} $el
- */
- _destroyPopover: function ($el) {
- var popover = $el.data('bs.popover');
- if (popover) {
- popover.dispose();
- }
- },
- /**
- * @private
- * @param {integer} partnerID
- * @param {string} partnerName
- * @returns {string} local id of the dataPoint
- */
- _makePartnerRecord: function (partnerID, partnerName) {
- var field = {
- relation: 'res.partner',
- type: 'many2one',
- name: 'partner_id',
- };
- if (partnerID) {
- field.value = [partnerID, partnerName];
- }
- return this.model.makeRecord('account.bank.statement.line', [field], {
- partner_id: {
- domain: ["|", ["is_company", "=", true], ["parent_id", "=", false]],
- options: {
- no_open: true
- }
- }
- });
- },
-
- /**
- * create account_id, tax_ids, analytic_account_id, analytic_tag_ids, label and amount fields
- *
- * @private
- * @param {object} state - statement line
- * @returns {Promise}
- */
- _renderCreate: function (state) {
- var self = this;
- return this.model.makeRecord('account.bank.statement.line', [{
- relation: 'account.account',
- type: 'many2one',
- name: 'account_id',
- domain: [['company_id', '=', state.st_line.company_id]],
- }, {
- relation: 'account.journal',
- type: 'many2one',
- name: 'journal_id',
- domain: [['company_id', '=', state.st_line.company_id]],
- }, {
- relation: 'account.tax',
- type: 'many2many',
- name: 'tax_ids',
- domain: [['company_id', '=', state.st_line.company_id]],
- }, {
- relation: 'account.analytic.account',
- type: 'many2one',
- name: 'analytic_account_id',
- }, {
- relation: 'account.analytic.tag',
- type: 'many2many',
- name: 'analytic_tag_ids',
- }, {
- type: 'boolean',
- name: 'force_tax_included',
- }, {
- type: 'char',
- name: 'label',
- }, {
- type: 'float',
- name: 'amount',
- }, {
- type: 'char', //TODO is it a bug or a feature when type date exists ?
- name: 'date',
- }, {
- type: 'boolean',
- name: 'to_check',
- }], {
- account_id: {
- string: _t("Account"),
- domain: [['deprecated', '=', false]],
- },
- label: {string: _t("Label")},
- amount: {string: _t("Account")},
- }).then(function (recordID) {
- self.handleCreateRecord = recordID;
- var record = self.model.get(self.handleCreateRecord);
-
- self.fields.account_id = new relational_fields.FieldMany2One(self,
- 'account_id', record, {mode: 'edit', attrs: {can_create:false}});
-
- self.fields.journal_id = new relational_fields.FieldMany2One(self,
- 'journal_id', record, {mode: 'edit'});
-
- self.fields.tax_ids = new relational_fields.FieldMany2ManyTags(self,
- 'tax_ids', record, {mode: 'edit', additionalContext: {append_type_to_tax_name: true}});
-
- self.fields.analytic_account_id = new relational_fields.FieldMany2One(self,
- 'analytic_account_id', record, {mode: 'edit'});
-
- self.fields.analytic_tag_ids = new relational_fields.FieldMany2ManyTags(self,
- 'analytic_tag_ids', record, {mode: 'edit'});
-
- self.fields.force_tax_included = new basic_fields.FieldBoolean(self,
- 'force_tax_included', record, {mode: 'edit'});
-
- self.fields.label = new basic_fields.FieldChar(self,
- 'label', record, {mode: 'edit'});
-
- self.fields.amount = new basic_fields.FieldFloat(self,
- 'amount', record, {mode: 'edit'});
-
- self.fields.date = new basic_fields.FieldDate(self,
- 'date', record, {mode: 'edit'});
-
- self.fields.to_check = new basic_fields.FieldBoolean(self,
- 'to_check', record, {mode: 'edit'});
-
- var $create = $(qweb.render("reconciliation.line.create", {'state': state}));
- self.fields.account_id.appendTo($create.find('.create_account_id .o_td_field'))
- .then(addRequiredStyle.bind(self, self.fields.account_id));
- self.fields.journal_id.appendTo($create.find('.create_journal_id .o_td_field'));
- self.fields.tax_ids.appendTo($create.find('.create_tax_id .o_td_field'));
- self.fields.analytic_account_id.appendTo($create.find('.create_analytic_account_id .o_td_field'));
- self.fields.analytic_tag_ids.appendTo($create.find('.create_analytic_tag_ids .o_td_field'));
- self.fields.force_tax_included.appendTo($create.find('.create_force_tax_included .o_td_field'));
- self.fields.label.appendTo($create.find('.create_label .o_td_field'))
- .then(addRequiredStyle.bind(self, self.fields.label));
- self.fields.amount.appendTo($create.find('.create_amount .o_td_field'))
- .then(addRequiredStyle.bind(self, self.fields.amount));
- self.fields.date.appendTo($create.find('.create_date .o_td_field'));
- self.fields.to_check.appendTo($create.find('.create_to_check .o_td_field'));
- self.$('.create').append($create);
-
- function addRequiredStyle(widget) {
- widget.$el.addClass('o_required_modifier');
- }
- });
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
- /**
- * The event on the partner m2o widget was propagated to the bank statement
- * line widget, causing it to expand and the others to collapse. This caused
- * the dropdown to be poorly placed and an unwanted update of this widget.
- *
- * @private
- */
- _onStopPropagation: function(ev) {
- ev.stopPropagation();
- },
-
- /**
- * @private
- * @param {MouseEvent} event
- */
- _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) {
- event.stopPropagation();
- var $line = $(event.target);
- var moveLineId = $line.closest('.mv_line').data('line-id');
- this.trigger_up('partial_reconcile', {'data': {mvLineId: moveLineId, 'amount': $line.val()}});
- },
- _onEditAmount: function (event) {
- event.preventDefault();
- event.stopPropagation();
- // Don't call when clicking inside the input field
- if (! $(event.target).hasClass('edit_amount_input')){
- var $line = $(event.target);
- this.trigger_up('getPartialAmount', {'data': $line.closest('.mv_line').data('line-id')});
- }
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _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_mode: "list",
- target: 'current'
- },
- {
- on_reverse_breadcrumb: function() {self.trigger_up('reload');},
- });
- },
- /**
- * @private
- * @param {OdooEvent} event
- */
- _onFieldChanged: function (event) {
- event.stopPropagation();
- var fieldName = event.target.name;
- if (fieldName === 'partner_id') {
- var partner_id = event.data.changes.partner_id;
- this.trigger_up('change_partner', {'data': partner_id});
- } else {
- if (event.data.changes.amount && isNaN(event.data.changes.amount)) {
- return;
- }
- this.trigger_up('update_proposition', {'data': event.data.changes});
- }
- },
- /**
- * @private
- */
- _onTogglePanel: function () {
- if (this.$el[0].getAttribute('data-mode') == 'inactive')
- this.trigger_up('change_mode', {'data': 'default'});
- },
- /**
- * @private
- */
- _onChangeTab: function(event) {
- if (event.currentTarget.nodeName === 'TFOOT') {
- this.trigger_up('change_mode', {'data': 'next'});
- } else {
- var modes = this.model.modes;
- var selected_mode = modes.find(function(e) {return event.target.getAttribute('href').includes(e)});
- if (selected_mode) {
- this.trigger_up('change_mode', {'data': selected_mode});
- }
- }
- },
- /**
- * @private
- * @param {input event} event
- */
- _onFilterChange: function (event) {
- this.trigger_up('change_filter', {'data': _.str.strip($(event.target).val())});
- },
- /**
- * @private
- * @param {keyup event} event
- */
- _onInputKeyup: function (event) {
- var target_partner_id = $(event.target).parents('[name="partner_id"]');
- if (target_partner_id.length === 1) {
- return;
- }
- if(event.keyCode === 13) {
- if ($(event.target).hasClass('edit_amount_input')) {
- $(event.target).blur();
- return;
- }
- var created_lines = _.findWhere(this.model.lines, {mode: 'create'});
- if (created_lines && created_lines.balance.amount) {
- this._onCreateProposition();
- }
- return;
- }
- if ($(event.target).hasClass('edit_amount_input')) {
- if (event.type === 'keyup') {
- return;
- }
- else {
- return this._editAmount(event);
- }
- }
-
- var self = this;
- for (var fieldName in this.fields) {
- var field = this.fields[fieldName];
- if (!field.$el.is(event.target)) {
- continue;
- }
- this._avoidFieldUpdate[field.name] = event.type !== 'focusout';
- field.value = false;
- field._setValue($(event.target).val()).then(function () {
- self._avoidFieldUpdate[field.name] = false;
- });
- break;
- }
- },
- /**
- * @private
- */
- _onLoadMore: function (ev) {
- ev.preventDefault();
- this.trigger_up('change_offset', {'data': 1});
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _onSelectMoveLine: function (event) {
- var $el = $(event.target);
- $el.prop('disabled', true);
- this._destroyPopover($el);
- var moveLineId = $el.closest('.mv_line').data('line-id');
- this.trigger_up('add_proposition', {'data': moveLineId});
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _onSelectProposition: function (event) {
- var $el = $(event.target);
- this._destroyPopover($el);
- var moveLineId = $el.closest('.mv_line').data('line-id');
- this.trigger_up('remove_proposition', {'data': moveLineId});
- },
- /**
- * @private
- * @param {MouseEvent} event
- */
- _onQuickCreateProposition: function (event) {
- document.activeElement && document.activeElement.blur();
- this.trigger_up('quick_create_proposition', {'data': $(event.target).data('reconcile-model-id')});
- },
- /**
- * @private
- */
- _onCreateProposition: function () {
- document.activeElement && document.activeElement.blur();
- var invalid = [];
- _.each(this.fields, function (field) {
- if (!field.isValid()) {
- invalid.push(field.string);
- }
- });
- if (invalid.length) {
- this.do_warn(_t("Some fields are undefined"), invalid.join(', '));
- return;
- }
- this.trigger_up('create_proposition');
- },
- /**
- * @private
- */
- _onValidate: function () {
- this.trigger_up('validate');
- }
-});
-
-
-/**
- * rendering of the manual reconciliation action contains progress bar, title
- * and auto reconciliation button
- */
-var ManualRenderer = StatementRenderer.extend({
- template: "reconciliation.manual.statement",
-
-});
-
-
-/**
- * rendering of the manual reconciliation, contains line data, proposition and
- * view for 'match' mode
- */
-var ManualLineRenderer = LineRenderer.extend({
- template: "reconciliation.manual.line",
- /**
- * @override
- * @param {string} handle
- * @param {number} proposition id (move line id)
- * @returns {Promise}
- */
- removeProposition: function (handle, id) {
- if (!id) {
- return Promise.resolve();
- }
- return this._super(handle, id);
- },
- /**
- * move the partner field
- *
- * @override
- */
- start: function () {
- var self = this;
- return this._super.apply(this, arguments).then(function () {
- return self.model.makeRecord('account.move.line', [{
- relation: 'account.account',
- type: 'many2one',
- name: 'account_id',
- value: [self._initialState.account_id.id, self._initialState.account_id.display_name],
- }]).then(function (recordID) {
- self.fields.title_account_id = new relational_fields.FieldMany2One(self,
- 'account_id',
- self.model.get(recordID),
- {mode: 'readonly'}
- );
- }).then(function () {
- return self.fields.title_account_id.appendTo(self.$('.accounting_view thead td:eq(0) span:first'));
- });
- });
- },
- /**
- * @override
- */
- update: function (state) {
- this._super(state);
- var props = _.filter(state.reconciliation_proposition, {'display': true});
- if (!props.length) {
- var $line = $(qweb.render("reconciliation.line.mv_line", {'line': {}, 'state': state}));
- this.$('.accounting_view tbody').append($line);
- }
- },
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
- /**
- * display journal_id field
- *
- * @override
- */
- _renderCreate: function (state) {
- var self = this;
- var parentPromise = this._super(state).then(function() {
- self.$('.create .create_journal_id').show();
- self.$('.create .create_date').removeClass('d-none');
- self.$('.create .create_journal_id .o_input').addClass('o_required_modifier');
- });
- return parentPromise;
- },
-
-});
-
-
-return {
- StatementRenderer: StatementRenderer,
- ManualRenderer: ManualRenderer,
- LineRenderer: LineRenderer,
- ManualLineRenderer: ManualLineRenderer,
-};
-});
diff --git a/addons/account/static/src/xml/account_reconciliation.xml b/addons/account/static/src/xml/account_reconciliation.xml
deleted file mode 100644
index e1ac928a8e6..00000000000
--- a/addons/account/static/src/xml/account_reconciliation.xml
+++ /dev/null
@@ -1,400 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<templates xml:space="preserve">
-
-<div t-name="reconciliation" class="o_reconciliation">
- <div class="o_form_view">
- <div class="o_form_sheet_bg">
- <div class="o_form_sheet"/>
- </div>
- </div>
-</div>
-
-<t t-name="reconciliation.control.pager">
- <div class="progress progress-reconciliation">
- <div aria-valuemin="0" t-att-aria-valuenow="widget._initialState.valuenow" t-att-aria-valuemax="widget._initialState.valuemax" class="progress-bar" role="progressbar" style="width: 0%;"><span class="valuenow"><t t-esc="widget._initialState.valuenow"/></span> / <span class="valuemax"><t t-esc="widget._initialState.valuemax"/></span></div>
- </div>
-</t>
-
-<t t-name="reconciliation.statement">
- <div t-if="widget._initialState.valuemax">
- <div class="notification_area"/>
- <div class="o_reconciliation_lines"/>
- <div t-if="widget._initialState.valuemax &gt; widget._initialState.defaultDisplayQty">
- <button class="btn btn-secondary js_load_more">Load more</button>
- </div>
- </div>
- <div t-else="" class="o_view_noreconciliation">
- <p>Nothing to do!</p>
- <p>This page displays all the bank transactions that are to be reconciled and provides with a neat interface to do so.</p>
- </div>
-</t>
-
-<t t-name="reconciliation.manual.statement" t-extend="reconciliation.statement">
- <t t-jquery="div:first" t-operation="attributes">
- <attribute name="class" value="o_manual_statement" />
- </t>
- <t t-jquery=".o_view_noreconciliation p" t-operation="replace"></t>
- <t t-jquery=".o_filter_input_wrapper" t-operation="replace"></t>
- <t t-jquery=".o_view_noreconciliation" t-operation="append">
- <p><b>Good Job!</b> There is nothing to reconcile.</p>
- <p>All invoices and payments have been matched, your accounts' balances are clean.</p>
- <p>
- From now on, you may want to:
- <ul>
- <li>Check that you have no bank statement lines to <a href="#"
- rel="do_action"
- data-tag="bank_statement_reconciliation_view">reconcile</a></li>
- <li>Verify <a href="#"
- rel="do_action"
- data-action_name="Unpaid Customer Invoices"
- data-model="account.move"
- data-domain="[('type', 'in', ('out_invoice', 'out_refund'))]"
- data-context="{'search_default_unpaid': 1}">unpaid invoices</a> and follow-up customers</li>
- <li>Pay your <a href="#"
- rel="do_action"
- data-action_name="Unpaid Vendor Bills"
- data-model="account.move"
- data-domain="[('type', 'in', ('in_invoice', 'in_refund'))]"
- data-context="{'search_default_unpaid': 1}">vendor bills</a></li>
- <li>Check all <a href="#"
- rel="do_action"
- data-action_name="Unreconciled Entries"
- data-model="account.move.line"
- data-context="{'search_default_unreconciled': 1}">unreconciled entries</a></li>
- </ul>
- </p>
- </t>
-</t>
-
-<div t-name="reconciliation.done" class="done_message">
- <h2>Congrats, you're all done!</h2>
- <p>You reconciled <strong><t t-esc="number"/></strong> transactions in <strong><t t-esc="duration"/></strong>.
- <t t-if="number > 1">
- <br/>That's on average <t t-esc="timePerTransaction"/> seconds per transaction.
- </t>
- </p>
- <t t-if="context &amp;&amp; context.active_model">
- <p t-if="context['active_model'] === 'account.journal' || context['active_model'] === 'account.bank.statement' || context['active_model'] === 'account.bank.statement.import'" class="actions_buttons">
- <t t-if="context.journal_id">
- <button class="button_back_to_statement btn btn-secondary" t-att-data_journal_id='context.journal_id'>Go to bank statement(s)</button>
- </t>
- <t t-if="context['active_model'] === 'account.bank.statement'">
- <button class="button_close_statement btn btn-primary" style="display: inline-block;">Close statement</button>
- </t>
- </p>
- </t>
-</div>
-
-<t t-name="reconciliation.line">
- <t t-set="state" t-value="widget._initialState"/>
- <div class="o_reconciliation_line" t-att-data-mode="state.mode" tabindex="0">
- <table class="accounting_view">
- <caption style="caption-side: top;">
- <div class="float-right o_buttons">
- <button t-attf-class="o_no_valid btn btn-secondary #{state.balance.type &lt; 0 ? '' : 'd-none'}" disabled="disabled" data-toggle="tooltip" title="Select a partner or choose a counterpart" accesskey="">Validate</button>
- <button t-attf-class="o_validate btn btn-secondary #{!state.balance.type ? '' : 'd-none'}">Validate</button>
- <button t-attf-class="o_reconcile btn btn-primary #{state.balance.type &gt; 0 ? '' : 'd-none'}">Validate</button>
- </div>
- </caption>
- <thead>
- <tr>
- <td class="cell_account_code"><t t-esc="state.st_line.account_code"/></td>
- <td class="cell_due_date"><t t-esc="state.st_line.date"/></td>
- <td class="cell_label"><t t-if="state.st_line.name" t-esc="state.st_line.name"/> <t t-if="state.st_line.amount_currency_str"> (<t t-esc="state.st_line.amount_currency_str"/>)</t></td>
- <td class="cell_left"><t t-if="state.st_line.amount &gt; 0"><t t-raw="state.st_line.amount_str"/></t></td>
- <td class="cell_right"><t t-if="state.st_line.amount &lt; 0"><t t-raw="state.st_line.amount_str"/></t></td>
- <td class="cell_info_popover"></td>
- </tr>
- </thead>
- <tbody>
- <t t-foreach="state.reconciliation_proposition" t-as="line"><t t-call="reconciliation.line.mv_line"/></t>
- </tbody>
- <tfoot>
- <t t-call="reconciliation.line.balance"/>
- </tfoot>
- </table>
- <div class="o_notebook">
- <ul class="nav nav-tabs ml-0 mr-0">
- <li class="nav-item" t-attf-title="{{'Match statement with existing lines on receivable/payable accounts&lt;br&gt;* Black line: existing journal entry that should be matched&lt;br&gt;* Blue lines: existing payment that should be matched'}}" data-toggle="tooltip"><a data-toggle="tab" disable_anchor="true" t-attf-href="#notebook_page_match_rp_#{state.st_line.id}" class="nav-link active nav-match_rp" role="tab" aria-selected="true">Customer/Vendor Matching</a></li>
- <li class="nav-item" title="Match with entries that are not from receivable/payable accounts" data-toggle="tooltip"><a data-toggle="tab" disable_anchor="true" t-attf-href="#notebook_page_match_other_#{state.st_line.id}" class="nav-link nav-match_other" role="tab" aria-selected="false">Miscellaneous Matching</a></li>
- <li class="nav-item" title="Create a counterpart" data-toggle="tooltip"><a data-toggle="tab" disable_anchor="true" t-attf-href="#notebook_page_create_#{state.st_line.id}" class="nav-link nav-create" role="tab" aria-selected="false">Manual Operations</a></li>
- </ul>
- <div class="tab-content">
- <div class="tab-pane active" t-attf-id="notebook_page_match_rp_#{state.st_line.id}">
- <div class="match">
- <t t-call="reconciliation.line.match"/>
- </div>
- </div>
- <div class="tab-pane" t-attf-id="notebook_page_match_other_#{state.st_line.id}">
- <div class="match">
- <t t-call="reconciliation.line.match"/>
- </div>
- </div>
- <div class="tab-pane" t-attf-id="notebook_page_create_#{state.st_line.id}">
- <div class="create"></div>
- </div>
- </div>
- </div>
- </div>
-</t>
-
-<t t-name="reconciliation.manual.line" t-extend="reconciliation.line">
- <t t-jquery=".o_buttons" t-operation="replace">
- <div class="float-right o_buttons">
- <button t-attf-class="o_validate btn btn-secondary #{!state.balance.type ? '' : 'd-none'}">Reconcile</button>
- <button t-attf-class="o_reconcile btn btn-primary #{state.balance.type &gt; 0 ? '' : 'd-none'}">Reconcile</button>
- <button t-attf-class="o_no_valid btn btn-secondary #{state.balance.type &lt; 0 ? '' : 'd-none'}">Skip</button>
- </div>
- </t>
- <t t-jquery=".accounting_view tbody" t-operation="append">
- <t t-if='!_.filter(state.reconciliation_proposition, {"display": true}).length'>
- <t t-set="line" t-value='{}'/>
- <t t-call="reconciliation.line.mv_line"/>
- </t>
- </t>
- <t t-jquery=".accounting_view thead tr" t-operation="replace">
- <tr>
- <td colspan="3"><span/><span t-if="state.last_time_entries_checked">Last Reconciliation: <t t-esc="state.last_time_entries_checked"/></span></td>
- <td colspan="2"><t t-esc="state.st_line.account_code"/></td>
- <td class="cell_info_popover"></td>
- </tr>
- </t>
- <t t-jquery='div[t-attf-id*="notebook_page_match_rp"]' t-operation="replace"/>
- <t t-jquery='a[t-attf-href*="notebook_page_match_rp"]' t-operation="replace"/>
-</t>
-
-<t t-name="reconciliation.line.balance">
- <tr t-if="state.balance.show_balance">
- <td class="cell_account_code"><t t-esc="state.balance.account_code"/></td>
- <td class="cell_due_date"></td>
- <td class="cell_label"><t t-if="state.st_line.partner_id">Open balance</t><t t-else="">Choose counterpart or Create Write-off</t></td>
- <td class="cell_left"><t t-if="state.balance.amount_currency &lt; 0"><span role="img" t-if="state.balance.amount_currency_str" t-attf-class="o_multi_currency o_multi_currency_color_#{state.balance.currency_id%8} line_info_button fa fa-money" t-att-data-content="state.balance.amount_currency_str" t-att-aria-label="state.balance.amount_currency_str" t-att-title="state.balance.amount_currency_str"/><t t-raw="state.balance.amount_str"/></t></td>
- <td class="cell_right"><t t-if="state.balance.amount_currency &gt; 0"><span role="img" t-if="state.balance.amount_currency_str" t-attf-class="o_multi_currency o_multi_currency_color_#{state.balance.currency_id%8} line_info_button fa fa-money" t-att-data-content="state.balance.amount_currency_str" t-att-aria-label="state.balance.amount_currency_str" t-att-title="state.balance.amount_currency_str"/><t t-raw="state.balance.amount_str"/></t></td>
- <td class="cell_info_popover"></td>
- </tr>
-</t>
-
-
-<div t-name="reconciliation.line.match">
- <div class="match_controls">
- <span><input class="filter o_input" placeholder="Filter on account, label, partner, amount,..." type="text" t-att-value="state['filter_{{state.mode}}']"/></span>
- <button class="btn btn-secondary btn-sm fa fa-search" type="button"></button>
- </div>
- <table>
- <tbody>
- </tbody>
- </table>
- <div class="load-more text-center">
- <a href="#">Load more... (<span></span> remaining)</a>
- </div>
-</div>
-
-
-<div t-name="reconciliation.line.create">
- <div class="quick_add">
- <div class="btn-group o_reconcile_models" t-if="state.reconcileModels">
- <t t-foreach="state.reconcileModels" t-as="reconcileModel">
- <button class="btn btn-primary"
- t-if="reconcileModel.rule_type === 'writeoff_button' &amp;&amp; (reconcileModel.match_journal_ids.length == 0 || reconcileModel.match_journal_ids.includes(state.st_line.journal_id))"
- t-att-data-reconcile-model-id="reconcileModel.id">
- <t t-esc="reconcileModel.name"/>
- </button>
- </t>
- <p t-if="!state.reconcileModels.length" style="color: #bbb;">To speed up reconciliation, define <a style="cursor: pointer;" class="reconcile_model_create">reconciliation models</a>.</p>
- </div>
- <div class="dropdown float-right">
- <a data-toggle="dropdown" href="#"><span class="fa fa-cog" role="img" aria-label="Settings"/></a>
- <div class="dropdown-menu dropdown-menu-right" role="menu" aria-label="Presets config">
- <a role="menuitem" class="dropdown-item reconcile_model_create" href="#">Create model</a>
- <a role="menuitem" class="dropdown-item reconcile_model_edit" href="#">Modify models</a>
- </div>
- </div>
- </div>
- <div class="clearfix o_form_sheet">
- <div class="o_group">
- <table class="o_group o_inner_group o_group_col_6">
- <tbody>
- <tr class="create_account_id">
- <td class="o_td_label"><label class="o_form_label">Account</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_tax_id">
- <td class="o_td_label"><label class="o_form_label">Taxes</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_analytic_account_id" groups="analytic.group_analytic_accounting">
- <td class="o_td_label"><label class="o_form_label">Analytic Acc.</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_analytic_tag_ids" groups="analytic.group_analytic_tags">
- <td class="o_td_label"><label class="o_form_label">Analytic Tags.</label></td>
- <td class="o_td_field"></td>
- </tr>
- </tbody>
- </table>
- <table class="o_group o_inner_group o_group_col_6">
- <tbody>
- <tr class="create_journal_id" style="display: none;">
- <td class="o_td_label"><label class="o_form_label">Journal</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_label">
- <td class="o_td_label"><label class="o_form_label">Label</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_amount">
- <td class="o_td_label"><label class="o_form_label">Amount</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_force_tax_included d-none">
- <td class="o_td_label"><label class="o_form_label">Tax Included in Price</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_date d-none">
- <td class="o_td_label"><label class="o_form_label">Writeoff Date</label></td>
- <td class="o_td_field"></td>
- </tr>
- <tr class="create_to_check">
- <td class="o_td_label"><label class="o_form_label">To Check</label></td>
- <td class="o_td_field"></td>
- </tr>
- </tbody>
- </table>
- </div>
-</div>
- <div class="add_line_container">
- <a class="add_line" t-att-style="!state.balance.amout ? 'display: none;' : null"><i class="fa fa-plus-circle"/> Save and New</a>
- </div>
-</div>
-
-
-<t t-name="reconciliation.line.mv_line.amount">
- <span t-att-class="(line.is_move_line &amp;&amp; proposition == true) ? 'cell' : ''">
- <span class="line_amount">
- <span t-if="line.amount_currency_str"
- t-attf-class="o_multi_currency o_multi_currency_color_#{line.currency_id%8} line_info_button fa fa-money"
- t-att-data-content="line.amount_currency_str"/>
- <span t-if="line.partial_amount &amp;&amp; line.partial_amount != line.amount" class="strike_amount text-muted">
- <t t-raw="line.amount_str"/>
- <br/>
- </span>
- </span>
- <t t-if="line.is_move_line &amp;&amp; proposition == true">
- <i class="fa fa-pencil edit_amount"></i>
- <input class="edit_amount_input text-right d-none"/>
- </t>
- <span class="line_amount">
- <t t-if="!line.partial_amount_str" t-raw="line.amount_str"/>
- <t t-if="line.partial_amount_str &amp;&amp; line.partial_amount != line.amount" t-raw="line.partial_amount_str"/>
- </span>
- </span>
-</t>
-
-
-<t t-name="reconciliation.line.mv_line">
- <tr t-if="line.display !== false" t-attf-class="mv_line #{line.already_paid ? ' already_reconciled' : ''} #{line.__invalid ? 'invalid' : ''} #{line.is_tax ? 'is_tax' : ''}" t-att-data-line-id="line.id" t-att-data-selected="selected">
- <td class="cell_account_code"><t t-esc="line.account_code"/>&#8203;</td> <!-- zero width space to make empty lines the height of the text -->
- <td class="cell_due_date">
- <t t-if="typeof(line.id) != 'number' &amp;&amp; line.id">
- <span class="badge badge-secondary">New</span>
- </t>
- <t t-else="" t-esc="line.date_maturity || line.date"/>
- </td>
- <td class="cell_label">
- <t t-if="line.partner_id &amp;&amp; line.partner_id !== state.st_line.partner_id">
- <t t-if="line.partner_name.length">
- <span class="font-weight-bold" t-esc="line.partner_name"/>:
- </t>
- </t>
- <t t-esc="line.label || line.name"/>
- <t t-if="line.ref &amp;&amp; line.ref.length"> : </t>
- <t t-esc="line.ref"/>
- </td>
- <td class="cell_left">
- <t t-if="line.amount &lt; 0">
- <t t-call="reconciliation.line.mv_line.amount"/>
- </t>
- </td>
- <td class="cell_right">
- <t t-if="line.amount &gt; 0">
- <t t-call="reconciliation.line.mv_line.amount"/>
- </t>
- </td>
- <td class="cell_info_popover"></td>
- </tr>
-</t>
-
-
-<t t-name="reconciliation.line.mv_line.details">
- <table class='details'>
- <tr t-if="line.account_code"><td>Account</td><td><t t-esc="line.account_code"/> <t t-esc="line.account_name"/></td></tr>
- <tr><td>Date</td><td><t t-esc="line.date"/></td></tr>
- <tr><td>Due Date</td><td><t t-esc="line.date_maturity || line.date"/></td></tr>
- <tr><td>Journal</td><td><t t-esc="line.journal_id[1]"/></td></tr>
- <tr t-if="line.partner_id"><td>Partner</td><td><t t-esc="line.partner_name"/></td></tr>
- <tr><td>Label</td><td><t t-esc="line.label"/></td></tr>
- <tr t-if="line.ref"><td>Ref</td><td><t t-esc="line.ref"/></td></tr>
- <tr><td>Amount</td><td><t t-raw="line.total_amount_str"/><t t-if="line.total_amount_currency_str"> (<t t-esc="line.total_amount_currency_str"/>)</t></td></tr>
- <tr t-if="line.is_partially_reconciled"><td>Residual</td><td>
- <t t-raw="line.amount_str"/><t t-if="line.amount_currency_str"> (<t t-esc="line.amount_currency_str"/>)</t>
- </td></tr>
- <tr class="one_line_info" t-if='line.already_paid'>
- <td colspan="2">This payment is registered but not reconciled.</td>
- </tr>
- </table>
-</t>
-
-
-<t t-name="reconciliation.line.statement_line.details">
- <table class='details'>
- <tr><td>Date</td><td><t t-esc="state.st_line.date"/></td></tr>
- <tr t-if="state.st_line.partner_name"><td>Partner</td><td><t t-esc="state.st_line.partner_name"/></td></tr>
- <tr t-if="state.st_line.ref"><td>Transaction</td><td><t t-esc="state.st_line.ref"/></td></tr>
- <tr><td>Description</td><td><t t-esc="state.st_line.name"/></td></tr>
- <tr><td>Amount</td><td><t t-raw="state.st_line.amount_str"/><t t-if="state.st_line.amount_currency_str"> (<t t-esc="state.st_line.amount_currency_str"/>)</t></td></tr>
- <tr><td>Account</td><td><t t-esc="state.st_line.account_code"/> <t t-esc="state.st_line.account_name"/></td></tr>
- <tr t-if="state.st_line.note"><td>Note</td><td style="white-space: pre;"><t t-esc="state.st_line.note"/></td></tr>
- </table>
-</t>
-
-
-<t t-name="reconciliation.notification.reconciled">
- <t t-if="details !== undefined">
- <a rel="do_action" href="#" aria-label="External link" title="External link"
- t-att-data-action_name="details.name"
- t-att-data-model="details.model"
- t-att-data-ids="details.ids">
- <t t-esc="nb_reconciled_lines"/>
- statement lines
- </a>
- have been reconciled automatically.
- </t>
-</t>
-
-
-<t t-name="reconciliation.notification.default">
- <t t-esc="message"/>
- <t t-if="details !== undefined">
- <a class="fa fa-external-link" rel="do_action" href="#" aria-label="External link" title="External link"
- t-att-data-action_name="details.name"
- t-att-data-model="details.model"
- t-att-data-ids="details.ids">
- </a>
- </t>
-</t>
-
-
-<t t-name="reconciliation.notification">
- <div t-att-class="'notification alert-dismissible alert alert-' + type" role="alert">
- <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span title="Close" class="fa fa-times"></span></button>
- <t t-if="template">
- <t t-call="{{template}}"/>
- </t>
- <t t-else="">
- <t t-call="reconciliation.notification.default"/>
- </t>
- </div>
-</t>
-
-</templates>
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
deleted file mode 100644
index a1e2e215126..00000000000
--- a/addons/account/static/tests/reconciliation_tests.js
+++ /dev/null
@@ -1,2093 +0,0 @@
-odoo.define('account.reconciliation_tests.data', function () {
-"use strict";
-
-/*
- * Debug tip:
- * To be able to "see" the test in the browser:
- * var $body = $('body');
- * $body.addClass('debug');
- * clientAction.appendTo($body);
- */
-
-var Datas = {};
-
-var db = {
- 'res.company': {
- fields: {
- id: {string: "ID", type: 'integer'},
- display_name: {string: "Displayed name", type: 'char'},
- },
- records: [
- {id: 1, display_name: "company 1"},
- ],
- },
- 'res.partner': {
- fields: {
- id: {string: "ID", type: 'integer'},
- display_name: {string: "Displayed name", type: 'char'},
- image: {string: "image", type: 'integer'},
- parent_id: {string: "Parent", type: 'boolean'},
- is_company: {string: "Is company", type: 'boolean'},
- property_account_receivable_id: {string: 'Account receivable', type: 'many2one', relation: 'account.account'},
- property_account_payable_id: {string: 'Account payable', type: 'many2one', relation: 'account.account'},
- },
- records: [
- {id: 1, display_name: "partner 1", image: 'AAA'},
- {id: 2, display_name: "partner 2", image: 'BBB'},
- {id: 3, display_name: "partner 3", image: 'CCC'},
- {id: 4, display_name: "partner 4", image: 'DDD'},
- {id: 8, display_name: "Agrolait", image: 'EEE'},
- {id: 12, display_name: "Camptocamp", image: 'FFF', property_account_receivable_id: 287, property_account_payable_id: 287},
- // add more to have 'Search More' option
- {id: 98, display_name: "partner 98", image: 'YYY'},
- {id: 99, display_name: "partner 99", image: 'ZZZ'},
- ],
- mark_as_reconciled: function () {
- return Promise.resolve();
- },
- },
- 'account.account': {
- fields: {
- id: {string: "ID", type: 'integer'},
- code: {string: "code", type: 'integer'},
- name: {string: "Displayed name", type: 'char'},
- company_id: {string: "Company", type: 'many2one', relation: 'res.company'},
- deprecated: {string: "Deprecated", type: 'boolean'},
- },
- records: [
- {id: 282, code: 100000, name: "100000 Fixed Asset Account", company_id: 1},
- {id: 283, code: 101000, name: "101000 Current Assets", company_id: 1},
- {id: 284, code: 101110, name: "101110 Stock Valuation Account", company_id: 1},
- {id: 285, code: 101120, name: "101120 Stock Interim Account (Received)", company_id: 1},
- {id: 286, code: 101130, name: "101130 Stock Interim Account (Delivered)", company_id: 1},
- {id: 287, code: 101200, name: "101200 Account Receivable", company_id: 1},
- {id: 288, code: 101300, name: "101300 Tax Paid", company_id: 1},
- {id: 308, code: 101401, name: "101401 Bank", company_id: 1},
- {id: 499, code: 499001, name: "499001 Suspense Account", company_id: 1},
- {id: 500, code: 500, name: "500 Account", company_id: 1},
- {id: 501, code: 501, name: "501 Account", company_id: 1},
- {id: 502, code: 502, name: "502 Account", company_id: 1},
- {id: 503, code: 503, name: "503 Account", company_id: 1},
- {id: 504, code: 504, name: "504 Account", company_id: 1},
- ],
- mark_as_reconciled: function () {
- return Promise.resolve();
- },
- },
- 'account.tax': {
- fields: {
- id: {string: "ID", type: 'integer'},
- display_name: {string: "Displayed name", type: 'char'},
- amount: {string: "amout", type: 'float'},
- price_include: {string: "Included in Price", type: 'boolean'},
- account_id: {string: "partner", type: 'many2one', relation: 'account.account'},
- company_id: {string: "Company", type: 'many2one', relation: 'res.company'},
- amount_type: {string: "type", type: 'selection'}
- },
- records: [
- {id: 6, display_name: "Tax 20.00%", amount: 20, amount_type: 'percent', price_include: false, company_id: 1},
- {id: 7, display_name: "Tax 10.00% include", amount: 10, amount_type: 'percent', price_include: true, account_id: 288, company_id: 1},
- ],
- json_friendly_compute_all: function (args) {
- var tax = _.find(db['account.tax'].records, {'id': args[0][0]});
- var amount = args[1];
- var tax_base = tax.price_include ? amount*100/(100+tax.amount) : amount;
- return Promise.resolve({
- "base": amount,
- "taxes": [{
- 'id': tax.id,
- 'amount': tax_base*tax.amount/100,
- "base": tax_base,
- 'name': tax.display_name,
- "analytic": false,
- "refund_account_id": false,
- 'account_id': tax.account_id
- }],
- "total_excluded": amount/100*(100-tax.amount),
- "total_included": amount,
- });
- },
- },
- 'account.journal': {
- fields: {
- id: {string: "ID", type: 'integer'},
- display_name: {string: "Displayed name", type: 'char'},
- company_id: {string: "Company", type: 'many2one', relation: 'res.company'},
- },
- records: [
- {id: 8, display_name: "company 1 journal", company_id: 1}
- ]
- },
- 'account.analytic.account': {
- fields: {
- id: {string: "ID", type: 'integer'},
- display_name: {string: "Displayed name", type: 'char'},
- },
- records: [
- {id: 16, display_name: "Administrative"},
- {id: 7, display_name: "Agrolait - Agrolait"},
- {id: 8, display_name: "Asustek - ASUSTeK"},
- {id: 15, display_name: "Camp to Camp - Camptocamp"},
- {id: 6, display_name: "CampToCamp - Camptocamp"},
- {id: 17, display_name: "Commercial & Marketing"},
- {id: 23, display_name: "Data Import/Export Plugin - Delta PC"},
- {id: 9, display_name: "Delta PC - Delta PC"},
- ]
- },
- 'account.analytic.tag': {
- fields: {
- id: {string: 'id', type: 'integer'},
- display_name: {string: 'display_name', type: 'char'},
- },
- records: [
- {id: 1, display_name: 'Come together'},
- {id: 2, display_name: 'Right now'},
- ],
- },
- 'account.bank.statement': {
- fields: {},
- },
- 'account.bank.statement.line': {
- fields: {
- id: {string: "ID", type: 'integer'},
- display_name: {string: "Displayed name", type: 'char'},
- partner_id: {string: "partner", type: 'many2one', relation: 'res.partner'},
- company_id: {string: "Company", type: 'many2one', relation: 'res.company'},
- },
- records: [
- {id: 5, display_name: "SAJ/2014/002 and SAJ/2014/003", company_id: 1},
- {id: 6, display_name: "Bank fees", company_id: 1},
- {id: 7, display_name: "Prepayment", company_id: 1},
- {id: 8, display_name: "First 2000 \u20ac of SAJ/2014/001", company_id: 1},
- ],
- },
- 'account.move.line': {
- fields: {},
- },
- 'account.reconcile.model': {
- fields: {
- id: {string: "ID", type: 'integer'},
- name: {string: "Button Label", type: 'char'},
- rule_type: {string: "Type", type: 'selection', selection: [['writeoff_button', 'Create a Button'], ['writeoff_suggestion', 'Write off Suggestion'], ['invoice_matching', 'Invoice matching']], default:'writeoff_button'},
- has_second_line: {string: "Add a second line", type: 'boolean'},
- account_id: {string: "Account", type: 'many2one', relation:'account.account'},
- journal_id: {string: "Journal", type: 'many2one', relation:'account.journal'},
- label: {string: "Journal Item Label", type: 'char'},
- amount_type: {string: 'amount_type', type: 'selection', selection: [['fixed', 'Fixed'], ['percentage', 'Percentage of balance']], default:'percentage'},
- amount: {string: "Amount", type: 'float', digits:0, help:"Fixed amount will count as a debit if it is negative, as a credit if it is positive.", default:100.0},
- tax_ids: {string: "Tax", type: 'many2many', relation:'account.tax'},
- analytic_account_id: {string: "Analytic Account", type: 'many2one', relation:'account.analytic.account'},
- second_account_id: {string: "Second Account", type: 'many2one', relation:'account.account', domain:[('deprecated', '=', false)]},
- second_journal_id: {string: "Second Journal", type: 'many2one', relation:'account.journal', help:"This field is ignored in a bank statement reconciliation."},
- second_label: {string: "Second Journal Item Label", type: 'char'},
- second_amount_type: {string: "Second amount_type", type: 'selection', selection: [['fixed', 'Fixed'], ['percentage', 'Percentage of balance']], default:'percentage'},
- second_amount: {string: "Second Amount", type: 'float', digits:0, help:"Fixed amount will count as a debit if it is negative, as a credit if it is positive.", default:100.0},
- second_tax_ids: {string: "Second Tax", type: 'many2many', relation: 'account.tax'},
- second_analytic_account_id: {string: "Second Analytic Account", type: 'many2one', relation:'account.analytic.account'},
- match_journal_ids: {string: "Journal Ids", type: 'many2many', relation: 'account.journal'},
- analytic_tag_ids: {string: 'Analytic tags', type: 'many2many', relation: 'account.analytic.tag'},
- },
- records: [
- {'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 4, 'analytic_account_id': false, 'display_name': "Int\u00e9rrets", 'rule_type': 'writeoff_button', 'second_tax_ids': [], 'has_second_line': false, 'journal_id': false, 'label': false, 'second_label': false, 'second_account_id': false, 'account_id': 282, 'company_id': [1, "Demo SPRL"], 'tax_ids': [], 'amount_type': "fixed", 'name': "Int\u00e9rrets", 'amount': 0.0, 'second_amount': 100.0, 'match_journal_ids': []},
- {'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 2, 'analytic_account_id': false, 'display_name': "Perte et Profit", 'rule_type': 'writeoff_button', 'second_tax_ids': [], 'has_second_line': false, 'journal_id': false, 'label': false, 'second_label': false, 'second_account_id': false, 'account_id': 283, 'company_id': [1, "Demo SPRL"], 'tax_ids': [], 'amount_type': "percentage", 'name': "Perte et Profit", 'amount': 100.0, 'second_amount': 100.0, 'match_journal_ids': []},
- {'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 5, 'analytic_account_id': false, 'display_name': "Fs bank", 'rule_type': 'writeoff_button', 'second_tax_ids': [], 'has_second_line': false, 'journal_id': false, 'label': false, 'second_label': false, 'second_account_id': false, 'account_id': 284, 'company_id': [1, "Demo SPRL"], 'tax_ids': [], 'amount_type': "percentage", 'name': "Fs bank", 'amount': 100.0, 'second_amount': 100.0},
- {'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 8, 'analytic_account_id': false, 'display_name': "Caisse Sand.", 'rule_type': 'writeoff_button', 'second_tax_ids': [], 'has_second_line': false, 'journal_id': false, 'label': "Caisse Sand.", 'second_label': false, 'second_account_id': false, 'account_id': 308, 'company_id': [1, "Demo SPRL"], 'tax_ids': [], 'amount_type': "percentage", 'name': "Caisse Sand.", 'amount': 100.0, 'second_amount': 100.0, 'match_journal_ids': []},
- {'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 3, 'analytic_account_id': false, 'display_name': "ATOS", 'rule_type': 'writeoff_button', 'second_tax_ids': [7], 'has_second_line': true, 'journal_id': false, 'label': "ATOS Banque", 'second_label': "ATOS Frais", 'second_account_id': 286, 'account_id': 285, 'company_id': [1, "Demo SPRL"], 'tax_ids': [6], 'amount_type': "percentage", 'name': "ATOS", 'amount': 97.5, 'second_amount': -14.75},
- {'second_analytic_account_id': false, 'second_amount_type': "percentage", 'second_journal_id': false, 'id': 10, 'analytic_account_id': false, 'display_name': "Double", 'rule_type': 'writeoff_button', 'second_tax_ids': [], 'has_second_line': true, 'journal_id': false, 'label': "Double Banque", 'second_label': "Double Frais", 'second_account_id': 286, 'account_id': 285, 'company_id': [1, "Demo SPRL"], 'tax_ids': [], 'amount_type': "percentage", 'name': "Double", 'amount': 97.5, 'second_amount': 100, 'match_journal_ids': [], 'analytic_tag_ids': [1,2]},
- ]
- },
- 'account.reconciliation.widget': {
- fields: {},
- auto_reconcile: function () {
- return Promise.resolve(Datas.used.auto_reconciliation);
- },
- process_bank_statement_line: function (args) {
- var datas = args[1];
- var ids = _.flatten(_.pluck(_.pluck(datas, 'counterpart_aml_dicts'), 'counterpart_aml_id'));
- ids = ids.concat(_.flatten(_.pluck(datas, 'payment_aml_ids')));
- ids = _.compact(ids);
-
- for (var key in Datas.used.move_lines_for_manual_reconciliation) {
- Datas.used.move_lines_for_manual_reconciliation[key] = _.filter(Datas.used.move_lines_for_manual_reconciliation[key], function (mv_line) {
- return ids.indexOf(mv_line.id) === -1;
- });
- }
- return Promise.resolve();
- },
- get_move_lines_for_bank_statement_line: function (args) {
- var partner_id = args.splice(1, 1)[0];
- var excluded_ids = args.splice(1, 1)[0];
- var mode = args.splice(-1, 1) [0]
- if (mode === 'other') return Promise.resolve([])
- args.splice(-1,1); // ignore limit
- var key = JSON.stringify(args);
- if (!Datas.used.mv_lines[key]) {
- throw new Error("Unknown parameters for get_move_lines_for_bank_statement_line: '"+ key + "'");
- }
- var lines = Datas.used.mv_lines[key]
- .filter(function (line) {
- return excluded_ids.indexOf(line.id) === -1 && (!partner_id || partner_id === line.partner_id);
- })
- .map(function (line, i, src) {
- line.recs_count = src.length;
- return line;
- })
- .slice(0, options.params.limitMoveLines);
- return Promise.resolve(lines);
- },
- get_bank_statement_line_data: function (args) {
- var ids = args[0];
- var results = {
- value_min: 0,
- value_max: ids.length,
- lines: _.filter(Datas.used.data_widget, function (w) {return _.contains(ids, w.st_line.id);})
- };
- return Promise.resolve(results);
- },
- get_bank_statement_data: function () {
- var results = Datas.used.data_preprocess;
- results.lines = _.filter(Datas.used.data_widget, function (w) {return _.contains(results.st_lines_ids, w.st_line.id);});
- return Promise.resolve(results);
- },
- get_move_lines_for_manual_reconciliation: function (args) {
- var excluded_ids = args.splice(2, 1)[0];
- args.splice(-1,1); // ignore limit
- var key = JSON.stringify(args);
- if (!Datas.used.move_lines_for_manual_reconciliation[key]) {
- throw new Error("Unknown parameters for get_move_lines_for_manual_reconciliation: '"+ key + "'");
- }
- var lines = Datas.used.move_lines_for_manual_reconciliation[key]
- .filter(function (line) {
- return excluded_ids.indexOf(line.id) === -1;
- })
- .map(function (line, i, src) {
- line.recs_count = src.length;
- return line;
- })
- .slice(0, options.params.limitMoveLines);
- return Promise.resolve(lines);
- },
- get_all_data_for_manual_reconciliation: function (args) {
- var key = JSON.stringify(args);
- if (!Datas.used.data_for_manual_reconciliation_widget[key]) {
- throw new Error("Unknown parameters for get_all_data_for_manual_reconciliation: '"+ key + "'");
- }
- return Promise.resolve(Datas.used.data_for_manual_reconciliation_widget[key]);
- },
- process_move_lines: function (args) {
- var datas = args[0];
- for (var i in datas) {
- var data = datas[i];
- for (var key in Datas.used.move_lines_for_manual_reconciliation) {
- Datas.used.move_lines_for_manual_reconciliation[key] = _.filter(Datas.used.move_lines_for_manual_reconciliation[key], function (mv_line) {
- return data.mv_line_ids.indexOf(mv_line.id) === -1;
- });
- }
- }
- return Promise.resolve();
- },
- }
-};
-
-var data_preprocess = {
- value_min: 0,
- value_max: 4,
- notifications: [],
- num_already_reconciled_lines: 0,
- st_lines_ids: [5, 6, 7, 8],
- statement_name: 'BNK/2014/001',
-};
-
-var data_widget = [
- {
- 'st_line': {
- 'currency_id': 3,
- 'communication_partner_name': false,
- 'open_balance_account_id': 287,
- 'name': "SAJ/2014/002 and SAJ/2014/003",
- 'partner_name': "Agrolait",
- 'partner_id': 8,
- 'has_no_partner': false,
- 'journal_id': 84,
- 'account_name': "Bank",
- 'note': "",
- 'amount': 1175.0,
- 'amount_str': "$ 1,175.00",
- 'amount_currency_str': "",
- 'date': "2017-01-01",
- 'account_code': "101401",
- 'ref': "",
- 'id': 5,
- 'statement_id': 2,
- 'company_id': 1,
- },
- 'reconciliation_proposition': []
- },
- {
- 'st_line': {
- 'currency_id': 3,
- 'communication_partner_name': false,
- 'name': "Bank fees",
- 'partner_name': false,
- 'partner_id': false,
- 'has_no_partner': true,
- 'journal_id': 84,
- 'account_name': "Bank",
- 'note': "",
- 'amount': -32.58,
- 'amount_str': "$ 32.58",
- 'amount_currency_str': "",
- 'date': "2017-01-01",
- 'account_code': "101401",
- 'ref': "",
- 'id': 6,
- 'statement_id': 2,
- 'company_id': 1,
- },
- 'reconciliation_proposition': []
- },
- {
- 'st_line': {
- 'currency_id': 3,
- 'communication_partner_name': false,
- 'open_balance_account_id': 287,
- 'name': "Prepayment",
- 'partner_name': "Camptocamp",
- 'partner_id': 12,
- 'has_no_partner': false,
- 'journal_id': 84,
- 'account_name': "Bank",
- 'note': "",
- 'amount': 650.0,
- 'amount_str': "$ 650.00",
- 'amount_currency_str': "",
- 'date': "2017-01-01",
- 'account_code': "101401",
- 'ref': "",
- 'id': 7,
- 'statement_id': 2,
- 'company_id': 1,
- },
- 'reconciliation_proposition': [
- {
- 'account_type': "receivable",
- 'amount_currency_str': "",
- 'currency_id': false,
- 'date_maturity': "2017-02-07",
- 'date': "2017-01-08",
- 'total_amount_str': "$ 650.00",
- 'partner_id': 12,
- 'account_name': "101200 Account Receivable",
- 'name': "INV/2017/0012",
- 'partner_name': "Camptocamp",
- 'total_amount_currency_str': "",
- 'id': 133,
- 'credit': 0.0,
- 'journal_id': [1, "Customer Invoices"],
- 'amount_str': "$ 650.00",
- 'debit': 650.0,
- 'account_id': [287, "101200 Account Receivable"],
- 'account_code': "101200",
- 'ref': "",
- 'already_paid': false
- },
- ]
- },
- {
- 'st_line': {
- 'currency_id': 3,
- 'communication_partner_name': false,
- 'open_balance_account_id': 285,
- 'name': "First 2000 \u20ac of SAJ/2014/001",
- 'partner_name': "Camptocamp",
- 'partner_id': 12,
- 'has_no_partner': false,
- 'journal_id': 84,
- 'account_name': "Bank",
- 'note': "",
- 'amount': 2000.0,
- 'amount_str': "$ 2,000.00",
- 'amount_currency_str': "",
- 'date': "2017-01-01",
- 'account_code': "101401",
- 'ref': "",
- 'id': 8,
- 'statement_id': 2,
- 'company_id': 1,
- },
- 'reconciliation_proposition': []
- },
-];
-
-var mv_lines = {
- '[]': [],
- '[5,"",0]': [
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 650.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 109, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 650.00", 'debit': 650.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 525.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0003", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 112, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 525.00", 'debit': 525.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 650.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0012", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 134, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 650.00", 'debit': 650.0, 'account_id': [287, "101200 Account Receivable"], 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 4,610.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 106, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 4,610.00", 'debit': 4610.0, 'account_id': [287, "101200 Account Receivable"], 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 10,000.00", 'partner_id': 12, 'account_name': "Account Payable", 'name': "BILL/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 114, 'credit': 10000.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 10,000.00", 'debit': 0.0, 'account_id': [284, "101110 Stock Valuation Account"], 'account_code': "111100", 'ref': "", 'already_paid': false}
- ],
- '[5,"b",0]': [
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 100.00", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0003: CUST.IN/2017/0001", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 394, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 100.00", 'debit': 100.0, 'account_code': "101401", 'ref': "", 'already_paid': true},
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 525.50", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0004: CUST.IN/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 396, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 525.50", 'debit': 525.5, 'account_code': "101401", 'ref': "INV/2017/0003", 'already_paid': true},
- ],
- '[6,"",0]': [
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 376.00", 'partner_id': 7, 'account_name': "Bank", 'name': "BNK1/2017/0002: SUPP.OUT/2017/0002", 'partner_name': "ASUSTeK", 'total_amount_currency_str': "", 'id': 392, 'credit': 376.0, 'journal_id': "Bank", 'amount_str': "$ 376.00", 'debit': 0.0, 'account_code': "101401", 'ref': "BILL/2017/0003", 'already_paid': true},
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 100.00", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0003: CUST.IN/2017/0001", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 394, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 100.00", 'debit': 100.0, 'account_code': "101401", 'ref': "", 'already_paid': true},
- {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 10,000.00", 'partner_id': 12, 'account_name': "Account Payable", 'name': "BILL/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 114, 'credit': 10000.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 10,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 525.50", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0004: CUST.IN/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 396, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 525.50", 'debit': 525.5, 'account_code': "101401", 'ref': "INV/2017/0003", 'already_paid': true},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 650.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 109, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 650.00", 'debit': 650.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-22", 'date': "2017-01-23", 'total_amount_str': "$ 525.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0004", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 399, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 525.00", 'debit': 525.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 4,610.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 106, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 4,610.00", 'debit': 4610.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-15", 'total_amount_str': "$ 5,749.99", 'partner_id': 7, 'account_name': "Account Payable", 'name': "BILL/2017/0002", 'partner_name': "ASUSTeK", 'total_amount_currency_str': "", 'id': 117, 'credit': 5749.99, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 5,749.99", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false}
- ],
- '[6,"",5]': [
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 4,610.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 106, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 4,610.00", 'debit': 4610.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 10,000.00", 'partner_id': 12, 'account_name': "Account Payable", 'name': "BILL/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 114, 'credit': 10000.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 10,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-15", 'total_amount_str': "$ 5,749.99", 'partner_id': 7, 'account_name': "Account Payable", 'name': "BILL/2017/0002", 'partner_name': "ASUSTeK", 'total_amount_currency_str': "", 'id': 117, 'credit': 5749.99, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 5,749.99", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false}
- ],
- '[7,"",0]': [
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 650.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0012", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 133, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 650.00", 'debit': 650.0, 'account_id': [287, "101200 Account Receivable"], 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 4,610.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 106, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 4,610.00", 'debit': 4610.0, 'account_id': [287, "101200 Account Receivable"], 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 10,000.00", 'partner_id': 12, 'account_name': "Account Payable", 'name': "BILL/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 114, 'credit': 10000.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 10,000.00", 'debit': 0.0, 'account_id': [284, "101110 Stock Valuation Account"], 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 376.00", 'partner_id': 7, 'account_name': "Bank", 'name': "BNK1/2017/0002: SUPP.OUT/2017/0002", 'partner_name': "ASUSTeK", 'total_amount_currency_str': "", 'id': 392, 'credit': 376.0, 'journal_id': "Bank", 'amount_str': "$ 376.00", 'debit': 0.0, 'account_code': "101401", 'ref': "BILL/2017/0003", 'already_paid': true},
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 100.00", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0003: CUST.IN/2017/0001", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 394, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 100.00", 'debit': 100.0, 'account_code': "101401", 'ref': "", 'already_paid': true},
- {'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 525.50", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0004: CUST.IN/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 396, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 525.50", 'debit': 525.5, 'account_code': "101401", 'ref': "INV/2017/0003", 'already_paid': true},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 650.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 109, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 650.00", 'debit': 650.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-22", 'date': "2017-01-23", 'total_amount_str': "$ 525.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0004", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 399, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 525.00", 'debit': 525.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- ],
- '[8,"",0]': [],
-};
-
-var auto_reconciliation = {
- 'num_already_reconciled_lines': 1,
- 'notifications': [{
- 'message': "1 transaction was automatically reconciled.",
- 'type': "info",
- 'details': {
- 'model': "account.move",
- 'name': "Automatically reconciled items",
- 'ids': [143]
- }
- }],
- 'st_lines_ids': [5, 6, 8],
- 'statement_name': false
-};
-
-var data_for_manual_reconciliation_widget = {
- '[null,[282,283,284,285,286,287,288,308,499,500,501,502,503,504]]': {
- 'customers': [
- {'account_id': 287, 'partner_name': "Agrolait", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-14 12:30:31", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'mode': "customers"},
- {'account_id': 7, 'partner_name': "Camptocamp", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-13 14:24:55", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'mode': "customers"}
- ],
- 'accounts': [
- {
- 'account_id': 283, 'account_name': "101000 Current Assets", 'currency_id': 3, 'max_date': "2017-02-16 14:32:04", 'last_time_entries_checked': "2017-02-16", 'account_code': "101000", 'mode': "accounts",
- 'reconciliation_proposition': [
- {'account_id': 283, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "BNK1/2017/0006: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 399, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "101000", 'ref': "", 'already_paid': false},
- {'account_id': 283, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "INV/2017/0006", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 402, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "101000", 'ref': "", 'already_paid': false}
- ]
- }
- ],
- 'suppliers': [
- {
- 'account_id': 284, 'partner_name': "Agrolait",
- 'reconciliation_proposition': [
- {'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "BNK1/999: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 999, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "INV/998", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 998, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "111100", 'ref': "", 'already_paid': false}
- ],
- 'currency_id': 3, 'max_date': "2017-02-14 12:36:05", 'last_time_entries_checked': null, 'account_code': "111100", 'partner_id': 8, 'account_name': "Account Payable", 'mode': "suppliers"
- }, {
- 'account_id': 284, 'partner_name': "Camptocamp",
- 'reconciliation_proposition': [
- {'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 12, 'account_name': "101000 Current Assets", 'name': "BNK1/1999: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 1999, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_id': 284, 'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 12, 'account_name': "101000 Current Assets", 'name': "INV/1998", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 1998, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "111100", 'ref': "", 'already_paid': false}
- ],
- 'currency_id': 3, 'max_date': "2017-02-14 12:36:05", 'last_time_entries_checked': null, 'account_code': "111100", 'partner_id': 12, 'account_name': "Account Payable", 'mode': "suppliers"
- }
- ]
- },
- '["partner",null,"receivable"]': [
- {'account_id': 287, 'partner_name': "Agrolait", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-14 12:30:31", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'mode': "customers"},
- {'account_id': 287, 'partner_name': "Camptocamp", 'reconciliation_proposition': [], 'currency_id': 3, 'max_date': "2017-02-13 14:24:55", 'last_time_entries_checked': null, 'account_code': "101200", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'mode': "customers"}
- ]
-};
-
-var move_lines_for_manual_reconciliation = {
- '[287,8,"",0]': [
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency_str': "10,222.00 €", 'currency_id': 1, 'date_maturity': "2017-02-08", 'date': "2017-02-08", 'total_amount_str': "$ 11,000.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0004: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 17, 'credit': 11000.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 11,000.00", 'debit': 0.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [7, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0005: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 19, 'credit': 1000.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 180.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "BILL/2017/0003: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 21, 'credit': 180.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 180.00", 'debit': 0.0, 'account_code': "101200", 'ref': "fddfgfdgfdgsdfg", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 90.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0006: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 23, 'credit': 90.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 90.00", 'debit': 0.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-10", 'date': "2017-02-08", 'total_amount_str': "$ 650.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0012", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 6, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1000.00", 'debit': 1000.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-10", 'date': "2017-02-08", 'total_amount_str': "$ 525.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0003", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 9, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 525.00", 'debit': 525.0, 'account_code': "101200", 'ref': "", 'already_paid': false}
- ],
- '[7,12,"",0]': [
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-08", 'date': "2017-02-08", 'total_amount_str': "$ 11,000.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0004: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 17, 'credit': 11000.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 11,000.00", 'debit': 0.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [7, "101200 Account Receivable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 1,000.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0005: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 19, 'credit': 1000.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency': 100, 'amount_currency_str': "100.00 €", 'currency_id': 1, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 170.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0003: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 21, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 170.00", 'debit': 170.0, 'account_code': "101200", 'ref': "INV fddfgfdgfdgsdfg", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency': 100, 'amount_currency_str': "100.00 €", 'currency_id': 1, 'date_maturity': "2017-02-10", 'date': "2017-02-10", 'total_amount_str': "$ 180.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "BILL/2017/0003: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 22, 'credit': 180.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 180.00", 'debit': 0.0, 'account_code': "101200", 'ref': "fddfgfdgfdgsdfg", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency': 170, 'amount_currency_str': "170.00 €", 'currency_id': 1, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 100.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0003: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 23, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 100.00", 'debit': 100.0, 'account_code': "101200", 'ref': "INV fddfgfdgfdgsdfg", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [287, "101200 Account Receivable"], 'amount_currency': 180, 'amount_currency_str': "180.00 €", 'currency_id': 1, 'date_maturity': "2017-02-10", 'date': "2017-02-10", 'total_amount_str': "$ 100.00", 'partner_id': 12, 'account_name': "101200 Account Receivable", 'name': "BILL/2017/0003: Customer Payment", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 24, 'credit': 100.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 100.00", 'debit': 0.0, 'account_code': "101200", 'ref': "fddfgfdgfdgsdfg", 'already_paid': false},
- ],
- '[284,8,"",0]': [
- {'account_type': "receivable", 'account_id': [284, "111100 Account Payable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-08", 'date': "2017-02-08", 'total_amount_str': "$ 11,000.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0004: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 17, 'credit': 11000.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 11,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [284, "111100 Account Payable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0005: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 19, 'credit': 1000.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
- {'account_type': "receivable", 'account_id': [284, "111100 Account Payable"], 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-09", 'date': "2017-02-09", 'total_amount_str': "$ 180.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "BILL/2017/0003: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 21, 'credit': 180.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 180.00", 'debit': 0.0, 'account_code': "111100", 'ref': "fddfgfdgfdgsdfg", 'already_paid': false},
- ],
- '[283,null,"",0]': [
- {'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-16", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "BNK1/2017/0006: Customer Payment", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 399, 'credit': 1000.0, 'journal_id': [3, "Bank"], 'amount_str': "$ 1,000.00", 'debit': 0.0, 'account_code': "101000", 'ref': "", 'already_paid': false},
- {'account_type': "other", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-03-18", 'date': "2017-02-16", 'total_amount_str': "$ 1,000.00", 'partner_id': 8, 'account_name': "101000 Current Assets", 'name': "INV/2017/0006", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 402, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 1,000.00", 'debit': 1000.0, 'account_code': "101000", 'ref': "", 'already_paid': false}
- ],
- '[284,12,"",0]': [],
-};
-
-var session = {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
-};
-
-var options = {
- context: {
- statement_line_ids: [4]
- },
- params: {
- limitMoveLines: 5,
- },
-};
-
-Datas.params = {
- data: db,
- data_preprocess: data_preprocess,
- data_widget: data_widget,
- mv_lines: mv_lines,
- auto_reconciliation: auto_reconciliation,
- data_for_manual_reconciliation_widget: data_for_manual_reconciliation_widget,
- move_lines_for_manual_reconciliation: move_lines_for_manual_reconciliation,
- session: session,
- options: options,
-};
- // this is the main function for this module. Its job is to export (and clone) all data for a test.
-Datas.getParams = function () {
- return (this.used = $.extend(true, {}, this.params));
-};
-return Datas;
-});
-
-odoo.define('account.reconciliation_tests', function (require) {
-"use strict";
-
-var ReconciliationClientAction = require('account.ReconciliationClientAction');
-var ReconciliationRenderer = require('account.ReconciliationRenderer');
-var demoData = require('account.reconciliation_tests.data');
-
-var testUtils = require('web.test_utils');
-var testUtilsDom = require('web.test_utils_dom');
-var testUtilsMock = require('web.test_utils_mock');
-
-QUnit.module('account', {
- beforeEach: function () {
- this.params = demoData.getParams();
- testUtils.patch(ReconciliationRenderer.LineRenderer, {
- MV_LINE_DEBOUNCE: 0,
- });
- },
- afterEach: function () {
- testUtils.unpatch(ReconciliationRenderer.LineRenderer);
- },
-}, function () {
- QUnit.module('Reconciliation');
-
- QUnit.test('Reconciliation basic rendering', async function (assert) {
- assert.expect(10);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
- var widget = clientAction.widgets[0];
-
- assert.hasClass(widget.$el,'o_reconciliation_line', "should instance of widget reconciliation");
- assert.containsOnce(widget, '.accounting_view', "should have one view");
- assert.containsN(widget, '[id*="notebook_page_match"]', 2, "should have 'match_rp' and 'match_other' panel");
- assert.containsOnce(widget, '.create', "should have 'create' panel");
-
- assert.strictEqual(widget.$('thead').text().replace(/[\n\r\s]+/g, ' '), " 101401 2017-01-01 SAJ/2014/002 and SAJ/2014/003 $ 1,175.00 ", "should display the line information");
- assert.ok(widget.$('caption .o_field_many2one').length, "should display the many2one with to select a partner");
-
- assert.containsN(clientAction, '[data-mode="inactive"]', 3, "should be as 'inactive' mode by default");
- assert.strictEqual(widget.$el.data('mode'), 'match_rp', "the first one should automatically switch to match_rp mode");
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- assert.strictEqual(widget.$el.data('mode'), 'create', "should switch to 'create' mode");
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_match_rp"]'));
- assert.strictEqual(widget.$el.data('mode'), 'match_rp', "should switch to 'match_rp' mode");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation fields', async function (assert) {
- assert.expect(6);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
-
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Agrolait", "the partner many2one should display agrolait");
- assert.strictEqual(clientAction.widgets[2].$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display Camptocamp");
- await testUtils.dom.click(widget.$('.accounting_view tfoot td:first'));
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- assert.containsN(widget, '.create input.o_input', 8,
- "create panel should contain 8 fields (account_id, tax_id, journal_id, analytic_account_id, analytic_tag_ids, label, amount, date)");
- assert.containsN(widget, '.create .create_account_id .o_required_modifier, .create .create_label .o_required_modifier, .create .create_amount .o_required_modifier', 3,
- "account_id, label and amount should be required fields");
- assert.strictEqual(widget.$('.create .create_label input').val(), 'SAJ/2014/002 and SAJ/2014/003',
- "should use the name of the reconciliation line for the default label value");
- assert.strictEqual(widget.$('.create .create_amount input').val(), '1175.00',
- "should have the balance amout as default value for the amout field");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation basic data', async function (assert) {
- assert.expect(17);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
- var widget = clientAction.widgets[0];
-
- assert.containsN(widget, '.match:first .mv_line', 2, "should display 2 account move lines");
- assert.strictEqual(widget.$('.match:first .mv_line').text().replace(/[\n\r\s]+/g, ' ').replace(/[\u200B]/g, ''),
- " 101200 2017-02-07 INV/2017/0002 $ 650.00 101200 2017-02-07 INV/2017/0003 $ 525.00 ",
- "should display 4 account move lines who contains the account_code, due_date, label and the credit");
- assert.strictEqual(widget.$('.match:first .mv_line .cell_right:contains(".")').length, 2, "should display only the credit account move lines (hide the debit)");
-
- await testUtils.dom.click(clientAction.widgets[1].$('.accounting_view thead td:first'));
- assert.containsN(clientAction.widgets[1], '.mv_line', 5, "should display 5 account move lines");
- assert.strictEqual(clientAction.widgets[1].$('.mv_line .cell_right:contains(".")').length, 3, "should display only the credit account move lines (hide the debit)");
- assert.containsN(clientAction.widgets[1], '.mv_line.already_reconciled', 3, "should display 3 already reconciled account move lines");
- assert.strictEqual(clientAction.widgets[1].$('.mv_line').text().replace(/[\n\r\s]+/g, ' ').replace(/[\u200B]/g, ''),
- " 101401 2017-01-23 ASUSTeK: BNK1/2017/0002: SUPP.OUT/2017/0002 : BILL/2017/0003 $ 376.00 101401 2017-01-23 Agrolait: BNK1/2017/0003: CUST.IN/2017/0001 $ 100.00 111100 2017-02-28 Camptocamp: BILL/2017/0001 $ 10,000.00 101401 2017-01-23 Agrolait: BNK1/2017/0004: CUST.IN/2017/0002 : INV/2017/0003 $ 525.50 101200 2017-02-07 Agrolait: INV/2017/0002 $ 650.00 ",
- "should display 4 account move lines who contains the account_code, due_date, label and the credit");
- assert.strictEqual(clientAction.widgets[1].$('.mv_line .cell_left:contains(".")').length, 2, "should display only 2 debit account move lines");
-
- // load more
- assert.ok(clientAction.widgets[1].$('.match:first div.load-more a:visible').length, "should display the 'load more' button");
- assert.equal(clientAction.widgets[1].$('.match:first div.load-more span').text(), 3, "should display 3 items remaining");
- await testUtils.dom.click(clientAction.widgets[1].$('.match:first div.load-more a'));
- assert.containsN(clientAction.widgets[1], '.mv_line', 8, "should load 3 more records");
- assert.notOk(clientAction.widgets[1].$('.match:first div.load-more a:visible').length, "should not display the 'load more' button anymore");
-
- assert.ok(clientAction.widgets[0].$('caption button.btn-secondary:visible').length, "should display the secondary 'Validate' button");
- assert.equal(clientAction.widgets[1].$('caption button:disabled:visible').length, 1,"button should be disabled");
- assert.ok(clientAction.widgets[2].$('caption button.btn-primary:visible').length, "should display the primary 'Validate' button");
-
- await testUtils.dom.click(clientAction.widgets[3].$('.accounting_view thead td:first'));
- assert.strictEqual(clientAction.widgets[3].$el.data('mode'), 'create', "should switch to 'create' mode instead of 'match_rp' mode when 'match_rp' mode is empty");
-
- // open the first line
- await testUtils.dom.click(widget.$('.accounting_view thead td:first'));
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_match_rp"]'));
- // select propositions
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
-
- // await testUtils.dom.click(widget.$('caption')); //why is it inactive?
-
- testUtils.mock.intercept(clientAction, 'call_service', function (event) {
- if (event.data.args[1].method == 'process_bank_statement_line') {
- assert.deepEqual(event.data.args[1].args,
- [[5],[{partner_id: 8, counterpart_aml_dicts: [{
- "counterpart_aml_id": 109,
- "credit": 650,
- "debit": 0,
- "name": "INV/2017/0002",
- "analytic_tag_ids": [[6, null, []]]
- },
- {
- "counterpart_aml_id": 112,
- "credit": 525,
- "debit": 0,
- "name": "INV/2017/0003",
- "analytic_tag_ids": [[6, null, []]]
- }],
- payment_aml_ids: [], new_aml_dicts: [], to_check: false}]],
- "Should call process_bank_statement_line with args");
- var def = testUtils.makeTestPromise();
- def.abort = function () {};
- event.data.callback(def);
- }
- });
-
- // click on reconcile button
- await testUtils.dom.click(widget.$('.o_reconcile:visible'));
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation validate without proposition', async function (assert) {
- assert.expect(1);
- // Test added to prevent this issue happening again: https://github.com/odoo/odoo/commit/3549688b21eb65e16b9c3f2b6462eb8d8b52cd47
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
- // Ensure that when we validate a line without any selection, it is the same
- // as when we manually create a line with the line.balance and that only one
- // line is send back to server.
- testUtils.mock.intercept(clientAction, 'call_service', function (event) {
- assert.deepEqual(event.data.args[1].args,
- [[5],[{partner_id: 8, to_check: false, counterpart_aml_dicts: [],
- payment_aml_ids: [],
- to_check: false,
- new_aml_dicts: [{
- account_id: 287,
- credit: 1175,
- debit: 0,
- name: 'SAJ/2014/002 and SAJ/2014/003',
- analytic_tag_ids: [[6, null, []]]
- }]}]],
- "Should call process_bank_statement_line with ids");
- var def = testUtils.makeTestPromise();
- def.abort = function () {};
- event.data.callback(def);
- });
-
- // click on validate button
- await testUtils.dom.click(widget.$('button.o_validate:not(:hidden)'));
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation validate with proposition', async function (assert) {
- assert.expect(1);
- // Test added to check this functionality: https://github.com/odoo/odoo/commit/2f3b469dee6f18cbccce1cdf2a81cfe57960c533
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
- var widget = clientAction.widgets[0];
- // Add a line as proposition
- // open the first line
- await testUtils.dom.click(widget.$('.accounting_view thead td:first'),{allowInvisible:true});
- await testUtils.nextTick();
- // select propositions
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'),{allowInvisible:true});
- await testUtils.nextTick();
-
- // Ensure that when we validate a line with propositions and that there is a remaining balance
- // We also create a line which is the open balance.
- testUtils.mock.intercept(clientAction, 'call_service', function (event) {
- assert.deepEqual(event.data.args[1].args,
- [[5],[{partner_id: 8, to_check: false,
- counterpart_aml_dicts: [{
- counterpart_aml_id: 109,
- credit: 650,
- debit: 0,
- name: 'INV/2017/0002',
- analytic_tag_ids: [[6, null, []]]
- }],
- payment_aml_ids: [],
- new_aml_dicts: [{
- account_id: 287,
- credit: 525,
- debit: 0,
- name: 'SAJ/2014/002 and SAJ/2014/003 : Open balance'
- }]}]],
- "Should call process_bank_statement_line with ids");
- var def = testUtils.makeTestPromise();
- def.abort = function () {};
- event.data.callback(def);
- });
-
- // click on validate button
- await testUtils.dom.click(widget.$('button.o_validate:not(:hidden)'));
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation partial [REQUIRE FOCUS]', async function (assert) {
- assert.expect(8);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- mockRPC: function (route, args) {
- console.log(args.method);
- if (args.method === 'process_bank_statement_line') {
- var lines = args.args['1'];
- console.log(args.arsg);
- assert.deepEqual(args.args, [
- [6],
- [{
- partner_id: lines.length == 1 ? lines[0].partner_id : false,
- counterpart_aml_dicts:[{
- "analytic_tag_ids": [[6, null, []]],
- "counterpart_aml_id": 114,
- "credit": 0,
- "debit": 32.58,
- "name": "BILL/2017/0001"
- }],
- payment_aml_ids: [],
- new_aml_dicts: [],
- to_check: false,
- }]
- ], "should call process_bank_statement_line with partial reconcile values");
- }
- return this._super(route, args);
- },
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
- var widget = clientAction.widgets[0];
-
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.equal( widget.$('.cell_right .edit_amount').length, 1, "should display the edition pencil");
-
- widget = clientAction.widgets[1];
-
- await testUtils.dom.click(widget.$('.accounting_view thead td:first'));
- assert.strictEqual(widget.$('.match:first .mv_line[data-line-id=114] .cell_account_code:first()').length, 1, 'Should have line')
- await testUtils.dom.click(widget.$('.match:first .mv_line[data-line-id=114] .cell_account_code'));
-
- assert.equal( widget.$('.accounting_view tbody .cell_left .edit_amount').length, 1, "should display the edition pencil");
-
- // The partner has been set automatically, remove it.
- await testUtils.dom.click(widget.$('.o_input_dropdown input'));
- await testUtils.fields.editAndTrigger(widget.$('.o_input_dropdown input'),'',['keyup','blur']);
-
- assert.equal(clientAction.widgets[1].$('caption button:disabled:visible').length, 1,"button should be disabled");
- await testUtils.dom.click(widget.$('.accounting_view .cell_left .edit_amount'));
- assert.strictEqual(widget.$('.accounting_view .cell_left .edit_amount_input:not(.d-none)').length, 1, "should display the input field to edit amount");
- // Edit amount
- await testUtils.fields.editAndTrigger(widget.$('.accounting_view .cell_left .edit_amount_input:not(.d-none)'),'32.58',['change','blur']);
- assert.strictEqual(widget.$('.accounting_view .cell_left .line_amount').text().replace(/[\n\r\s]+/g, ' '), " $ 10000.00 $ 32.58 ", "should display previous amount and new amount")
-
- assert.strictEqual(widget.$('button.btn-primary:visible').length, 1, "should display the reconcile button");
- await testUtils.dom.click(widget.$('button.btn-primary:visible'));
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation currencies', async function (assert) {
- assert.expect(2);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: this.params.session,
- translateParameters: {
- date_format: "%m/%d/%Y",
- direction:"ltr",
- name:"English",
- thousands_sep: ",",
- time_format: "%H:%M:%S",
- decimal_point: ".",
- id:1,
- grouping: [3,0],
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
-
- assert.strictEqual(clientAction.$('.accounting_view tfoot .cell_right, .accounting_view tfoot .cell_left').text().replace(/[\n\r\s]+/g, ' '),
- "$ 1,175.00$ 32.58$ 2,000.00", "should display the different amounts with the currency");
- // await testUtils.dom.click(widget.$('.accounting_view thead .mv_line td:first'));
-
- assert.strictEqual(clientAction.$('.accounting_view tbody').text().replace(/[\n\r\s]+/g, ' ').replace(/[\u200B]/g, ''),
- " 101200 2017-02-07 INV/2017/0012 $ 650.00 ", "should display the created reconciliation line with the currency");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation change partner', async function (assert) {
- assert.expect(17);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- archs: {
- 'res.partner,false,list': '<tree string="Partners"><field name="display_name"/></tree>',
- 'res.partner,false,search': '<search string="Partners">' +
- '<field name="display_name" string="Name"/>' +
- '</search>',
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
- var widget = clientAction.widgets[0];
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Agrolait", "the partner many2one should display agrolait");
- assert.containsN(widget, '.match:first table tr', 2, "agrolait should have 2 propositions for reconciliation");
-
- // Adding the two propositions
- // This is in order to try that after changing partner the propositions are emptied
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.containsN(widget, '.accounting_view tbody tr', 2, "Both proposition should be selected");
-
- // Similate changing partner to one that does not have propositions to see if create mode is open after
- await testUtils.dom.click(widget.$('.o_input_dropdown input'));
- $('.ui-autocomplete .ui-menu-item a:contains(partner 1)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- clientAction._onAction({target: widget, name: 'change_partner', data: {data: {display_name: 'partner 1', id: 1}}, stopped: false});
- await testUtils.nextTick();
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "partner 1", "the partner many2one should display partner 1");
- assert.containsNone(widget, '.match:first table tr.mv_line', "partner 1 should have 0 propositions for reconciliation");
- assert.strictEqual(widget.$el.data('mode'), 'create', "widget should be in create mode");
-
- // Simulate changing partner
- await testUtils.dom.clickFirst(widget.$('.o_input_dropdown input'));
- $('.ui-autocomplete .ui-menu-item a:contains(Camptocamp)').trigger('mouseenter').trigger('click');
- clientAction._onAction({target: widget, name: 'change_partner', data: {data: {display_name: 'Camptocamp', id: 12}}, stopped: false});
- await testUtils.nextTick();
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display Camptocamp");
- assert.containsN(widget, '.match:first table tr.mv_line', 3, "camptocamp should have 3 propositions for reconciliation");
-
- // Simulate changing partner with SelectCreateDialog
- widget = clientAction.widgets[1];
- assert.strictEqual($('.modal').length, 0, "shouldn't have any opened modal");
- await testUtils.dom.click(widget.$('.o_input_dropdown input'));
- $('.ui-autocomplete .ui-menu-item a:contains(Search More):eq(1)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- assert.strictEqual($('.modal').length, 1, "should open a SelectCreateDialog");
- await testUtils.dom.click($('.modal table.o_list_table td:contains(Camptocamp)'));
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display Camptocamp");
-
- widget = clientAction.widgets[2];
- await testUtils.dom.click(widget.$('.accounting_view thead td:first'));
- await testUtils.dom.click(widget.$('.accounting_view .mv_line .cell_label'));
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display agrolait");
- assert.containsN(widget, '.match:first table tr', 3, "Camptocamp should have 3 propositions for reconciliation");
- assert.notOk(widget.$('.match:first div.load-more a:visible').length, "should not display the load more button");
-
- // Simulate remove partner
- await testUtils.dom.click(widget.$('.o_input_dropdown input'));
- await testUtils.fields.editAndTrigger(widget.$('.o_input_dropdown input'),'',['keyup','blur']);
-
- assert.strictEqual(widget.$('.o_input_dropdown input').val(), "", "the partner many2one should be empty");
- assert.containsN(widget, '.match:first table tr.mv_line', 5, "should have 5 propositions for reconciliation if partner is false");
- assert.ok(widget.$('.match:first div.load-more a:visible').length, "should display the load more button");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation create line', async function (assert) {
- assert.expect(23);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- assert.strictEqual(clientAction.$('.accounting_view tfoot .cell_right, .accounting_view tfoot .cell_left').text().replace(/[$, ]+/g, ''), " 1175.00 32.58 2000.00", "should display the open balance values");
-
- var widget = clientAction.widgets[0];
-
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label').text(), 'Open balance', "should display 'Open Balance' line with the rest to reconcile");
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- await testUtils.dom.click(widget.$('.create .create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(101200 Account Receivable)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
-
- assert.notOk(widget.$('.accounting_view tfoot .cell_label').text(), "should not display 'Open Balance' line because the rest to reconcile is null");
- assert.containsOnce(widget, '.accounting_view tbody tr', "should have only the created reconcile line");
- assert.strictEqual(widget.$('.accounting_view tbody tr').text().replace(/[\n\r\s$,]+/g, ' ').replace(/[\u200B]/g, ''), " 101200 New SAJ/2014/002 and SAJ/2014/003 1175.00 ",
- "the new line should have the selected account, name and amout");
- assert.ok(widget.$('caption button.btn-primary:visible').length, "should display the 'Reconcile' button");
-
- testUtils.mock.intercept(clientAction, 'do_action', function (event) {
- assert.strictEqual(JSON.stringify(event.data.action),
- '{"type":"ir.actions.act_window","res_model":"account.reconcile.model","views":[[false,"form"]],"target":"current"}',
- "should open the reconcile model form view");
- });
- await testUtils.dom.click(widget.$('.create .reconcile_model_create'),{allowInvisible:true});
-
- testUtils.mock.intercept(clientAction, 'do_action', function (event) {
- assert.strictEqual(JSON.stringify(event.data.action),
- '{"type":"ir.actions.act_window","res_model":"account.reconcile.model","views":[[false,"list"],[false,"form"]],"view_mode":"list","target":"current"}',
- "should open the reconcile model list view");
- });
- await testUtils.dom.click(widget.$('.create .reconcile_model_edit'),{allowInvisible:true});
-
- await testUtils.fields.editInput(widget.$('.create .create_amount input'), '1100.00');
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_right').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '), "$ 1100.00", "should display the value 1100.00 in right column");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_right').text(), "$ 75.00", "should display 'Open Balance' line because the rest to reconcile is 75.00");
- assert.containsOnce(widget, '.accounting_view tbody tr', "should have ever only the created reconcile line");
- assert.strictEqual(widget.$('.accounting_view tbody tr').text().replace(/[\n\r\s$,]+/g, ' ').replace(/[\u200B]/g, ''), " 101200 New SAJ/2014/002 and SAJ/2014/003 1100.00 ",
- "the new line should be update the amout");
- assert.ok(widget.$('caption button.btn-secondary:visible').length, "should display the 'validate' button");
-
- await testUtils.dom.click(widget.$('.create .add_line'),{allowInvisible:true});
- await testUtils.fields.editInput(widget.$('.create .create_amount input'), '-100');
- await testUtils.dom.click(widget.$('.create .create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(101000 Current Assets)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- await testUtils.fields.editInput(widget.$('.create .create_label input'), 'test0');
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_left:last').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '), "$ 100.00", "should display the value 100.00 in left column");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label').text(), "Open balance", "should display 'Open Balance'");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_right').text(), "$ 175.00", "should display 'Open Balance' line because the rest to reconcile is 175.00");
- assert.containsN(widget, '.accounting_view tbody tr', 2, "should have 2 created reconcile lines");
- assert.strictEqual(widget.$('.accounting_view tbody tr:eq(1)').text().replace(/[\n\r\s$,]+/g, ' ').replace(/[\u200B]/g, ''), " 101000 New test0 100.00 ",
- "the new line should have the selected account, name and amout");
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
-
- assert.strictEqual(widget.$('.create .create_amount input').val(), "175.00", "should have '175.00' as default amount value");
-
- await testUtils.fields.editInput(widget.$('.create .create_amount input'), '200');
- widget.$('.create .create_account_id input').trigger('click');
- $('.ui-autocomplete .ui-menu-item a:contains(101000 Current Assets)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- await testUtils.fields.editInput(widget.$('.create .create_label input'), 'test1');
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_right:last').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '), "$ 200.00", "should display the value 200.00 in left column");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label').text(), "Open balance", "should display 'Open balance'");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_left').text(), "$ 25.00", "should display 'Open balance' with 25.00 in left column");
- assert.containsN(widget, '.accounting_view tbody tr', 3, "should have 3 created reconcile lines");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation create line (many2one test)', async function (assert) {
- assert.expect(5);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- var def = testUtils.makeTestPromise();
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- "account.account,false,list": '<tree string="Account"><field name="code"/><field name="name"/></tree>',
- "account.account,false,search": '<search string="Account"><field name="code"/></search>',
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- mockRPC: function (route, args) {
- if (args.method === 'name_get') {
- return def.then(this._super.bind(this, route, args));
- }
- return this._super(route, args);
- },
- });
-
- await clientAction.prependTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
-
- // open the first line in write-off mode
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
-
- // select an account with the many2one (drop down)
- await testUtils.dom.click(widget.$('.create .create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(101200)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- assert.strictEqual(widget.$('.create .create_account_id input').val(), "101200 Account Receivable", "Display the selected account");
- assert.strictEqual(widget.$('tbody:first .cell_account_code').text().replace(/[\u200B]/g, ''), "101200", "Display the code of the selected account");
-
- // use the many2one select dialog to change the account
- await testUtils.dom.click(widget.$('.create .create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(Search)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- // select the account who does not appear in the drop drown
- await testUtils.dom.click($('.modal tr.o_data_row:contains(502)'));
- assert.strictEqual(widget.$('.create .create_account_id input').val(), "101200 Account Receivable", "Selected account does not change");
- // wait the name_get to render the changes
- def.resolve();
- await testUtils.nextTick();
- assert.strictEqual(widget.$('.create .create_account_id input').val(), "502 Account", "Display the selected account");
- assert.strictEqual(widget.$('tbody:first .cell_account_code').text().replace(/[\u200B]/g, ''), "502", "Display the code of the selected account");
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation create line with taxes', async function (assert) {
- assert.expect(13);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- await testUtils.dom.click(widget.$('.create .create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(101000 Current Assets)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
- await testUtils.fields.editInput(widget.$('.create .create_label input'), 'test1');
- await testUtils.fields.editInput(widget.$('.create .create_amount input'), '1100');
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_right:last').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '), "$ 1100.00", "should display the value 1100.00 in left column");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label').text(), "Open balance", "should display 'Open Balance'");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_right').text(), "$\u00a075.00", "should display 'Open Balance' with 75.00 in right column");
- assert.containsOnce(widget, '.accounting_view tbody tr', "should have 1 created reconcile lines");
-
- await testUtils.dom.click(widget.$('.create .create_tax_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(10.00%)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_right').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '), "$ 1000.00 $ 100.00", "should have 2 created reconcile lines with right column values");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label').text(), "Open balance", "should display 'Open Balance'");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_right').text(), "$\u00a075.00", "should display 'Open Balance' with 75.00 in right column");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_left').text(), "", "should display 'Open Balance' without any value in left column");
- assert.containsN(widget, '.accounting_view tbody tr', 2, "should have 2 created reconcile lines");
- await testUtils.dom.click(widget.$('[name="tax_ids"] a.o_delete'));
- widget.$('.create .create_tax_id input').val('').trigger('keyup').trigger('blur');
- await testUtils.dom.click(widget.$('.create .create_tax_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(20.00%)').trigger('mouseenter').trigger('click');
- await testUtils.nextTick();
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_right').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '), "$ 1100.00 $ 220.00", "should have 2 created reconcile lines with right column values");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label').text(), "Open balance", "should display 'Open balance'");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_left').text(), "$\u00a0145.00", "should display 'Open balance' with 145.00 in right column");
- assert.containsN(widget, '.accounting_view tbody tr', 2, "should have 2 created reconcile lines");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation create line from reconciliation model', async function (assert) {
- assert.expect(6);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- await testUtils.dom.click(widget.$('.create .quick_add button:contains(ATOS)'));
-
- assert.strictEqual(widget.$('.accounting_view tbody .cell_label, .accounting_view tbody .cell_right').text().replace(/[\n\r\s$,]+/g, ' '),
- " ATOS Banque 1145.63 ATOS Banque Tax 20.00% 229.13 ATOS Frais 26.78 ATOS Frais Tax 10.00% include 2.68 ", "should display 4 lines");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label, .accounting_view tfoot .cell_left').text().replace(/[\n\r\s$,]+/g, ''),
- "Openbalance229.22", "should display the 'Open balance' line with value in left column");
-
- await testUtils.fields.editAndTrigger(widget.$('.create .create_amount input'), '100',['input']);
-
- assert.strictEqual(widget.$('.accounting_view tbody').text().replace(/[\n\r\s$,]+/g, ' ').replace(/[\u200B]/g, ''),
- " 101120 New ATOS Banque 1145.63 101120 New ATOS Banque Tax 20.00% 229.13 101130 New ATOS Frais 90.91 101300 New ATOS Frais Tax 10.00% include 9.09 ",
- "should update the value of the 2 lines (because the line + its tax must have 100% of the value)");
- assert.strictEqual(widget.$('.accounting_view tfoot .cell_label, .accounting_view tfoot .cell_left').text().replace(/[\n\r\s$,]+/g, ''),
- "Openbalance299.76", "should change the 'Open balance' line because the 20.00% tax is not an include tax");
-
- await testUtils.dom.click(widget.$('.accounting_view tbody .cell_account_code:first'));
- await testUtils.dom.click(widget.$('.accounting_view tbody .cell_label:first'));
-
- assert.strictEqual(widget.$('.accounting_view tbody').text().replace(/[\n\r\s$,]+/g, ' '), "", "should removed every line");
-
- await testUtils.dom.click(widget.$('.create .quick_add button:contains(Double)'));
-
- assert.strictEqual(widget.$('.accounting_view tbody').text().replace(/[\n\r\s$,]+/g, ' ').replace(/[\u200B]/g, ''),
- " 101120 New Double Banque 1145.63 101130 New Double Frais 29.37 ",
- "should have a sum of reconciliation proposition amounts equal to the line amount");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation fetch correct reconciliation models', async function (assert) {
- assert.expect(1);
-
- testUtilsMock.patch(this.params.options.context, {
- active_model: 'account.journal', // On account dashboard, click "Reconcile" on a journal
- active_ids: [1,2], // Active journals
- company_ids: [3,4], // Active companies
- });
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.addMockEnvironment(clientAction, {
- data: this.params.data,
- mockRPC: async function (route, args) {
- if (args.model === 'account.reconcile.model' && args.method === 'search_read') {
- assert.deepEqual(
- args.kwargs.domain,
- [['company_id', 'in', [3,4]], ['match_journal_ids', 'in', [false, 1, 2]]],
- 'The domain to get reconcile models should contain the right fields and values'
- );
- }
- return this._super.apply(this, arguments);
- }
- });
- clientAction.appendTo($('#qunit-fixture'));
- testUtilsMock.unpatch(this.params.options.context);
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation manual', async function (assert) {
- assert.expect(13);
-
- var clientAction = new ReconciliationClientAction.ManualAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: this.params.session,
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- assert.strictEqual(clientAction.$('.accounting_view:first thead').text().replace(/[\n\r\s]+/g, ' '),
- " 101000 Current AssetsLast Reconciliation: 2017-02-16 101000 ",
- "should display the account as title");
- assert.strictEqual(clientAction.$('.o_reconciliation_line:first').data('mode'), "inactive", "should be in 'inactive' mode because no line to displayed and the balance amount is null");
- assert.containsN(clientAction, '.accounting_view:first tbody tr', 2, "should have 2 propositions");
- assert.containsOnce(clientAction, '.accounting_view:first .o_reconcile:visible', "should display the reconcile button");
-
- await testUtils.dom.click(clientAction.$('.accounting_view:first .o_reconcile:visible'));
-
- assert.strictEqual(clientAction.$('.accounting_view:first thead').text().replace(/[\n\r\s]+/g, ' '),
- " 101200 Account Receivable 101200 ",
- "should display the account and the account code as title");
-
- assert.strictEqual(clientAction.$('.o_reconciliation_line:first .match:first tr:first .cell_right').text().trim().replace(/[\n\r\s\u00a0]+/g, ' '),
- "$ 11,000.00", "sould display the line in $");
- assert.strictEqual(clientAction.$('.o_reconciliation_line:first .match:first tr:first .cell_right .o_multi_currency').data('content'),
- "10,222.00 €", "sould display the monetary information in €");
-
- assert.containsOnce(clientAction, '.accounting_view:first .o_no_valid:visible', "should display the skip button");
-
- await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) .accounting_view'))
- await testUtils.dom.click(clientAction.$('.accounting_view:eq(1) thead td:first'));
- await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) [data-line-id="21"] .cell_label'));
- await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) [data-line-id="22"] .cell_label'));
-
- assert.strictEqual(clientAction.$('.o_reconciliation_line:eq(1) tfoot tr').length, 0, "should not display the 'Write-off' line because the balance is null in Euro");
- assert.strictEqual(clientAction.$('.o_reconciliation_line:eq(1) .o_reconcile:visible').length, 1, "should display 'Reconcile' button in green");
-
- await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) .o_reconcile:visible'));
-
- assert.containsOnce(clientAction, '.o_reconciliation_line[data-mode!="inactive"]', "should have only one line open");
-
- await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) [data-line-id="23"] .cell_label'));
- await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) [data-line-id="24"] .cell_label'));
-
- assert.strictEqual(clientAction.$('.o_reconciliation_line:eq(1) tfoot tr').length, 1, "should display the 'Write-off' line because the balance is not null in Euro");
- assert.strictEqual(clientAction.$('.o_reconciliation_line:eq(1) .o_validate:visible').length, 1, "should display 'Reconcile' button");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation: Payment < inv1 + inv2(partial)', async function (assert) {
- assert.expect(3);
-
- /*
- * One payment: $1175
- * Two Invoices
- * The first invoice will be fully reconciled $650
- * The second invoice will be partially paid with the rest of the payment $999
- */
-
- // modify the second line that is already in db to put it at $999
- var indexModif = _.findIndex(this.params.mv_lines['[5,"",0]'], function (line) {return line.id === 112});
- this.params.mv_lines['[5,"",0]'][indexModif] =
- {account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
- total_amount_str: "$ 999.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0003",
- partner_name: "Agrolait", total_amount_currency_str: "", id: 112, credit: 0.0, journal_id: [1, "Customer Invoices"],
- amount_str: "$ 999.00", debit: 999.0, account_code: "101200", ref: "", already_paid: false};
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- mockRPC: function (route, args) {
- if (args.method === 'process_bank_statement_line') {
- assert.deepEqual(args.args,
- [
- [5], // Id of the bank statement line
-
- [{counterpart_aml_dicts:
- [{name:"INV/2017/0002",
- debit: 0,
- credit: 650,
- analytic_tag_ids: [[6, null, []]],
- counterpart_aml_id: 109},
-
- {name: "INV/2017/0003",
- debit: 0,
- credit: 525,
- analytic_tag_ids: [[6, null, []]],
- counterpart_aml_id: 112}],
-
- payment_aml_ids: [],
- partner_id: 8,
- to_check: false,
- new_aml_dicts: []}]
- ], "should call process_bank_statement_line with partial reconcile values");
- }
- return this._super(route, args);
- },
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- // The first reconciliation "line" is where it happens
- var widget = clientAction.widgets[0];
-
- // Add first invoice to reconcile fully
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.ok( widget.$('.cell_right .edit_amount').length,
- "should display the pencil to edit amount");
-
- // Add second invoice to reconcile partially
- await testUtils.dom.click( widget.$('.match:first .cell_account_code:first'));
-
- // Edit amount on last invoice
- await testUtils.dom.click(widget.$('.edit_amount:last()'));
- await testUtils.fields.editAndTrigger(widget.$('.edit_amount_input:last()'),'525',['blur']);
-
- var $buttonReconcile = widget.$('button.o_reconcile:not(hidden)');
-
- assert.equal($buttonReconcile.length, 1,
- 'The reconcile button must be visible');
-
- await testUtils.dom.click($buttonReconcile);
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation: payment and 2 partials', async function (assert) {
- assert.expect(6);
-
- /*
- * One payment: $1175
- * Two Invoices as Inv1 = 1200; Inv2 = 1200:
- * Payment < Inv1 AND Payment < Inv2
- * No partial reconcile is possible, as a write-off of 1225 is necessary
- */
-
- // modify the invoice line to have their amount > payment
- var indexInv1 = _.findIndex(this.params.mv_lines['[5,"",0]'], function (line) {return line.id === 109});
- this.params.mv_lines['[5,"",0]'][indexInv1] =
- {account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
- total_amount_str: "$ 1200.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0002", partner_name: "Agrolait",
- total_amount_currency_str: "", id: 109, credit: 0.0, journal_id: [1, "Customer Invoices"], amount_str: "$ 1200.00", debit: 1200.0,
- account_code: "101200", ref: "", already_paid: false};
-
- var indexInv2 = _.findIndex(this.params.mv_lines['[5,"",0]'], function (line) {return line.id === 112});
- this.params.mv_lines['[5,"",0]'][indexInv2] =
- {account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
- total_amount_str: "$ 1200.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0003",
- partner_name: "Agrolait", total_amount_currency_str: "", id: 112, credit: 0.0, journal_id: [1, "Customer Invoices"],
- amount_str: "$ 1200.00", debit: 1200.0, account_code: "101200", ref: "", already_paid: false};
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- mockRPC: function (route, args) {
- if (args.method === 'process_bank_statement_line') {
- assert.deepEqual(args.args,
- [
- [5], // Id of the bank statement line
-
- [{counterpart_aml_dicts:
- [{name:"INV/2017/0002",
- debit: 0,
- credit: 1200,
- analytic_tag_ids: [[6, null, []]],
- counterpart_aml_id: 109},
-
- {name: "INV/2017/0003",
- debit: 0,
- credit: 1200,
- analytic_tag_ids: [[6, null, []]],
- counterpart_aml_id: 112}],
-
- payment_aml_ids: [],
- partner_id: 8,
- to_check: false,
- new_aml_dicts: [
- {account_id: 282,
- credit: 0,
- debit: 1225,
- analytic_tag_ids: [[6, null, []]],
- name: 'SAJ/2014/002 and SAJ/2014/003',
- }
- ]}]
- ], "should call process_bank_statement_line with new aml dict reconcile values");
- }
- return this._super(route, args);
- },
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- // The first reconciliation "line" is where it happens
- var widget = clientAction.widgets[0];
-
- // Add first invoice
- // There should be the opportunity to reconcile partially
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.ok(widget.$('.cell_right .edit_amount').length,
- "should display the pencil to edit amount");
-
- // Add second invoice
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.ok(widget.$('.cell_right .edit_amount').length,
- "should display the pencil to edit amount");
-
- 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(), -1225,
- 'The right amount should be proposed for the write-off');
-
- await testUtils.dom.click(writeOffCreate.find('.create_account_id input.ui-autocomplete-input'));
- await testUtils.dom.click($('ul.ui-autocomplete li a:first'));
-
- var $buttonReconcile = widget.$('button.o_reconcile:not(hidden)');
-
- assert.equal($buttonReconcile.length, 1,
- 'The reconcile button must be visible');
-
- await testUtils.dom.click($buttonReconcile);
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation: partial payment of 2 invoices with one payment [REQUIRE FOCUS]', async function (assert) {
- assert.expect(4);
-
- /*
- * One payment: $1175
- * Two Invoices as Inv1 = 1200; Inv2 = 1200:
- * Payment < Inv1 AND Payment < Inv2
- * Assign 500 to inv1 and 675 to inv2
- */
-
- // modify the invoice line to have their amount > payment
- var indexInv1 = _.findIndex(this.params.mv_lines['[5,"",0]'], function (line) {return line.id === 109});
- this.params.mv_lines['[5,"",0]'][indexInv1] =
- {account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
- total_amount_str: "$ 1200.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0002", partner_name: "Agrolait",
- total_amount_currency_str: "", id: 109, credit: 0.0, journal_id: [1, "Customer Invoices"], amount_str: "$ 1200.00", debit: 1200.0,
- account_code: "101200", ref: "", already_paid: false};
-
- var indexInv2 = _.findIndex(this.params.mv_lines['[5,"",0]'], function (line) {return line.id === 112});
- this.params.mv_lines['[5,"",0]'][indexInv2] =
- {account_type: "receivable", amount_currency_str: "", currency_id: false, date_maturity: "2017-02-07", date: "2017-01-08",
- total_amount_str: "$ 1200.00", partner_id: 8, account_name: "101200 Account Receivable", name: "INV/2017/0003",
- partner_name: "Agrolait", total_amount_currency_str: "", id: 112, credit: 0.0, journal_id: [1, "Customer Invoices"],
- amount_str: "$ 1200.00", debit: 1200.0, account_code: "101200", ref: "", already_paid: false};
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- mockRPC: function (route, args) {
- if (args.method === 'process_bank_statement_line') {
- assert.deepEqual(args.args,
- [
- [5], // Id of the bank statement line
-
- [{counterpart_aml_dicts:
- [{name:"INV/2017/0002",
- debit: 0,
- credit: 500,
- analytic_tag_ids: [[6, null, []]],
- counterpart_aml_id: 109},
-
- {name: "INV/2017/0003",
- debit: 0,
- credit: 675,
- analytic_tag_ids: [[6, null, []]],
- counterpart_aml_id: 112}],
-
- payment_aml_ids: [],
- partner_id: 8,
- to_check: false,
- new_aml_dicts: []
- }]
- ], "should call process_bank_statement_line with correct counterpart_aml_dicts");
- }
- return this._super(route, args);
- },
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- // The first reconciliation "line" is where it happens
- var widget = clientAction.widgets[0];
-
- // Add first invoice
- // There should be the opportunity to reconcile partially
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.ok(widget.$('.cell_right .edit_amount').length,
- "should display the pencil to edit amount");
-
- // Add second invoice
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.ok(widget.$('.cell_right .edit_amount').length,
- "should display the pencil to edit amount");
-
- // Edit invoice first amount
- await testUtils.dom.click(widget.$('.edit_amount:first()'));
- await testUtils.fields.editAndTrigger(widget.$('.edit_amount_input:first()'),'500',['blur']);
- // Edit invoice second amount
- var $buttonReconcile = widget.$('button.o_reconcile:not(hidden)');
- await testUtils.dom.click(widget.$('.edit_amount:last()'));
- await testUtils.fields.editAndTrigger(widget.$('.edit_amount_input:last()'),'675',['blur']);
-
- assert.equal($buttonReconcile.length, 1,
- 'The reconcile button must be visible');
-
- await testUtils.dom.click($buttonReconcile,{allowInvisible:true});
-
- clientAction.destroy();
- });
-
- QUnit.test('Manual Reconciliation: remove a prop to attain balance and reconcile', async 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,[282,283,284,285,286,287,288,308,499,500,501,502,503,504]]']);
- 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.mock.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: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- // The first reconciliation "line" is where it happens
- var widget = clientAction.widgets[0];
-
- // Add first prop
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- assert.equal( widget.$('.cell_right .edit_amount').length, 0, "should not display the pencil to edit amount");
-
- // Add second prop
- await testUtils.dom.click(widget.$('.match:first .cell_account_code:first'));
- // Check that a create form is here
-
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- 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
- await testUtils.dom.click(widget.$('tr[data-line-id="999"] td:first'));
-
- var $buttonReconcile = widget.$('button.o_reconcile:visible');
- assert.equal($buttonReconcile.length, 1,
- 'The reconcile button must be visible');
-
- await testUtils.dom.click($buttonReconcile);
-
- clientAction.destroy();
- });
-
- QUnit.test('Manual Reconciliation: No lines for account', async function (assert) {
- assert.expect(2);
-
- var clientAction = new ReconciliationClientAction.ManualAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- // 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');
-
- await testUtils.dom.click(emptyLine.find('td:first'));
-
- // 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('Tax on account receivable', async function(assert){
- assert.expect(21);
-
- this.params.data_for_manual_reconciliation_widget['[null,[282,283,284,285,286,287,288,308,499,500,501,502,503,504]]'].accounts = [];
- var clientAction = new ReconciliationClientAction.ManualAction(null, this.params.options);
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {},
- mockRPC: function(route, args) {
- if (args.method === "name_search") {
- switch (args.model) {
- // mock the default mock to do the minimal processing required
- // to get the available values for the droplists.
- case 'account.account':
- assert.step("Account");
- return Promise.resolve(
- _.map(this.data[args.model].records, function (record) {
- return [record.id, record.name];
- })
- );
- case 'account.tax':
- assert.step("Tax");
- return Promise.resolve(
- _.map(this.data[args.model].records, function (record) {
- return [record.id, record.display_name];
- })
- );
- case 'account.journal':
- assert.step("Journal");
- return Promise.resolve(
- _.map(this.data[args.model].records, function (record) {
- return [record.id, record.display_name];
- })
- );
- }
- }
- if (args.method === 'process_move_lines') {
- var mv_line_ids = args.args[0][0].mv_line_ids.slice(0);
- mv_line_ids.sort(function(a, b) {return a - b});
- assert.deepEqual(mv_line_ids, [6, 19, 21],
- "Reconciliation rpc payload, mv_line_ids are correct");
-
- // Index aiming at the correct object in the list
- var idx = _.has(args.args[0][0].new_mv_line_dicts[0], 'journal_id') ? 0 : 1;
- assert.deepEqual(
- _.pick(args.args[0][0].new_mv_line_dicts[idx],
- 'account_id', 'name', 'credit', 'debit', 'journal_id'),
- {account_id: 287, name: "dummy text", credit: 0, debit: 180, journal_id: 8},
- "Reconciliation rpc payload, new_mv_line_dicts.gift is correct"
- );
- assert.deepEqual(
- _.pick(args.args[0][0].new_mv_line_dicts[1 - idx],
- 'account_id', 'name', 'credit', 'debit', 'tax_line_id'),
- {account_id: 287, name: "Tax 20.00%", credit: 0, debit: 36, tax_line_id: 6},
- "Reconciliation rpc payload, new_mv_line_dicts.tax is correct"
- );
- }
- return this._super.apply(this, arguments);
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- var widget = clientAction.widgets[0];
-
- // Select invoice of 1k$, payment of 1k$ and payment of 180$
- var $tableToReconcile = widget.$('.match');
- var defs = _.map([6, 19, 21], function(id) {
- return testUtils.dom.click($tableToReconcile.find('tr.mv_line[data-line-id='+id+']:first td:first-child'));
- });
- await Promise.all(defs);
- assert.verifySteps([], "No rpc done");
-
- // Store the money in excess to the "account receivable" account with 20% taxes
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
- var $reconcileForm = widget.$(".create");
- await testUtils.dom.click($reconcileForm.find('.create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(101200 Account Receivable)').trigger('mouseover').trigger('click');
- await testUtils.nextTick();
- assert.verifySteps(["Account"], "Account rpc done");
-
- await testUtils.dom.click($reconcileForm.find('.create_tax_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(Tax 20.00%)').trigger('mouseover').trigger('click');
- await testUtils.nextTick();
- assert.verifySteps(["Tax"], "Tax rpc done");
-
- await testUtils.dom.click($reconcileForm.find('.create_journal_id input'),{allowInvisible:true});
- $('.ui-autocomplete .ui-menu-item a:contains(company 1 journal)').trigger('mouseover').trigger('click');
- await testUtils.nextTick();
- await testUtils.fields.editAndTrigger($reconcileForm.find('.create_label input'),'dummy text','input');
- await testUtils.dom.click($reconcileForm.find('.create_label input'));
- assert.verifySteps(["Journal"], "Journal rpc done");
-
- // Verify the two (gift + tax) lines were added to the list
- var $newLines = widget.$('tr.mv_line[data-line-id^=createLine]');
- var idx = ($($($newLines[0]).find("td")[3]).text().trim() === "dummy text") ? 0 : 1;
-
- var $newLineGiftTds = $($newLines[1 - idx]).find("td");
- assert.equal($($newLineGiftTds[0]).text().trim().replace(/[\u200B]/g, ''), "101200",
- "Gift line account number is valid");
- assert.equal($($newLineGiftTds[1]).text().trim(), "New",
- "Gift line is flagged as new");
- assert.equal($($newLineGiftTds[2]).text().trim(), "dummy text",
- "Gift line has the correct label");
- assert.equal($($newLineGiftTds[3]).text().trim(), "180.00",
- "Gift line has the correct left amount");
- assert.equal($($newLineGiftTds[4]).text().trim(), "",
- "Gift line has the correct right amount");
-
- var $newLineTaxeTds = $($newLines[idx]).find("td");
- assert.equal($($newLineTaxeTds[0]).text().trim().replace(/[\u200B]/g, ''), "101200",
- "Tax line account number is valid");
- assert.equal($($newLineTaxeTds[1]).text().trim(), "New",
- "Tax line is flagged as new");
- assert.equal($($newLineTaxeTds[2]).text().trim(), "Tax 20.00%",
- "Tax line has the correct label");
- assert.equal($($newLineTaxeTds[3]).text().trim(), "36.00",
- "Tax line has the correct left amount");
- assert.equal($($newLineTaxeTds[4]).text().trim(), "",
- "Tax line has the correct right amount");
-
- // Reconcile
- await testUtils.dom.click(widget.$("button.o_reconcile.btn.btn-primary:first"));
- assert.ok(true, "No error in reconciliation");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconcile temporarily and ask to check', async function(assert){
- assert.expect(4);
- this.params.options.context['to_check'] = true;
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
-
- testUtils.mock.addMockEnvironment(clientAction, {
- data: this.params.data,
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
- await clientAction.appendTo($('#qunit-fixture'));
- var widget = clientAction.widgets[0];
-
- // Add a line as proposition
- // open the first line
- await testUtils.nextTick();
- await testUtils.dom.click(widget.$('.accounting_view tfoot td.cell_label'));
- await testUtils.dom.click(widget.$('.o_notebook .nav-link[href*="notebook_page_create"]'));
-
- var $reconcileForm = widget.$(".create");
- $reconcileForm.find('.create_account_id input').val('499001 Suspense Account').keydown().keyup();
- await testUtils.dom.click($reconcileForm.find('.create_account_id input'));
- $('.ui-autocomplete .ui-menu-item a:contains(499001 Suspense Account)')
- .trigger('mouseover')
- .trigger('click');
- await testUtils.nextTick();
-
- assert.equal($("button.o_validate.btn.btn-secondary.text-warning:first").length, 0, "should not display reconcile button in orange");
- await testUtils.dom.click($reconcileForm.find('.create_to_check input'));
- assert.equal($("button.o_validate.btn.btn-secondary.text-warning:first").length, 1, "should display reconcile button in orange");
-
- testUtils.mock.intercept(clientAction, 'call_service', function (event) {
- assert.deepEqual(event.data.args[1].args,
- [[5],[{partner_id: 8,
- counterpart_aml_dicts: [],
- payment_aml_ids: [],
- new_aml_dicts: [{account_id: 499,
- credit: 1175,
- debit: 0,
- analytic_tag_ids: [[6, null, []]],
- name: 'SAJ/2014/002 and SAJ/2014/003',
- }],
- to_check: true,
- }]],
- "Should call process_bank_statement_line with to_check set to true");
- var def = testUtils.makeTestPromise();
- def.abort = function () {};
- event.data.callback(def);
- });
-
- await testUtils.dom.click(widget.$("button.o_validate.btn.btn-secondary:first"));
- assert.ok(true, "No error in reconciliation");
-
- clientAction.destroy();
- });
-
- QUnit.test('Reconciliation Models handle analytic tags', async function (assert) {
- assert.expect(6);
-
- var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options);
- testUtils.addMockEnvironment(clientAction, {
- data: this.params.data,
- mockRPC: function (route, args) {
- if (args.method === 'process_bank_statement_line') {
- var new_aml_dicts = args.args[1][0].new_aml_dicts;
- assert.strictEqual(new_aml_dicts.length, 2);
- // I personnally judge the following use case rotten, since
- // the first and the second line wouldn't have the same tags
- assert.deepEqual(new_aml_dicts[0].analytic_tag_ids, [[6, null, [1, 2]]]);
- assert.deepEqual(new_aml_dicts[1].analytic_tag_ids, [[6, null, [2]]]);
- }
- return this._super(route, args);
- },
- session: {
- currencies: {
- 3: {
- digits: [69, 2],
- position: "before",
- symbol: "$"
- }
- }
- },
- archs: {
- 'account.bank.statement.line,false,search': '<search string="Statement Line"><field name="display_name"/></search>',
- },
- });
-
- await clientAction.appendTo($('#qunit-fixture'));
- await testUtils.nextTick();
-
- // The first reconciliation "line" is where it happens
- var widget = clientAction.widgets[0];
-
- await testUtilsDom.click(widget.$('.nav-create:visible'));
- await testUtilsDom.click(widget.$('.quick_add button:contains("Double")'));
- assert.containsN(widget, '.create_analytic_tag_ids .o_field_many2manytags .badge', 2,
- 'Two tags are loaded');
- assert.containsOnce(widget, '.create_analytic_tag_ids .o_field_many2manytags .badge:contains("Come together")',
- 'Tags should have a name');
- assert.containsOnce(widget, '.create_analytic_tag_ids .o_field_many2manytags .badge:contains("Right now")',
- 'Tags should have a name');
-
- await testUtilsDom.click(widget.$('.create_analytic_tag_ids .o_field_many2manytags .badge a.o_delete:first()'));
-
- await testUtilsDom.click(widget.$('.o_reconcile:visible'));
-
- clientAction.destroy();
- });
-});
-});
diff --git a/addons/account/static/tests/tours/reconciliation.js b/addons/account/static/tests/tours/reconciliation.js
deleted file mode 100644
index f100e0d1a0e..00000000000
--- a/addons/account/static/tests/tours/reconciliation.js
+++ /dev/null
@@ -1,108 +0,0 @@
-odoo.define('account.tour_bank_statement_reconciliation', function(require) {
-'use strict';
-
-var core = require('web.core');
-var rpc = require('web.rpc');
-var Tour = require('web_tour.tour');
-
-Tour.register('bank_statement_reconciliation', {
- test: true,
- // Go to the reconciliation page of the statement: "BNK/2014/001"
- }, [
- // Reconciliation of 'INV/2018/0002'
- // Click on reconcile (matching done automatically by the reconciliation rule).
-
- {
- content: "reconcile the line",
- trigger: '.o_reconciliation_line:nth-child(1) .o_reconcile:visible',
- },
-
- // Reconciliation of 'First 2000 $ of INV/2018/0001'
- // Make a partial reconciliation
-
- {
- content: "open the 4th line in match_rp mode to test the partial reconciliation",
- extra_trigger: '.o_reconciliation_line:first[data-mode="match_rp"]',
- trigger: '.o_reconciliation_line:nth-child(4) .cell_label:contains("First")'
- },
- {
- content: "click on partial reconcile",
- trigger: '.o_reconciliation_line:nth-child(4) .accounting_view .edit_amount',
- },
- {
- content: "Edit amount",
- trigger: '.o_reconciliation_line:nth-child(4) .accounting_view .edit_amount_input:not(.d-none)',
- run: 'text 2000'
- },
- {
- content: "Press enter to validate amount",
- trigger: '.o_reconciliation_line:nth-child(4) .accounting_view .edit_amount_input:not(.d-none)',
- run: 'keydown 13' // requires focus
- },
- {
- content: "Check that amount has changed",
- trigger: '.o_reconciliation_line:nth-child(4) .accounting_view .line_amount:contains("2,000.00")'
- },
- {
- content: "reconcile the line",
- trigger: '.o_reconciliation_line:nth-child(4) .o_reconcile:visible',
- },
-
- // Reconciliation of 'Prepayment'
- // Test changing the partner
-
- {
- content: "change the partner of the second line",
- trigger: '.o_reconciliation_line:nth-child(2) .o_field_many2one[name="partner_id"] input',
- run: 'text Deco'
- },
- {
- content: "select Deco Addict ",
- extra_trigger: '.ui-autocomplete:visible li:eq(1):contains(Create "Deco")',
- trigger: '.ui-autocomplete:visible li:contains(Deco Addict)',
- },
- {
- content: "use filter",
- trigger: '.o_reconciliation_line:nth-child(1) .match .match_controls .filter',
- run: 'text 4610'
- },
- {
- content: "select a line linked to Deco Addict ",
- extra_trigger: ".o_reconciliation_line:nth-child(1) .match .line_info_button:last[data-content*='4,610']",
- trigger: ".o_reconciliation_line:nth-child(1) .match .line_info_button[data-content*='Deco Addict']"
- },
- {
- content: "deselect the line",
- trigger: '.o_reconciliation_line:nth-child(1) .accounting_view tbody .cell_label:first',
- run: function() {
- $('.o_reconciliation_line:nth-child(1) .accounting_view tbody .cell_label:first').trigger('click');
- }
- },
- {
- content: "create a write-off",
- extra_trigger: '.o_reconciliation_line:nth-child(2) .accounting_view tfoot .cell_left:visible:contains(32.58)',
- trigger: '.o_reconciliation_line:nth-child(2) .o_notebook .nav-link[href*="notebook_page_create"]'
- },
- {
- content: "enter an account",
- trigger: '.o_reconciliation_line:nth-child(2) .o_field_many2one[name="account_id"] input',
- run: 'text 151000'
- },
- {
- content: "select the first account",
- trigger: '.ui-autocomplete:visible li:last:contains(151000)',
- },
- {
- content: "reconcile the line with the write-off",
- trigger: '.o_reconciliation_line:nth-child(2) .o_reconcile:visible',
- },
-
- // Be done
- {
- content: "check the number off validate lines",
- trigger: '.o_control_panel .progress-reconciliation:contains(3 / 6)'
- },
- ]
-);
-
-});