diff options
Diffstat (limited to 'addons/account/static/src')
5 files changed, 147 insertions, 124 deletions
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js index 88668a2da97..bdca26fa15f 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_action.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js @@ -7,6 +7,7 @@ var ReconciliationRenderer = require('account.ReconciliationRenderer'); var ControlPanelMixin = require('web.ControlPanelMixin'); var Widget = require('web.Widget'); var core = require('web.core'); +var _t = core._t; /** @@ -109,7 +110,18 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, { this.renderer.prependTo(self.$('.o_form_sheet')); this._renderLines(); - this._openFirstLine(); + + // No more lines to reconcile, trigger the rainbowman. + var initialState = this.renderer._initialState; + if(initialState.valuenow === initialState.valuemax){ + initialState.context = this.model.getContext(); + this.renderer.showRainbowMan(initialState); + }else{ + // Create a notification if some lines has been reconciled automatically. + if(initialState.valuenow > 0) + this.renderer._renderNotifications(this.model.statement.notifications); + this._openFirstLine(); + } }, /** diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js index e8a6838a139..dcc93343189 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_model.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js @@ -358,13 +358,13 @@ var StatementModel = BasicModel.extend({ .then(function (statement) { self.statement = statement; self.bank_statement_id = statement_ids.length === 1 ? {id: statement_ids[0], display_name: statement.statement_name} : false; - self.valuenow = 0; - self.valuemax = statement.st_lines_ids.length; + self.valuenow = statement.value_min; + self.valuemax = statement.value_max; self.context.journal_id = statement.journal_id; - _.each(statement.st_lines_ids, function (id) { + _.each(statement.lines, function (res) { var handle = _.uniqueId('rline'); self.lines[handle] = { - id: id, + id: res.st_line.id, handle: handle, reconciled: false, mode: 'inactive', @@ -412,7 +412,7 @@ var StatementModel = BasicModel.extend({ var ids = _.pluck(self.lines, 'id'); ids = ids.splice(0, self.defaultDisplayQty); self.pagerIndex = ids.length; - return self.loadData(ids, []); + return self._formatLine(self.statement.lines); }); }, /** @@ -445,7 +445,9 @@ var StatementModel = BasicModel.extend({ args: [ids, excluded_ids], context: self.context, }) - .then(self._formatLine.bind(self)); + .then(function(res){ + return self._formatLine(res['lines']); + }) }, /** * Add lines into the propositions from the reconcile model @@ -545,24 +547,14 @@ var StatementModel = BasicModel.extend({ var line = this.getLine(handle); // Retrieve the toggle proposition - var selected; - var targetLineAmount = line.st_line.amount; - line.reconciliation_proposition.every(function (prop) { - if (!prop.invalid) { - if (((line.balance.amount < 0 || !line.partial_reconcile) && prop.amount > 0 && targetLineAmount > 0 && targetLineAmount < prop.amount) || - ((line.balance.amount > 0 || !line.partial_reconcile) && prop.amount < 0 && targetLineAmount < 0 && targetLineAmount > prop.amount)) { - selected = prop; - return false; - } - targetLineAmount -= prop.amount; - } - return true; - }); + var selected = _.filter(line.reconciliation_proposition, function(prop){return prop.display_triangle}); // If no toggled proposition found, reject it - if (selected == null) + if(selected.length != 1) return $.Deferred().reject(); + selected = selected[0]; + // Inverse partial_reconcile value selected.partial_reconcile = !selected.partial_reconcile; if (!selected.partial_reconcile) { @@ -985,41 +977,32 @@ var StatementModel = BasicModel.extend({ self._formatLineProposition(line, line.reconciliation_proposition); if (!line.reconciliation_proposition.length) { delete line.reconciliation_proposition; - }else{ - // loop state propositions - var debit_props = _.filter(line.reconciliation_proposition, function(p){ - return p.amount > 0 && line.st_line.amount; - }); - var credit_props = _.filter(line.reconciliation_proposition, function(p){ - return p.amount < 0 && line.st_line.amount < 0; - }); - var sum_debit_props = debit_props.length > 0 ? debit_props.map(function(p){return p.amount}).reduce(function(p1, p2){return p1 + p2}) : 0; - var sum_credit_props = credit_props.length > 0 ? credit_props.map(function(p){return p.amount}).reduce(function(p1, p2){return p1 + p2}) : 0; - - // Want to display the triangle for partial reconciliation or not. - if(line.st_line.amount_currency > 0 && line.st_line.amount_currency < sum_debit_props && debit_props.length == 1) - debit_props[0].display_triangle = true; - else if(line.st_line.amount_currency < 0 && line.st_line.amount_currency > sum_credit_props && credit_props.length == 1) - credit_props[0].display_triangle = true; } // No partner set on st_line and all matching amls have the same one: set it on the st_line. - if(!line.st_line.partner_id && line.reconciliation_proposition){ - var hasDifferentPartners = function(prop){ - return !prop.partner_id || prop.partner_id != line.reconciliation_proposition[0].partner_id; - } - - if(!_.any(line.reconciliation_proposition, hasDifferentPartners)){ - defs.push(self.changePartner(line.handle, { - 'id': line.reconciliation_proposition[0].partner_id, - 'display_name': line.reconciliation_proposition[0].partner_name, - }, true)); - } - } + 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; + } - defs.push(self._computeLine(line)); + 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); + } + } + return true; + }) + .then(function(){ + return data.status === 'write_off'? self.quickCreateProposition(line.handle, data.model_id) : true; + }) + ); }); - return $.when.apply($, defs); + return $.when(defs); }, /** * Format the server value then compute the line diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index e7b9067ca3d..e59934610b6 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -84,6 +84,29 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, { hideLoadMoreButton: function () { this.$('.js_load_more').hide(); }, + showRainbowMan(state){ + 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('.button_close_statement').click(this._onCloseBankStatement.bind(this)); + $done.find('.button_back_to_statement').click(this._onGoToBankStatement.bind(this)); + this.$el.children().hide(); + // 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 * @@ -103,27 +126,7 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, { .css('width', (state.valuenow/state.valuemax*100) + '%'); if (state.valuenow === state.valuemax && !this.$('.done_message').length) { - 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('.button_close_statement').click(this._onCloseBankStatement.bind(this)); - $done.find('.button_back_to_statement').click(this._onGoToBankStatement.bind(this)); - this.$el.children().hide(); - // 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); - } + this.showRainbowMan(state); } if (state.notifications) { @@ -145,11 +148,7 @@ var StatementRenderer = Widget.extend(FieldManagerMixin, { _renderNotifications: function(notifications) { this.$(".notification_area").empty(); for (var i=0; i<notifications.length; i++) { - var $notification = $(qweb.render("reconciliation.notification", { - type: notifications[i].type, - message: notifications[i].message, - details: notifications[i].details, - })).hide(); + var $notification = $(qweb.render("reconciliation.notification", notifications[i])).hide(); $notification.appendTo(this.$(".notification_area")).slideDown(300); } }, @@ -363,38 +362,48 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { // reconciliation_proposition var $props = this.$('.accounting_view tbody').empty(); - // loop state propositions + // Search propositions that could be a partial credit/debit. var props = []; - var partialDebitProps = 0; - var partialCreditProps = 0; + var partialDebitProp; + var partialCreditProp; + var balance = state.balance.amount_currency; _.each(state.reconciliation_proposition, function (prop) { if (prop.display) { props.push(prop); - if (prop.amount > 0 && prop.amount > state.st_line.amount) { - partialDebitProps++; - } else if (prop.amount < 0 && prop.amount < state.st_line.amount) { - partialCreditProps++; - } + /* + Examples: + statement line | 100 | | + move line 1 | | 200 | <- can be a partial of 100 + balance: -100 + + statement line | 500 | | + move line 1 | | 300 | <- is not a eligible to be a partial due to the second line. + move line 2 | | 300 | <- can be a partial of 200 + balance: -100 + + statement line | 500 | | + move line 1 | | 700 | <- must not be a partial (debit = 800 > 700 = credit). + move line 2 | 300 | | + balance: 100 + */ + if(!prop.display_new && balance < 0 && prop.amount > 0 && balance + prop.amount > 0) + partialDebitProp = prop; + else if(!prop.display_new && balance > 0 && prop.amount < 0 && balance + prop.amount < 0) + partialCreditProp = prop; } }); - var targetLineAmount = state.st_line.amount; - _.each(props, function (line) { - var display_triangle = (line.already_paid === false && - ((state.balance.amount_currency < 0 || line.partial_reconcile) - && line.amount > 0 && state.st_line.amount > 0 && targetLineAmount < line.amount && partialDebitProps <= 1) || - ((state.balance.amount_currency > 0 || line.partial_reconcile) - && line.amount < 0 && state.st_line.amount < 0 && targetLineAmount > line.amount && partialCreditProps <= 1)); - var $line = $(qweb.render("reconciliation.line.mv_line", {'line': line, 'state': state, 'display_triangle': display_triangle})); + line.display_triangle = (line.already_paid === false && + ((state.balance.amount_currency < 0 || line.partial_reconcile) && partialDebitProp && partialDebitProp === line) || + ((state.balance.amount_currency > 0 || line.partial_reconcile) && partialCreditProp && partialCreditProp === 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})); } - targetLineAmount -= line.amount; - $props.append($line); }); diff --git a/addons/account/static/src/js/reconciliation/tour_reconciliation.js b/addons/account/static/src/js/reconciliation/tour_reconciliation.js index ab6efaef113..533b68274ba 100644 --- a/addons/account/static/src/js/reconciliation/tour_reconciliation.js +++ b/addons/account/static/src/js/reconciliation/tour_reconciliation.js @@ -9,24 +9,15 @@ Tour.register('bank_statement_reconciliation', { test: true, // Go to the reconciliation page of the statement: "BNK/2014/001" }, [ - // Reconciliation of 'SAJ/2018/002' - // Select the 'INV/2018/0002' line and click on reconcile. + // Reconciliation of 'INV/2018/0002' + // Click on reconcile (matching done automatically by the reconciliation rule). { - content: "open the last line in match mode to test the reconcile button", - trigger: '.toggle_match:last', - extra_trigger: '.o_reconciliation', - }, - { - content: "select the 'INV/2018/0002' line", - trigger: '.o_reconciliation_line:last .match .cell_label:contains("INV/2018/0002")' - }, - { content: "reconcile the line", - trigger: '.o_reconciliation_line:last .o_reconcile:visible', + trigger: '.o_reconciliation_line:nth-child(1) .o_reconcile:visible', }, - // Reconciliation of 'First 2000 € of SAJ/2014/0001' + // Reconciliation of 'First 2000 $ of INV/2018/0001' // Make a partial reconciliation { @@ -35,10 +26,6 @@ Tour.register('bank_statement_reconciliation', { trigger: '.o_reconciliation_line:last .cell_label:contains("First")' }, { - content: "select a line with with a higher amount", - trigger: '.o_reconciliation_line:last .match .cell_label:contains("INV/2018/0001")' - }, - { content: "click on partial reconcile", trigger: '.o_reconciliation_line:last .accounting_view .do_partial_reconcile_true' }, @@ -67,7 +54,6 @@ Tour.register('bank_statement_reconciliation', { }, { content: "select a line linked to Deco Addict ", - extra_trigger: '.o_reconciliation_line:nth-child(2) .match:not(:has(tr:eq(1)))', trigger: ".o_reconciliation_line:nth-child(2) .match .line_info_button[data-content*='Deco Addict']" }, { diff --git a/addons/account/static/src/xml/account_reconciliation.xml b/addons/account/static/src/xml/account_reconciliation.xml index 3d5cc28ec3d..39cff76a6a6 100644 --- a/addons/account/static/src/xml/account_reconciliation.xml +++ b/addons/account/static/src/xml/account_reconciliation.xml @@ -32,7 +32,6 @@ <div t-if="widget._initialState.valuemax > widget._initialState.defaultDisplayQty"> <button class="btn btn-secondary js_load_more">Load more</button> </div> - <div class="float-right text-muted">Tip: Hit CTRL-Enter to reconcile all the balanced items in the sheet.</div> </div> <div t-else="" class="o_view_noreconciliation"> <p>Nothing to do!</p> @@ -157,7 +156,11 @@ <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-att-data-reconcile-model-id="reconcileModel.id"><t t-esc="reconcileModel.name"/></button> + <button class="btn btn-primary" + t-if="reconcileModel.rule_type === 'writeoff_button' && (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> @@ -214,7 +217,7 @@ <t t-name="reconciliation.line.mv_line.triangle"> - <t t-if="display_triangle || line.display_triangle"> + <t t-if="line.display_triangle"> <t t-if="line.partial_reconcile"> <span t-att-class="'do_partial_reconcile_'+(!line.partial_reconcile)+' line_info_button fa fa-exclamation-triangle'" data-content="Undo the partial reconciliation."/> @@ -231,7 +234,12 @@ <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_action"><span class="fa fa-add-remove" role="img" aria-label="Set" title="Set"/></td> <td class="cell_account_code"><t t-esc="line.account_code"/></td> - <td class="cell_due_date"><t t-esc="line.date_maturity === false ? line.date : line.date_maturity"/></td> + <td class="cell_due_date"> + <t t-if="typeof(line.id) != 'number'"> + <span class="badge badge-secondary">New</span> + </t> + <t t-else="" t-esc="line.date_maturity === false ? line.date : line.date_maturity"/> + </td> <td class="cell_label"> <t t-if="line.partner_id && line.partner_id !== state.st_line.partner_id"> <t t-if="line.partner_name.length"> @@ -297,16 +305,41 @@ </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-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-if="template"> + <t t-call="{{template}}"/> + </t> + <t t-else=""> + <t t-call="reconciliation.notification.default"/> </t> </div> </t> |
