summaryrefslogtreecommitdiff
path: root/base_accounting_kit/static/src/js/payment_render.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 17:14:58 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 17:14:58 +0700
commit1ca3b3df3421961caec3b747a364071c80f5c7da (patch)
tree6778a1f0f3f9b4c6e26d6d87ccde16e24da6c9d6 /base_accounting_kit/static/src/js/payment_render.js
parentb57188be371d36d96caac4b8d65a40745c0e972c (diff)
initial commit
Diffstat (limited to 'base_accounting_kit/static/src/js/payment_render.js')
-rw-r--r--base_accounting_kit/static/src/js/payment_render.js929
1 files changed, 929 insertions, 0 deletions
diff --git a/base_accounting_kit/static/src/js/payment_render.js b/base_accounting_kit/static/src/js/payment_render.js
new file mode 100644
index 0000000..96266ce
--- /dev/null
+++ b/base_accounting_kit/static/src/js/payment_render.js
@@ -0,0 +1,929 @@
+odoo.define('base_accounting_kit.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'));
+ });
+ var def3 = session.user_has_group('analytic.group_analytic_tags').then(function(has_group) {
+ self.group_tags = has_group;
+ });
+ var def4 = session.user_has_group('analytic.group_analytic_accounting').then(function(has_group) {
+ self.group_acc = has_group;
+ });
+ $('<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, def3, def4]);
+ },
+
+ //--------------------------------------------------------------------------
+ // 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 || to_check_checked);
+ 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 = state['remaining_' + matching_modes[i]];
+ 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);
+ $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], ['deprecated', '=', false]],
+ }, {
+ 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"),
+ },
+ 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, 'group_tags': self.group_tags, 'group_acc': self.group_acc}));
+ 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');
+ },
+ /**
+ * @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,
+};
+});