diff --git a/addons/account/static/src/js/account_move_line_quickadd.js b/addons/account/static/src/js/account_move_line_quickadd.js deleted file mode 100644 index 0bbdb959d17..00000000000 --- a/addons/account/static/src/js/account_move_line_quickadd.js +++ /dev/null @@ -1,101 +0,0 @@ -openerp.account.quickadd = function (instance) { - var _t = instance.web._t, - _lt = instance.web._lt; - var QWeb = instance.web.qweb; - - instance.web.account = instance.web.account || {}; - - instance.web.views.add('tree_account_move_line_quickadd', 'instance.web.account.QuickAddListView'); - instance.web.account.QuickAddListView = instance.web.ListView.extend({ - init: function() { - this._super.apply(this, arguments); - this.journals = []; - this.periods = []; - this.current_journal = null; - this.current_period = null; - this.default_period = null; - this.default_journal = null; - this.current_journal_type = null; - this.current_journal_currency = null; - this.current_journal_analytic = null; - }, - start:function(){ - var tmp = this._super.apply(this, arguments); - var self = this; - var defs = []; - this.$el.parent().prepend(QWeb.render("AccountMoveLineQuickAdd", {widget: this})); - - this.$el.parent().find('.oe_account_select_journal').change(function() { - self.current_journal = this.value === '' ? null : parseInt(this.value); - self.do_search(self.last_domain, self.last_context, self.last_group_by); - }); - this.$el.parent().find('.oe_account_select_period').change(function() { - self.current_period = this.value === '' ? null : parseInt(this.value); - self.do_search(self.last_domain, self.last_context, self.last_group_by); - }); - this.on('edit:after', this, function () { - self.$el.parent().find('.oe_account_select_journal').attr('disabled', 'disabled'); - self.$el.parent().find('.oe_account_select_period').attr('disabled', 'disabled'); - }); - this.on('save:after cancel:after', this, function () { - self.$el.parent().find('.oe_account_select_journal').removeAttr('disabled'); - self.$el.parent().find('.oe_account_select_period').removeAttr('disabled'); - }); - var mod = new instance.web.Model("account.move.line", self.dataset.context, self.dataset.domain); - defs.push(mod.call("default_get", [['journal_id','period_id'],self.dataset.context]).then(function(result) { - self.current_period = result['period_id']; - self.current_journal = result['journal_id']; - })); - defs.push(mod.call("list_journals", []).then(function(result) { - self.journals = result; - })); - defs.push(mod.call("list_periods", []).then(function(result) { - self.periods = result; - })); - return $.when(tmp, defs); - }, - do_search: function(domain, context, group_by) { - var self = this; - this.last_domain = domain; - this.last_context = context; - this.last_group_by = group_by; - this.old_search = _.bind(this._super, this); - var o; - self.$el.parent().find('.oe_account_select_journal').children().remove().end(); - self.$el.parent().find('.oe_account_select_journal').append(new Option('', '')); - for (var i = 0;i < self.journals.length;i++){ - o = new Option(self.journals[i][1], self.journals[i][0]); - if (self.journals[i][0] === self.current_journal){ - self.current_journal_type = self.journals[i][2]; - self.current_journal_currency = self.journals[i][3]; - self.current_journal_analytic = self.journals[i][4]; - $(o).attr('selected',true); - } - self.$el.parent().find('.oe_account_select_journal').append(o); - } - self.$el.parent().find('.oe_account_select_period').children().remove().end(); - self.$el.parent().find('.oe_account_select_period').append(new Option('', '')); - for (var i = 0;i < self.periods.length;i++){ - o = new Option(self.periods[i][1], self.periods[i][0]); - self.$el.parent().find('.oe_account_select_period').append(o); - } - self.$el.parent().find('.oe_account_select_period').val(self.current_period).attr('selected',true); - return self.search_by_journal_period(); - }, - search_by_journal_period: function() { - var self = this; - var domain = []; - if (self.current_journal !== null) domain.push(["journal_id", "=", self.current_journal]); - if (self.current_period !== null) domain.push(["period_id", "=", self.current_period]); - self.last_context["journal_id"] = self.current_journal === null ? false : self.current_journal; - if (self.current_period === null) delete self.last_context["period_id"]; - else self.last_context["period_id"] = self.current_period; - self.last_context["journal_type"] = self.current_journal_type; - self.last_context["currency"] = self.current_journal_currency; - self.last_context["analytic_journal_id"] = self.current_journal_analytic; - var compound_domain = new instance.web.CompoundDomain(self.last_domain, domain); - self.dataset.domain = compound_domain.eval(); - return self.old_search(compound_domain, self.last_context, self.last_group_by); - }, - }); -}; diff --git a/addons/account/static/src/js/account_tour_bank_statement_reconciliation.js b/addons/account/static/src/js/account_tour_bank_statement_reconciliation.js deleted file mode 100644 index fbc55346772..00000000000 --- a/addons/account/static/src/js/account_tour_bank_statement_reconciliation.js +++ /dev/null @@ -1,147 +0,0 @@ -(function () { - 'use strict'; - - var _t = openerp._t; - - openerp.Tour.register({ - id: 'bank_statement_reconciliation', - name: _t("Reconcile the demo bank statement"), - path: '/web', - mode: 'test', - // TODO : identify menu by data-menu attr or text node ? - steps: [ - // Go to the first statement reconciliation - { - title: "go to accounting", - element: '.oe_menu_toggler:contains("Accounting"):visible', - }, - { - title: "go to bank statements", - element: '.oe_menu_leaf:contains("Bank Statement"):visible', - }, - { - title: "select first bank statement", - element: '.oe_list_content tbody tr:contains("BNK/2014/001")', - }, - { - title: "click the reconcile button", - element: '.oe_form_container header button:contains("Reconcile")', - }, - - - // Check mutual exclusion of move lines - { - title: "set second reconciliation in match mode", - element: '.oe_bank_statement_reconciliation_line:nth-child(2) .initial_line' - }, - { - title: "deselect SAJ/2014/002 from second reconciliation", - element: '.oe_bank_statement_reconciliation_line:nth-child(2) .accounting_view .mv_line:contains("SAJ/2014/002")' - }, - { - title: "check it appeared in first reconciliation's matches list and select SAJ/2014/002 in second reconciliation", - waitNot: '.oe_bank_statement_reconciliation_line:nth-child(2) .accounting_view .mv_line:contains("SAJ/2014/002")', - waitFor: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/002")', - element: '.oe_bank_statement_reconciliation_line:nth-child(2) .mv_line:contains("SAJ/2014/002")' - }, - - - // Make a partial reconciliation - { - title: "select SAJ/2014/001", - element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/001")' - }, - { - title: "click on the partial reconciliation button", - element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/001") .do_partial_reconcile_button' - }, - { - title: "click on the OK button", - element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight' - }, - - - // Test changing the partner - { - title: "change the partner (1)", - waitNot: '.oe_bank_statement_reconciliation_line:nth-child(4)', // wait for the reconciliation to be processed - element: '.oe_bank_statement_reconciliation_line:first-child .partner_name' - }, - { - title: "change the partner (2)", - element: '.oe_bank_statement_reconciliation_line:first-child .change_partner_container input', - sampleText: 'Vauxoo', - }, - { - title: "change the partner (3)", - element: '.ui-autocomplete .ui-menu-item:contains("Vauxoo")' - }, - { - title: "check the reconciliation is reloaded and has no match", - element: '.oe_bank_statement_reconciliation_line:first-child.no_match', - }, - { - title: "change the partner back (1)", - element: '.oe_bank_statement_reconciliation_line:first-child .partner_name' - }, - { - title: "change the partner back (2)", - element: '.oe_bank_statement_reconciliation_line:first-child .change_partner_container input', - sampleText: 'Best Designers', - }, - { - title: "change the partner back (3)", - element: '.ui-autocomplete .ui-menu-item:contains("Best Designers")' - }, - { - title: "select SAJ/2014/002", - element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/002")' - }, - { - title: "click on the OK button", - element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight' - }, - - - // Create a new move line in first reconciliation and validate it - { - title: "check following reconciliation passes in mode create", - waitNot: '.oe_bank_statement_reconciliation_line:nth-child(3)', // wait for the reconciliation to be processed - element: '.oe_bank_statement_reconciliation_line:first-child[data-mode="create"]' - }, - { - title: "click the Profit/Loss preset", - element: '.oe_bank_statement_reconciliation_line:first-child button:contains("Profit / Loss")' - }, - { - title: "click on the OK button", - element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight' - }, - - - // Leave an open balance - { - title: "select SAJ/2014/003", - waitNot: '.oe_bank_statement_reconciliation_line:nth-child(2)', // wait for the reconciliation to be processed - element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/003")' - }, - { - title: "click on the Keep Open button", - element: '.oe_bank_statement_reconciliation_line:first-child .button_ok:not(.oe_highlight)' - }, - - - // Be done - { - title: "check 'finish screen' and close the statement", - waitFor: '.done_message', - element: '.button_close_statement' - }, - { - title: "check the statement is closed", - element: '.oe_form_container header .label:contains("Closed")' - }, - ] - }); - -}()); diff --git a/addons/account/static/src/js/account_widgets.js b/addons/account/static/src/js/account_widgets.js deleted file mode 100644 index 4b826808457..00000000000 --- a/addons/account/static/src/js/account_widgets.js +++ /dev/null @@ -1,1872 +0,0 @@ -openerp.account = function (instance) { - openerp.account.quickadd(instance); - var _t = instance.web._t, - _lt = instance.web._lt; - var QWeb = instance.web.qweb; - - instance.web.account = instance.web.account || {}; - - instance.web.client_actions.add('bank_statement_reconciliation_view', 'instance.web.account.bankStatementReconciliation'); - instance.web.account.bankStatementReconciliation = instance.web.Widget.extend({ - className: 'oe_bank_statement_reconciliation', - - events: { - "click *[rel='do_action']": "doActionClickHandler", - "click .statement_name span": "statementNameClickHandler", - "keyup .change_statement_name_field": "changeStatementNameFieldHandler", - "click .change_statement_name_button": "changeStatementButtonClickHandler", - "click .show_more": "showMoreButtonClickHandler", - }, - - init: function(parent, context) { - this._super(parent); - // Number of reconciliations loaded initially and by clicking 'show more' - this.num_reconciliations_fetched_in_batch = 10; - if (context.context.statement_id) this.statement_ids = [context.context.statement_id]; - if (context.context.statement_ids) this.statement_ids = context.context.statement_ids; - this.single_statement = this.statement_ids !== undefined && this.statement_ids.length === 1; - this.multiple_statements = this.statement_ids !== undefined && this.statement_ids.length > 1; - this.title = context.context.title || _t("Reconciliation"); - this.notifications = context.context.notifications || []; - this.st_lines = []; - this.last_displayed_reconciliation_index = undefined; // Flow control - this.reconciled_lines = 0; // idem - this.already_reconciled_lines = 0; // Number of lines of the statement which were already reconciled - this.model_bank_statement = new instance.web.Model("account.bank.statement"); - this.model_bank_statement_line = new instance.web.Model("account.bank.statement.line"); - this.reconciliation_menu_id = false; // Used to update the needaction badge - this.formatCurrency; // Method that formats the currency ; loaded from the server - this.action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof instance.web.ActionManager }); - - // Only for statistical purposes - this.lines_reconciled_with_ctrl_enter = 0; - this.time_widget_loaded = Date.now(); - - // Stuff used by the children bankStatementReconciliationLine - this.max_move_lines_displayed = 5; - this.animation_speed = 100; // "Blocking" animations - this.aestetic_animation_speed = 300; // eye candy - this.map_currency_id_rounding = {}; - this.presets = {}; - // We'll need to get the code of an account selected in a many2one (whose value is the id) - this.map_account_id_code = {}; - // The same move line cannot be selected for multiple resolutions - this.excluded_move_lines_ids = {}; - // Description of the fields to initialize in the "create new line" form - // NB : for presets to work correctly, a field id must be the same string as a preset field - this.create_form_fields = { - account_id: { - id: "account_id", - index: 0, - corresponding_property: "account_id", // a account.move field name - label: _t("Account"), - required: true, - tabindex: 10, - constructor: instance.web.form.FieldMany2One, - field_properties: { - relation: "account.account", - string: _t("Account"), - type: "many2one", - domain: [['type','not in',['view', 'closed', 'consolidation']]], - }, - }, - label: { - id: "label", - index: 1, - corresponding_property: "label", - label: _t("Label"), - required: true, - tabindex: 11, - constructor: instance.web.form.FieldChar, - field_properties: { - string: _t("Label"), - type: "char", - }, - }, - tax_id: { - id: "tax_id", - index: 2, - corresponding_property: "tax_id", - label: _t("Tax"), - required: false, - tabindex: 12, - constructor: instance.web.form.FieldMany2One, - field_properties: { - relation: "account.tax", - string: _t("Tax"), - type: "many2one", - domain: [['type_tax_use','in',['purchase', 'all']], ['parent_id', '=', false]], - }, - }, - amount: { - id: "amount", - index: 3, - corresponding_property: "amount", - label: _t("Amount"), - required: true, - tabindex: 13, - constructor: instance.web.form.FieldFloat, - field_properties: { - string: _t("Amount"), - type: "float", - }, - }, - analytic_account_id: { - id: "analytic_account_id", - index: 4, - corresponding_property: "analytic_account_id", - label: _t("Analytic Acc."), - required: false, - tabindex: 14, - group:"analytic.group_analytic_accounting", - constructor: instance.web.form.FieldMany2One, - field_properties: { - relation: "account.analytic.account", - string: _t("Analytic Acc."), - type: "many2one", - domain: [['type', '!=', 'view'], ['state', 'not in', ['close','cancelled']]], - }, - }, - }; - }, - - start: function() { - this._super(); - var self = this; - // Retreive statement infos and reconciliation data from the model - var lines_filter = [['journal_entry_ids', '=', false], ['account_id', '=', false]]; - var deferred_promises = []; - - // Working on specified statement(s) - if (self.statement_ids && self.statement_ids.length > 0) { - lines_filter.push(['statement_id', 'in', self.statement_ids]); - - // If only one statement, display its name as title and allow to modify it - if (self.single_statement) { - deferred_promises.push(self.model_bank_statement - .query(["name"]) - .filter([['id', '=', self.statement_ids[0]]]) - .first() - .then(function(title){ - self.title = title.name; - }) - ); - } - // Anyway, find out how many statement lines are reconciled (for the progressbar) - deferred_promises.push(self.model_bank_statement - .call("number_of_lines_reconciled", [self.statement_ids]) - .then(function(num) { - self.already_reconciled_lines = num; - }) - ); - } - - // Get operation templates - deferred_promises.push(new instance.web.Model("account.statement.operation.template") - .query(['id','name','account_id','label','amount_type','amount','tax_id','analytic_account_id']) - .all().then(function (data) { - _(data).each(function(preset){ - self.presets[preset.id] = preset; - }); - }) - ); - - // Get the function to format currencies - deferred_promises.push(new instance.web.Model("res.currency") - .call("get_format_currencies_js_function") - .then(function(data) { - self.formatCurrency = new Function("amount, currency_id", data); - }) - ); - - // Get statement lines - deferred_promises.push(self.model_bank_statement_line - .query(['id']) - .filter(lines_filter) - .order_by('statement_id, id') - .all().then(function (data) { - self.st_lines = _(data).map(function(o){ return o.id }); - }) - ); - - // When queries are done, render template and reconciliation lines - return $.when.apply($, deferred_promises).then(function(){ - - // If there is no statement line to reconcile, stop here - if (self.st_lines.length === 0) { - self.$el.prepend(QWeb.render("bank_statement_nothing_to_reconcile")); - return; - } - - // Create a dict account id -> account code for display facilities - new instance.web.Model("account.account") - .query(['id', 'code']) - .all().then(function(data) { - _.each(data, function(o) { self.map_account_id_code[o.id] = o.code }); - }); - - // Create a dict currency id -> rounding factor - new instance.web.Model("res.currency") - .query(['id', 'rounding']) - .all().then(function(data) { - _.each(data, function(o) { self.map_currency_id_rounding[o.id] = o.rounding }); - }); - - new instance.web.Model("ir.model.data") - .call("xmlid_to_res_id", ["account.menu_bank_reconcile_bank_statements"]) - .then(function(data) { - self.reconciliation_menu_id = data; - self.doReloadMenuReconciliation(); - }); - - // Bind keyboard events TODO : méthode standard ? - $("body").on("keypress", function (e) { - self.keyboardShortcutsHandler(e); - }); - - // Render and display - self.$el.prepend(QWeb.render("bank_statement_reconciliation", { - title: self.title, - single_statement: self.single_statement, - total_lines: self.already_reconciled_lines+self.st_lines.length - })); - self.updateProgressbar(); - var reconciliations_to_show = self.st_lines.slice(0, self.num_reconciliations_fetched_in_batch); - self.last_displayed_reconciliation_index = reconciliations_to_show.length; - self.$(".reconciliation_lines_container").css("opacity", 0); - - // Display the reconciliations - return self.model_bank_statement_line - .call("get_data_for_reconciliations", [reconciliations_to_show]) - .then(function (data) { - var child_promises = []; - while ((datum = data.shift()) !== undefined) - child_promises.push(self.displayReconciliation(datum.st_line.id, 'inactive', false, true, datum.st_line, datum.reconciliation_proposition)); - $.when.apply($, child_promises).then(function(){ - self.$(".reconciliation_lines_container").animate({opacity: 1}, self.aestetic_animation_speed); - self.getChildren()[0].set("mode", "match"); - self.updateShowMoreButton(); - if (self.notifications) { - self.displayNotifications(self.notifications); - } - }); - }); - }); - }, - - doActionClickHandler: function(e) { - var name = e.currentTarget.dataset.action_nam; - var model = e.currentTarget.dataset.model; - var ids = e.currentTarget.dataset.ids.split(",").map(Number); - this.action_manager.do_action({ - name: name, - res_model: model, - domain: [['id', 'in', ids]], - views: [[false, 'list'], [false, 'form']], - type: 'ir.actions.act_window', - view_type: "list", - view_mode: "list" - }); - }, - - displayNotifications: function(notifications, speed) { - speed = speed === undefined ? this.aestetic_animation_speed : speed; - for (var i=0; i= 1) time_taken = Math.floor(sec_taken/60) +"' "+ sec_taken%60 +"''"; - else time_taken = sec_taken%60 +" seconds"; - - var title; - if (sec_per_item < 5) title = _t("Whew, that was fast !") + " "; - else title = _t("Congrats, you're all done !") + " "; - - if (self.lines_reconciled_with_ctrl_enter === self.reconciled_lines) - achievements.push({ - title: _t("Efficiency at its finest"), - desc: _t("Only use the ctrl-enter shortcut to validate reconciliations."), - icon: "fa-keyboard-o"} - ); - - if (sec_per_item < 5) - achievements.push({ - title: _t("Fast reconciler"), - desc: _t("Take on average less than 5 seconds to reconcile a transaction."), - icon: "fa-bolt"} - ); - - // Render it - self.$(".protip").hide(); - self.updateShowMoreButton(); - self.$(".oe_form_sheet").append(QWeb.render("bank_statement_reconciliation_done_message", { - title: title, - time_taken: time_taken, - sec_per_item: sec_per_item, - transactions_done: self.reconciled_lines, - done_with_ctrl_enter: self.lines_reconciled_with_ctrl_enter, - achievements: achievements, - single_statement: self.single_statement, - multiple_statements: self.multiple_statements, - })); - - // Animate it - var container = $("
"); - self.$(".done_message").wrap(container).css("opacity", 0).css("position", "relative").css("left", "-50%"); - self.$(".done_message").animate({opacity: 1, left: 0}, self.aestetic_animation_speed*2, "easeOutCubic"); - self.$(".done_message").animate({opacity: 1}, self.aestetic_animation_speed*3, "easeOutCubic"); - - // Make it interactive - self.$(".achievement").popover({'placement': 'top', 'container': self.el, 'trigger': 'hover'}); - - if (self.$(".button_back_to_statement").length !== 0) { - self.$(".button_back_to_statement").click(function() { - self.goBackToStatementsTreeView(); - }); - } - - if (self.$(".button_close_statement").length !== 0) { - self.$(".button_close_statement").hide(); - self.model_bank_statement - .query(["balance_end_real", "balance_end"]) - .filter([['id', 'in', self.statement_ids]]) - .all() - .then(function(data){ - if (_.all(data, function(o) { return o.balance_end_real === o.balance_end })) { - self.$(".button_close_statement").show(); - self.$(".button_close_statement").click(function() { - self.$(".button_close_statement").attr("disabled", "disabled"); - self.model_bank_statement - .call("button_confirm_bank", [self.statement_ids]) - .then(function () { - self.goBackToStatementsTreeView(); - }, function() { - self.$(".button_close_statement").removeAttr("disabled"); - }); - }); - } - }); - } - }, - - showMoreButtonClickHandler: function() { - this.displayReconciliations(this.num_reconciliations_fetched_in_batch); - }, - - updateShowMoreButton: function() { - var items_remaining = this.st_lines.length - this.last_displayed_reconciliation_index; - if (items_remaining > 0) - this.$(".show_more").show().find(".num_items_remaining").text(items_remaining); - else - this.$(".show_more").hide(); - }, - - updateProgressbar: function() { - var self = this; - var done = self.already_reconciled_lines + self.reconciled_lines; - var total = self.already_reconciled_lines + self.st_lines.length; - var prog_bar = self.$(".progress .progress-bar"); - prog_bar.attr("aria-valuenow", done); - prog_bar.css("width", (done/total*100)+"%"); - self.$(".progress .progress-text .valuenow").text(done); - }, - - /* reloads the needaction badge */ - doReloadMenuReconciliation: function () { - var menu = instance.webclient.menu; - if (!menu || !this.reconciliation_menu_id) { - return $.when(); - } - return menu.rpc("/web/menu/load_needaction", {'menu_ids': [this.reconciliation_menu_id]}).done(function(r) { - menu.on_needaction_loaded(r); - }).then(function () { - menu.trigger("need_action_reloaded"); - }); - }, - }); - - instance.web.account.bankStatementReconciliationLine = instance.web.Widget.extend({ - className: 'oe_bank_statement_reconciliation_line', - - events: { - "click .change_partner": "changePartnerClickHandler", - "click .button_ok": "persistAndDestroy", - "click .mv_line": "moveLineClickHandler", - "click .initial_line": "initialLineClickHandler", - "click .line_open_balance": "lineOpenBalanceClickHandler", - "click .pager_control_left:not(.disabled)": "pagerControlLeftHandler", - "click .pager_control_right:not(.disabled)": "pagerControlRightHandler", - "keyup .filter": "filterHandler", - "click .line_info_button": function(e){e.stopPropagation()}, // small usability hack - "click .add_line": "addLineBeingEdited", - "click .preset": "presetClickHandler", - "click .do_partial_reconcile_button": "doPartialReconcileButtonClickHandler", - "click .undo_partial_reconcile_button": "undoPartialReconcileButtonClickHandler", - }, - - init: function(parent, context) { - this._super(parent); - - this.formatCurrency = this.getParent().formatCurrency; - if (context.initial_data_provided) { - // Process data - _.each(context.reconciliation_proposition, function(line) { - this.decorateMoveLine(line, context.st_line.currency_id); - }, this); - this.set("mv_lines_selected", context.reconciliation_proposition); - this.st_line = context.st_line; - this.partner_id = context.st_line.partner_id; - this.decorateStatementLine(this.st_line); - - // Exclude selected move lines - if (this.getParent().excluded_move_lines_ids[this.partner_id] === undefined) - this.getParent().excluded_move_lines_ids[this.partner_id] = []; - this.getParent().excludeMoveLines(this, this.partner_id, context.reconciliation_proposition); - } else { - this.set("mv_lines_selected", []); - this.st_line = undefined; - this.partner_id = undefined; - } - - this.context = context; - this.st_line_id = context.st_line_id; - this.max_move_lines_displayed = this.getParent().max_move_lines_displayed; - this.animation_speed = this.getParent().animation_speed; - this.aestetic_animation_speed = this.getParent().aestetic_animation_speed; - this.model_bank_statement_line = new instance.web.Model("account.bank.statement.line"); - this.model_res_users = new instance.web.Model("res.users"); - this.model_tax = new instance.web.Model("account.tax"); - this.map_currency_id_rounding = this.getParent().map_currency_id_rounding; - this.map_account_id_code = this.getParent().map_account_id_code; - this.presets = this.getParent().presets; - this.is_valid = true; - this.is_consistent = true; // Used to prevent bad server requests - this.is_rapprochement; // matching a statement line with an already reconciled journal item - this.can_fetch_more_move_lines; // Tell if we can show more move lines - this.filter = ""; - // In rare cases like when deleting a statement line's partner we don't want the server to - // look for a reconciliation proposition (in this particular case it might find a move line - // matching the statement line and decide to set the statement line's partner accordingly) - this.do_load_reconciliation_proposition = true; - - this.set("mode", undefined); - this.on("change:mode", this, this.modeChanged); - this.set("balance", undefined); // Debit is +, credit is - - this.on("change:balance", this, this.balanceChanged); - this.set("pager_index", 0); - this.on("change:pager_index", this, this.pagerChanged); - // NB : mv_lines represent the counterpart that will be created to reconcile existing move lines, so debit and credit are inverted - this.set("mv_lines", []); - this.on("change:mv_lines", this, this.mvLinesChanged); - this.mv_lines_deselected = []; // deselected lines are displayed on top of the match table - this.on("change:mv_lines_selected", this, this.mvLinesSelectedChanged); - this.set("lines_created", []); - this.set("line_created_being_edited", [{'id': 0}]); - this.on("change:lines_created", this, this.createdLinesChanged); - this.on("change:line_created_being_edited", this, this.createdLinesChanged); - }, - - start: function() { - var self = this; - return self._super().then(function() { - // no animation while loading - self.animation_speed = 0; - self.aestetic_animation_speed = 0; - - self.is_consistent = false; - if (self.context.animate_entrance) { - self.$el.fadeOut(0); - self.$el.slideUp(0); - } - return $.when(self.loadData()).then(function(){ - return $.when(self.render()).then(function(){ - self.is_consistent = true; - // Make an entrance - self.animation_speed = self.getParent().animation_speed; - self.aestetic_animation_speed = self.getParent().aestetic_animation_speed; - if (self.context.animate_entrance) { - return self.$el.stop(true, true).fadeIn({ duration: self.aestetic_animation_speed, queue: false }).css('display', 'none').slideDown(self.aestetic_animation_speed); - } - }); - }); - }); - }, - - loadData: function() { - var self = this; - if (self.context.initial_data_provided) - return; - - // Get ids of selected move lines (to exclude them from reconciliation proposition) - var excluded_move_lines_ids = []; - if (self.do_load_reconciliation_proposition) { - _.each(self.getParent().excluded_move_lines_ids, function(o){ - excluded_move_lines_ids = excluded_move_lines_ids.concat(o); - }); - } - // Load statement line - return self.model_bank_statement_line - .call("get_data_for_reconciliations", [[self.st_line_id], excluded_move_lines_ids, self.do_load_reconciliation_proposition]) - .then(function (data) { - self.st_line = data[0].st_line; - self.decorateStatementLine(self.st_line); - self.partner_id = data[0].st_line.partner_id; - if (self.getParent().excluded_move_lines_ids[self.partner_id] === undefined) - self.getParent().excluded_move_lines_ids[self.partner_id] = []; - var mv_lines = []; - _.each(data[0].reconciliation_proposition, function(line) { - self.decorateMoveLine(line, self.st_line.currency_id); - mv_lines.push(line); - }, self); - self.set("mv_lines_selected", self.get("mv_lines_selected").concat(mv_lines)); - }); - }, - - render: function() { - var self = this; - var presets_array = []; - for (var id in self.presets) - if (self.presets.hasOwnProperty(id)) - presets_array.push(self.presets[id]); - self.$el.prepend(QWeb.render("bank_statement_reconciliation_line", { - line: self.st_line, - mode: self.context.mode, - presets: presets_array - })); - - // Stuff that require the template to be rendered - self.$(".match").slideUp(0); - self.$(".create").slideUp(0); - if (self.st_line.no_match) self.$el.addClass("no_match"); - self.bindPopoverTo(self.$(".line_info_button")); - self.createFormWidgets(); - // Special case hack : no identified partner - if (self.st_line.has_no_partner) { - self.$el.css("opacity", "0"); - self.updateBalance(); - self.$(".change_partner_container").show(0); - self.$(".match").slideUp(0); - self.$el.addClass("no_partner"); - self.set("mode", self.context.mode); - self.balanceChanged(); - self.updateAccountingViewMatchedLines(); - self.animation_speed = self.getParent().animation_speed; - self.aestetic_animation_speed = self.getParent().aestetic_animation_speed; - self.$el.animate({opacity: 1}, self.aestetic_animation_speed); - return; - } - - // TODO : the .on handler's returned deferred is lost - return $.when(self.set("mode", self.context.mode)).then(function(){ - // Make sure the display is OK - self.balanceChanged(); - self.createdLinesChanged(); - self.updateAccountingViewMatchedLines(); - }); - }, - - restart: function(mode) { - var self = this; - mode = (mode === undefined ? 'inactive' : mode); - self.context.animate_entrance = false; - self.$el.css("height", self.$el.outerHeight()); - // Destroy everything - _.each(self.getChildren(), function(o){ o.destroy() }); - self.is_consistent = false; - return $.when(self.$el.animate({opacity: 0}, self.animation_speed)).then(function() { - self.getParent().unexcludeMoveLines(self, self.partner_id, self.get("mv_lines_selected")); - $.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy') }); - self.$el.empty(); - self.$el.removeClass("no_partner"); - self.context.mode = mode; - self.context.initial_data_provided = false; - self.is_valid = true; - self.is_consistent = true; - self.filter = ""; - self.set("balance", undefined, {silent: true}); - self.set("mode", undefined, {silent: true}); - self.set("pager_index", 0, {silent: true}); - self.set("mv_lines", [], {silent: true}); - self.set("mv_lines_selected", [], {silent: true}); - self.mv_lines_deselected = []; - self.set("lines_created", [], {silent: true}); - self.set("line_created_being_edited", [{'id': 0}], {silent: true}); - // Rebirth - return $.when(self.start()).then(function() { - self.$el.css("height", "auto"); - self.is_consistent = true; - self.$el.animate({opacity: 1}, self.animation_speed); - }); - }); - }, - - /* create form widgets, append them to the dom and bind their events handlers */ - createFormWidgets: function() { - var self = this; - var create_form_fields = self.getParent().create_form_fields; - var create_form_fields_arr = []; - for (var key in create_form_fields) - if (create_form_fields.hasOwnProperty(key)) - create_form_fields_arr.push(create_form_fields[key]); - create_form_fields_arr.sort(function(a, b){ return b.index - a.index }); - - // field_manager - var dataset = new instance.web.DataSet(this, "account.account", self.context); - dataset.ids = []; - dataset.arch = { - attrs: { string: "Stéphanie de Monaco", version: "7.0", class: "oe_form_container" }, - children: [], - tag: "form" - }; - - var field_manager = new instance.web.FormView ( - this, dataset, false, { - initial_mode: 'edit', - disable_autofocus: false, - $buttons: $(), - $pager: $() - }); - - field_manager.load_form(dataset); - - // fields default properties - var Default_field = function() { - this.context = {}; - this.domain = []; - this.help = ""; - this.readonly = false; - this.required = true; - this.selectable = true; - this.states = {}; - this.views = {}; - }; - var Default_node = function(field_name) { - this.tag = "field"; - this.children = []; - this.required = true; - this.attrs = { - invisible: "False", - modifiers: '{"required":true}', - name: field_name, - nolabel: "True", - }; - }; - - // Append fields to the field_manager - field_manager.fields_view.fields = {}; - for (var i=0; i 0) balance_type = "greater"; - else if (self.get("balance") * self.st_line.amount < 0) balance_type = "lower"; - - // Adjust to different cases - if (balance_type === "equal") { - displayValidState(true); - } else if (balance_type === "greater") { - createOpenBalance("Create Write-off"); - } else if (balance_type === "lower") { - if (self.st_line.has_no_partner) { - createOpenBalance("Choose counterpart"); - } else { - displayValidState(false, "Keep open"); - createOpenBalance("Open balance"); - } - } - - // Show or hide partial reconciliation - if (self.get("mv_lines_selected").length > 0) { - var propose_partial = self.getCreatedLines().length === 0 - && self.get("mv_lines_selected").length === 1 - && balance_type === "greater" - && ! self.get("mv_lines_selected")[0].partial_reconcile - && ! self.is_rapprochement; - self.get("mv_lines_selected")[0].propose_partial_reconcile = propose_partial; - self.updateAccountingViewMatchedLines(); - } - - function displayValidState(higlight_ok_button, ok_button_text) { - self.is_valid = true; - self.$(".tip_reconciliation_not_balanced").hide(); - self.$(".button_ok").removeAttr("disabled"); - if (higlight_ok_button) self.$(".button_ok").addClass("btn-primary"); - if (ok_button_text !== undefined) self.$(".button_ok").text(ok_button_text) - } - - function createOpenBalance(name) { - var balance = self.get("balance"); - var amount = self.formatCurrency(Math.abs(balance), self.st_line.currency_id); - var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", { - debit: balance > 0 ? amount : "", - credit: balance < 0 ? amount : "", - account_code: self.map_account_id_code[self.st_line.open_balance_account_id] - })); - if (name !== undefined) - $line.find(".cell_label").text(name); - self.$(".tbody_open_balance").empty().append($line); - } - }, - - modeChanged: function(o, val) { - var self = this; - - self.$(".action_pane.active").removeClass("active"); - - if (val.oldValue === "create") - self.addLineBeingEdited(); - - if (self.get("mode") === "inactive") { - self.$(".match").slideUp(self.animation_speed); - self.$(".create").slideUp(self.animation_speed); - self.el.dataset.mode = "inactive"; - - } else if (self.get("mode") === "match") { - // TODO : remove this old_animation_speed / new_animation_speed hack - // when .on handler's returned deferred's no longer lost - var old_animation_speed = self.animation_speed; - return $.when(self.updateMatches()).then(function() { - var new_animation_speed = self.animation_speed; - self.animation_speed = old_animation_speed; - if (self.$el.hasClass("no_match")) { - self.animation_speed = 0; - self.set("mode", "create"); - return; - } - self.$(".match").slideDown(self.animation_speed); - self.$(".create").slideUp(self.animation_speed); - self.el.dataset.mode = "match"; - self.animation_speed = new_animation_speed; - }); - - } else if (self.get("mode") === "create") { - self.initializeCreateForm(); - self.$(".match").slideUp(self.animation_speed); - self.$(".create").slideDown(self.animation_speed); - self.el.dataset.mode = "create"; - } - }, - - pagerChanged: function() { - this.updateMatches(); - }, - - mvLinesChanged: function() { - var self = this; - // If pager_index is out of range, set it to display the last page - if (self.get("pager_index") !== 0 && self.get("mv_lines").length === 0 && ! self.can_fetch_more_move_lines) { - self.set("pager_index", 0); - } - - // If there is no match to display, disable match view and pass in mode inactive - if (self.get("mv_lines").length + self.mv_lines_deselected.length === 0 && !self.can_fetch_more_move_lines && self.filter === "") { - self.$el.addClass("no_match"); - if (self.get("mode") === "match") { - self.set("mode", "inactive"); - } - } else { - self.$el.removeClass("no_match"); - } - - _.each(self.get("mv_lines"), function(line) { - if (line.partial_reconciliation_siblings_ids.length > 0) { - var correct_format = _.collect(line.partial_reconciliation_siblings_ids, function(o) { return {'id': o} }); - self.getParent().excludeMoveLines(self, self.partner_id, correct_format); - } - }); - - self.updateMatchView(); - self.updatePagerControls(); - }, - - mvLinesSelectedChanged: function(elt, val) { - var self = this; - - var added_lines = _.difference(val.newValue, val.oldValue); - var removed_lines = _.difference(val.oldValue, val.newValue); - - self.getParent().excludeMoveLines(self, self.partner_id, added_lines); - self.getParent().unexcludeMoveLines(self, self.partner_id, removed_lines); - - self.is_rapprochement = added_lines.length > 0 && added_lines[0].already_paid; - - $.when(self.updateMatches()).then(function(){ - self.updateAccountingViewMatchedLines(); - self.updateBalance(); - }); - }, - - // Generic function for updating the line_created_being_edited - formCreateInputChanged: function(elt, val) { - var self = this; - var line_created_being_edited = self.get("line_created_being_edited"); - line_created_being_edited[0][elt.corresponding_property] = val.newValue; - line_created_being_edited[0].currency_id = self.st_line.currency_id; - - // Specific cases - if (elt === self.account_id_field) - line_created_being_edited[0].account_num = self.map_account_id_code[elt.get("value")]; - - // Update tax line - var deferred_tax = new $.Deferred(); - if (elt === self.tax_id_field || elt === self.amount_field) { - var amount = self.amount_field.get("value"); - var tax_id = self.tax_id_field.get("value"); - if (amount && tax_id) { - deferred_tax = $.when(self.model_tax - .call("compute_for_bank_reconciliation", [tax_id, amount])) - .then(function(data){ - line_created_being_edited[0].amount_before_tax = (data.total.toFixed(3) === amount.toFixed(3) ? data.total : data.total_included); - line_created_being_edited[0].amount = data.total - var current_line_cursor = 1; - $.each(data.taxes, function(index, tax){ - if (tax.amount !== 0.0) { - var tax_account_id = (amount > 0 ? tax.account_collected_id : tax.account_paid_id); - tax_account_id = tax_account_id !== false ? tax_account_id: line_created_being_edited[0].account_id; - line_created_being_edited[current_line_cursor] = { - id: line_created_being_edited[0].id, - account_id: tax_account_id, - account_num: self.map_account_id_code[tax_account_id], - label: tax.name, - amount: tax.amount, - no_remove_action: true, - currency_id: self.st_line.currency_id, - is_tax_line: true - }; - current_line_cursor = current_line_cursor + 1; - } - }); - } - ); - } else { - line_created_being_edited.length = 1; - deferred_tax.resolve(); - } - } else { deferred_tax.resolve(); } - - $.when(deferred_tax).then(function(){ - // Format amounts - var rounding = 1/self.map_currency_id_rounding[self.st_line.currency_id]; - $.each(line_created_being_edited, function(index, val) { - if (val.amount) { - line_created_being_edited[index].amount = Math.round(val.amount*rounding)/rounding; - line_created_being_edited[index].amount_str = self.formatCurrency(Math.abs(val.amount), val.currency_id); - } - if (val.amount_before_tax) - line_created_being_edited[index].amount_before_tax = Math.round(val.amount_before_tax*rounding)/rounding; - }); - self.set("line_created_being_edited", line_created_being_edited); - self.createdLinesChanged(); // TODO For some reason, previous line doesn't trigger change handler - }); - }, - - createdLinesChanged: function() { - var self = this; - self.updateAccountingViewCreatedLines(); - self.updateBalance(); - - if (self.islineCreatedBeingEditedValid()) self.$(".add_line").show(); - else self.$(".add_line").hide(); - }, - - - /** Model */ - - doPartialReconcileButtonClickHandler: function(e) { - var self = this; - - var line_id = $(e.currentTarget).closest("tr").data("lineid"); - var line = _.find(self.get("mv_lines_selected"), function(o) { return o.id == line_id }); - self.partialReconcileLine(line); - - $(e.currentTarget).popover('destroy'); - self.updateAccountingViewMatchedLines(); - self.updateBalance(); - e.stopPropagation(); - }, - - partialReconcileLine: function(line) { - var self = this; - var balance = self.get("balance"); - line.initial_amount = line.debit !== 0 ? line.debit : -1 * line.credit; - if (balance < 0) { - line.debit += balance; - line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id); - } else { - line.credit -= balance; - line.credit_str = self.formatCurrency(line.credit, self.st_line.currency_id); - } - line.propose_partial_reconcile = false; - line.partial_reconcile = true; - }, - - undoPartialReconcileButtonClickHandler: function(e) { - var self = this; - - var line_id = $(e.currentTarget).closest("tr").data("lineid"); - var line = _.find(self.get("mv_lines_selected"), function(o) { return o.id == line_id }); - self.unpartialReconcileLine(line); - - $(e.currentTarget).popover('destroy'); - self.updateAccountingViewMatchedLines(); - self.updateBalance(); - e.stopPropagation(); - }, - - unpartialReconcileLine: function(line) { - var self = this; - if (line.initial_amount > 0) { - line.debit = line.initial_amount; - line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id); - } else { - line.credit = -1 * line.initial_amount; - line.credit_str = self.formatCurrency(line.credit, self.st_line.currency_id); - } - line.propose_partial_reconcile = true; - line.partial_reconcile = false; - }, - - updateBalance: function() { - var self = this; - var mv_lines_selected = self.get("mv_lines_selected"); - var lines_selected_num = mv_lines_selected.length; - - // Undo partial reconciliation if necessary - if (lines_selected_num !== 1) { - _.each(mv_lines_selected, function(line) { - if (line.partial_reconcile === true) self.unpartialReconcileLine(line); - if (line.propose_partial_reconcile === true) line.propose_partial_reconcile = false; - }); - self.updateAccountingViewMatchedLines(); - } - - // Compute balance - var balance = 0; - balance -= self.st_line.amount; - _.each(mv_lines_selected, function(o) { - balance = balance - o.debit + o.credit; - }); - _.each(self.getCreatedLines(), function(o) { - balance += o.amount; - }); - // Dealing with floating-point - balance = Math.round(balance*1000)/1000; - - self.set("balance", balance); - - }, - - // Loads move lines according to the widget's state - updateMatches: function() { - var self = this; - var deselected_lines_num = self.mv_lines_deselected.length; - var offset = self.get("pager_index") * self.max_move_lines_displayed - deselected_lines_num; - if (offset < 0) offset = 0; - var limit = (self.get("pager_index")+1) * self.max_move_lines_displayed - deselected_lines_num; - if (limit > self.max_move_lines_displayed) limit = self.max_move_lines_displayed; - var excluded_ids = _.collect(self.get("mv_lines_selected").concat(self.mv_lines_deselected), function(o) { return o.id; }); - var globally_excluded_ids = []; - if (self.st_line.has_no_partner) - _.each(self.getParent().excluded_move_lines_ids, function(o) { globally_excluded_ids = globally_excluded_ids.concat(o) }); - else - globally_excluded_ids = self.getParent().excluded_move_lines_ids[self.partner_id]; - if (globally_excluded_ids !== undefined) - for (var i=0; i 0) { - return self.model_bank_statement_line - .call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, offset, limit]) - .then(function (lines) { - _.each(lines, function(line) { self.decorateMoveLine(line, self.st_line.currency_id) }, self); - // If we could fetch 1 more item than what we'll display, that means there are move lines left to be displayed (so we enable the pager) - self.can_fetch_more_move_lines = (lines.length === limit); - self.set("mv_lines", lines.slice(0, limit-1)); - }); - } else { - self.set("mv_lines", []); - } - }, - - // Changes the partner_id of the statement_line in the DB and reloads the widget - changePartner: function(partner_id) { - var self = this; - self.is_consistent = false; - return self.model_bank_statement_line - // Update model - .call("write", [[self.st_line_id], {'partner_id': partner_id}]) - .then(function () { - self.do_load_reconciliation_proposition = false; // of the server might set the statement line's partner - self.animation_speed = 0; - return $.when(self.restart(self.get("mode"))).then(function(){ - self.do_load_reconciliation_proposition = true; - self.is_consistent = true; - self.set("mode", "match"); - }); - }); - }, - - // Returns an object that can be passed to process_reconciliation() - prepareSelectedMoveLineForPersisting: function(line) { - return { - name: line.name, - debit: line.debit, - credit: line.credit, - counterpart_move_line_id: line.id, - already_paid: line.already_paid, - }; - }, - - // idem - prepareCreatedMoveLineForPersisting: function(line) { - var dict = {}; - if (dict['account_id'] === undefined) - dict['account_id'] = line.account_id; - dict['name'] = line.label; - var amount = line.tax_id ? line.amount_before_tax: line.amount; - if (amount > 0) dict['credit'] = amount; - if (amount < 0) dict['debit'] = -1 * amount; - if (line.tax_id) dict['account_tax_id'] = line.tax_id; - if (line.analytic_account_id) dict['analytic_account_id'] = line.analytic_account_id; - - return dict; - }, - - // idem - prepareOpenBalanceForPersisting: function() { - var balance = this.get("balance"); - var dict = {}; - - dict['account_id'] = this.st_line.open_balance_account_id; - dict['name'] = this.st_line.name + ' : ' + _t("Open balance"); - if (balance > 0) dict['debit'] = balance; - if (balance < 0) dict['credit'] = -1*balance; - - return dict; - }, - - makeMoveLineDicts: function() { - var self = this; - var mv_line_dicts = []; - _.each(self.get("mv_lines_selected"), function(l) { mv_line_dicts.push(self.prepareSelectedMoveLineForPersisting(l)) }); - created_lines = _.filter(self.getCreatedLines(), function(l) { return ! l.is_tax_line; }); // Tax lines are created via account_tax_id - _.each(created_lines, function(l) { mv_line_dicts.push(self.prepareCreatedMoveLineForPersisting(l)) }); - if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting()); - return mv_line_dicts; - }, - - // Persist data, notify parent view and terminate widget - persistAndDestroy: function(speed) { - var self = this; - speed = (isNaN(speed) ? self.animation_speed : speed); - if (! self.is_consistent) return; - - // Sliding animation - var height = self.$el.outerHeight(); - var container = $("
"); - container.css("height", height) - .css("marginTop", self.$el.css("marginTop")) - .css("marginBottom", self.$el.css("marginBottom")); - self.$el.wrap(container); - var deferred_animation = self.$el.parent().slideUp(speed*height/150); - - // RPC - self.$(".button_ok").attr("disabled", "disabled"); - return self.model_bank_statement_line - .call("process_reconciliation", [self.st_line_id, self.makeMoveLineDicts()]) - .done(function () { - self.getParent().unexcludeMoveLines(self, self.partner_id, self.get("mv_lines_selected")); - $.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy') }); - return $.when(deferred_animation).then(function(){ - self.$el.parent().remove(); - var parent = self.getParent(); - return $.when(self.destroy()).then(function() { - parent.childValidated(self); - }); - }); - }).fail(function(){ - self.$el.parent().slideDown(speed*height/150, function(){ - self.$el.unwrap(); - }); - }).always(function() { - self.$(".button_ok").removeAttr("disabled"); - }); - }, - }); - - instance.web.views.add('tree_account_reconciliation', 'instance.web.account.ReconciliationListView'); - instance.web.account.ReconciliationListView = instance.web.ListView.extend({ - init: function() { - this._super.apply(this, arguments); - var self = this; - this.current_partner = null; - this.on('record_selected', this, function() { - if (self.get_selected_ids().length === 0) { - self.$(".oe_account_recon_reconcile").attr("disabled", ""); - } else { - self.$(".oe_account_recon_reconcile").removeAttr("disabled"); - } - }); - }, - load_list: function() { - var self = this; - var tmp = this._super.apply(this, arguments); - if (this.partners) { - this.$el.prepend(QWeb.render("AccountReconciliation", {widget: this})); - this.$(".oe_account_recon_previous").click(function() { - self.current_partner = (((self.current_partner - 1) % self.partners.length) + self.partners.length) % self.partners.length; - self.search_by_partner(); - }); - this.$(".oe_account_recon_next").click(function() { - self.current_partner = (self.current_partner + 1) % self.partners.length; - self.search_by_partner(); - }); - this.$(".oe_account_recon_reconcile").click(function() { - self.reconcile(); - }); - this.$(".oe_account_recom_mark_as_reconciled").click(function() { - self.mark_as_reconciled(); - }); - } - return tmp; - }, - do_search: function(domain, context, group_by) { - var self = this; - this.last_domain = domain; - this.last_context = context; - this.last_group_by = group_by; - this.old_search = _.bind(this._super, this); - var mod = new instance.web.Model("account.move.line", context, domain); - return mod.call("list_partners_to_reconcile", []).then(function(result) { - var current = self.current_partner !== null ? self.partners[self.current_partner][0] : null; - self.partners = result; - var index = _.find(_.range(self.partners.length), function(el) { - if (current === self.partners[el][0]) - return true; - }); - if (index !== undefined) - self.current_partner = index; - else - self.current_partner = self.partners.length == 0 ? null : 0; - self.search_by_partner(); - }); - }, - search_by_partner: function() { - var self = this; - var fct = function() { - return self.old_search(new instance.web.CompoundDomain(self.last_domain, - [["partner_id", "in", self.current_partner === null ? [] : - [self.partners[self.current_partner][0]] ]]), self.last_context, self.last_group_by); - }; - if (self.current_partner === null) { - self.last_reconciliation_date = _t("Never"); - return fct(); - } else { - return new instance.web.Model("res.partner").call("read", - [self.partners[self.current_partner][0], ["last_reconciliation_date"]]).then(function(res) { - self.last_reconciliation_date = - instance.web.format_value(res.last_reconciliation_date, {"type": "datetime"}, _t("Never")); - return fct(); - }); - } - }, - reconcile: function() { - var self = this; - var ids = this.get_selected_ids(); - if (ids.length === 0) { - new instance.web.Dialog(this, { - title: _t("Warning"), - size: 'medium', - }, $("
").text(_t("You must choose at least one record."))).open(); - return false; - } - - new instance.web.Model("ir.model.data").call("get_object_reference", ["account", "action_view_account_move_line_reconcile"]).then(function(result) { - var additional_context = _.extend({ - active_id: ids[0], - active_ids: ids, - active_model: self.model - }); - return self.rpc("/web/action/load", { - action_id: result[1], - context: additional_context - }).done(function (result) { - result.context = instance.web.pyeval.eval('contexts', [result.context, additional_context]); - result.flags = result.flags || {}; - result.flags.new_window = true; - return self.do_action(result, { - on_close: function () { - self.do_search(self.last_domain, self.last_context, self.last_group_by); - } - }); - }); - }); - }, - mark_as_reconciled: function() { - var self = this; - var id = self.partners[self.current_partner][0]; - new instance.web.Model("res.partner").call("mark_as_reconciled", [[id]]).then(function() { - self.do_search(self.last_domain, self.last_context, self.last_group_by); - }); - }, - do_select: function (ids, records) { - this.trigger('record_selected') - this._super.apply(this, arguments); - }, - }); - -}; diff --git a/addons/account/static/src/js/move_line_quickadd.js b/addons/account/static/src/js/move_line_quickadd.js new file mode 100644 index 00000000000..80b70fe03e0 --- /dev/null +++ b/addons/account/static/src/js/move_line_quickadd.js @@ -0,0 +1,105 @@ +odoo.define('account.move_line_quickadd', ['web.core', 'web.data', 'web.ListView', 'web.Model'], function (require) { +"use strict"; + +var core = require('web.core'); +var data = require('web.data'); +var ListView = require('web.ListView'); +var Model = require('web.Model'); + +var QWeb = core.qweb; + +var QuickAddListView = ListView.extend({ + init: function() { + this._super.apply(this, arguments); + this.journals = []; + this.periods = []; + this.current_journal = null; + this.current_period = null; + this.default_period = null; + this.default_journal = null; + this.current_journal_type = null; + this.current_journal_currency = null; + this.current_journal_analytic = null; + }, + start:function(){ + var tmp = this._super.apply(this, arguments); + var self = this; + var defs = []; + this.$el.parent().prepend(QWeb.render("AccountMoveLineQuickAdd", {widget: this})); + + this.$el.parent().find('.oe_account_select_journal').change(function() { + self.current_journal = this.value === '' ? null : parseInt(this.value); + self.do_search(self.last_domain, self.last_context, self.last_group_by); + }); + this.$el.parent().find('.oe_account_select_period').change(function() { + self.current_period = this.value === '' ? null : parseInt(this.value); + self.do_search(self.last_domain, self.last_context, self.last_group_by); + }); + this.on('edit:after', this, function () { + self.$el.parent().find('.oe_account_select_journal').attr('disabled', 'disabled'); + self.$el.parent().find('.oe_account_select_period').attr('disabled', 'disabled'); + }); + this.on('save:after cancel:after', this, function () { + self.$el.parent().find('.oe_account_select_journal').removeAttr('disabled'); + self.$el.parent().find('.oe_account_select_period').removeAttr('disabled'); + }); + var mod = new Model("account.move.line", self.dataset.context, self.dataset.domain); + defs.push(mod.call("default_get", [['journal_id','period_id'],self.dataset.context]).then(function(result) { + self.current_period = result['period_id']; + self.current_journal = result['journal_id']; + })); + defs.push(mod.call("list_journals", []).then(function(result) { + self.journals = result; + })); + defs.push(mod.call("list_periods", []).then(function(result) { + self.periods = result; + })); + return $.when(tmp, defs); + }, + do_search: function(domain, context, group_by) { + var self = this; + this.last_domain = domain; + this.last_context = context; + this.last_group_by = group_by; + this.old_search = _.bind(this._super, this); + var o; + self.$el.parent().find('.oe_account_select_journal').children().remove().end(); + self.$el.parent().find('.oe_account_select_journal').append(new Option('', '')); + for (var i = 0;i < self.journals.length;i++){ + o = new Option(self.journals[i][1], self.journals[i][0]); + if (self.journals[i][0] === self.current_journal){ + self.current_journal_type = self.journals[i][2]; + self.current_journal_currency = self.journals[i][3]; + self.current_journal_analytic = self.journals[i][4]; + $(o).attr('selected',true); + } + self.$el.parent().find('.oe_account_select_journal').append(o); + } + self.$el.parent().find('.oe_account_select_period').children().remove().end(); + self.$el.parent().find('.oe_account_select_period').append(new Option('', '')); + for (var i = 0;i < self.periods.length;i++){ + o = new Option(self.periods[i][1], self.periods[i][0]); + self.$el.parent().find('.oe_account_select_period').append(o); + } + self.$el.parent().find('.oe_account_select_period').val(self.current_period).attr('selected',true); + return self.search_by_journal_period(); + }, + search_by_journal_period: function() { + var self = this; + var domain = []; + if (self.current_journal !== null) domain.push(["journal_id", "=", self.current_journal]); + if (self.current_period !== null) domain.push(["period_id", "=", self.current_period]); + self.last_context["journal_id"] = self.current_journal === null ? false : self.current_journal; + if (self.current_period === null) delete self.last_context["period_id"]; + else self.last_context["period_id"] = self.current_period; + self.last_context["journal_type"] = self.current_journal_type; + self.last_context["currency"] = self.current_journal_currency; + self.last_context["analytic_journal_id"] = self.current_journal_analytic; + var compound_domain = new data.CompoundDomain(self.last_domain, domain); + self.dataset.domain = compound_domain.eval(); + return self.old_search(compound_domain, self.last_context, self.last_group_by); + }, +}); +core.view_registry.add('tree_account_move_line_quickadd', QuickAddListView); + +}); diff --git a/addons/account/static/src/js/tour_bank_statement_reconciliation.js b/addons/account/static/src/js/tour_bank_statement_reconciliation.js new file mode 100644 index 00000000000..8c6cf843f5b --- /dev/null +++ b/addons/account/static/src/js/tour_bank_statement_reconciliation.js @@ -0,0 +1,150 @@ +odoo.define('account.tour_bank_statement_reconciliation', ['web.core', 'web.Tour'], function(require) { +'use strict'; + +var core = require('web.core'); +var Tour = require('web.Tour'); + +var _t = core._t; + +Tour.register({ + id: 'bank_statement_reconciliation', + name: _t("Reconcile the demo bank statement"), + path: '/web', + mode: 'test', + // TODO : identify menu by data-menu attr or text node ? + steps: [ + // Go to the first statement reconciliation + { + title: "go to accounting", + element: '.oe_menu_toggler:contains("Accounting"):visible', + }, + { + title: "go to bank statements", + element: '.oe_menu_leaf:contains("Bank Statement"):visible', + }, + { + title: "select first bank statement", + element: '.oe_list_content tbody tr:contains("BNK/2014/001")', + }, + { + title: "click the reconcile button", + element: '.oe_form_container header button:contains("Reconcile")', + }, + + + // Check mutual exclusion of move lines + { + title: "set second reconciliation in match mode", + element: '.oe_bank_statement_reconciliation_line:nth-child(2) .initial_line' + }, + { + title: "deselect SAJ/2014/002 from second reconciliation", + element: '.oe_bank_statement_reconciliation_line:nth-child(2) .accounting_view .mv_line:contains("SAJ/2014/002")' + }, + { + title: "check it appeared in first reconciliation's matches list and select SAJ/2014/002 in second reconciliation", + waitNot: '.oe_bank_statement_reconciliation_line:nth-child(2) .accounting_view .mv_line:contains("SAJ/2014/002")', + waitFor: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/002")', + element: '.oe_bank_statement_reconciliation_line:nth-child(2) .mv_line:contains("SAJ/2014/002")' + }, + + + // Make a partial reconciliation + { + title: "select SAJ/2014/001", + element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/001")' + }, + { + title: "click on the partial reconciliation button", + element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/001") .do_partial_reconcile_button' + }, + { + title: "click on the OK button", + element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight' + }, + + + // Test changing the partner + { + title: "change the partner (1)", + waitNot: '.oe_bank_statement_reconciliation_line:nth-child(4)', // wait for the reconciliation to be processed + element: '.oe_bank_statement_reconciliation_line:first-child .partner_name' + }, + { + title: "change the partner (2)", + element: '.oe_bank_statement_reconciliation_line:first-child .change_partner_container input', + sampleText: 'Vauxoo', + }, + { + title: "change the partner (3)", + element: '.ui-autocomplete .ui-menu-item:contains("Vauxoo")' + }, + { + title: "check the reconciliation is reloaded and has no match", + element: '.oe_bank_statement_reconciliation_line:first-child.no_match', + }, + { + title: "change the partner back (1)", + element: '.oe_bank_statement_reconciliation_line:first-child .partner_name' + }, + { + title: "change the partner back (2)", + element: '.oe_bank_statement_reconciliation_line:first-child .change_partner_container input', + sampleText: 'Best Designers', + }, + { + title: "change the partner back (3)", + element: '.ui-autocomplete .ui-menu-item:contains("Best Designers")' + }, + { + title: "select SAJ/2014/002", + element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/002")' + }, + { + title: "click on the OK button", + element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight' + }, + + + // Create a new move line in first reconciliation and validate it + { + title: "check following reconciliation passes in mode create", + waitNot: '.oe_bank_statement_reconciliation_line:nth-child(3)', // wait for the reconciliation to be processed + element: '.oe_bank_statement_reconciliation_line:first-child[data-mode="create"]' + }, + { + title: "click the Profit/Loss preset", + element: '.oe_bank_statement_reconciliation_line:first-child button:contains("Profit / Loss")' + }, + { + title: "click on the OK button", + element: '.oe_bank_statement_reconciliation_line:first-child .button_ok.oe_highlight' + }, + + + // Leave an open balance + { + title: "select SAJ/2014/003", + waitNot: '.oe_bank_statement_reconciliation_line:nth-child(2)', // wait for the reconciliation to be processed + element: '.oe_bank_statement_reconciliation_line:first-child .mv_line:contains("SAJ/2014/003")' + }, + { + title: "click on the Keep Open button", + element: '.oe_bank_statement_reconciliation_line:first-child .button_ok:not(.oe_highlight)' + }, + + + // Be done + { + title: "check 'finish screen' and close the statement", + waitFor: '.done_message', + element: '.button_close_statement' + }, + { + title: "check the statement is closed", + element: '.oe_form_container header .label:contains("Closed")' + }, + ] +}); + +}); diff --git a/addons/account/static/src/js/widgets.js b/addons/account/static/src/js/widgets.js new file mode 100644 index 00000000000..e93598b78b6 --- /dev/null +++ b/addons/account/static/src/js/widgets.js @@ -0,0 +1,1898 @@ +odoo.define('account.widgets', ['web.ActionManager', 'web.core', 'web.CrashManager', 'web.data', 'web.Dialog', 'web.formats', 'web.FormView', 'web.ListView', 'web.Model', 'web.pyeval', 'web.web_client', 'web.Widget'], function (require) { +"use strict"; + +var ActionManager = require('web.ActionManager'); +var core = require('web.core'); +var CrashManager = require('web.CrashManager'); +var data = require('web.data'); +var Dialog = require('web.Dialog'); +var formats = require('web.formats'); +var FormView = require('web.FormView'); +var ListView = require('web.ListView'); +var Model = require('web.Model'); +var pyeval = require('web.pyeval'); +var web_client = require('web.web_client'); +var Widget = require('web.Widget'); + +var _t = core._t; +var QWeb = core.qweb; + + +var bankStatementReconciliation = Widget.extend({ + className: 'oe_bank_statement_reconciliation', + + events: { + "click *[rel='do_action']": "doActionClickHandler", + "click .statement_name span": "statementNameClickHandler", + "keyup .change_statement_name_field": "changeStatementNameFieldHandler", + "click .change_statement_name_button": "changeStatementButtonClickHandler", + "click .show_more": "showMoreButtonClickHandler", + }, + + init: function(parent, context) { + this._super(parent); + // Number of reconciliations loaded initially and by clicking 'show more' + this.num_reconciliations_fetched_in_batch = 10; + if (context.context.statement_id) this.statement_ids = [context.context.statement_id]; + if (context.context.statement_ids) this.statement_ids = context.context.statement_ids; + this.single_statement = this.statement_ids !== undefined && this.statement_ids.length === 1; + this.multiple_statements = this.statement_ids !== undefined && this.statement_ids.length > 1; + this.title = context.context.title || _t("Reconciliation"); + this.notifications = context.context.notifications || []; + this.st_lines = []; + this.last_displayed_reconciliation_index = undefined; // Flow control + this.reconciled_lines = 0; // idem + this.already_reconciled_lines = 0; // Number of lines of the statement which were already reconciled + this.model_bank_statement = new Model("account.bank.statement"); + this.model_bank_statement_line = new Model("account.bank.statement.line"); + this.reconciliation_menu_id = false; // Used to update the needaction badge + this.formatCurrency; // Method that formats the currency ; loaded from the server + this.action_manager = this.findAncestor(function(ancestor){ return ancestor instanceof ActionManager; }); + + // Only for statistical purposes + this.lines_reconciled_with_ctrl_enter = 0; + this.time_widget_loaded = Date.now(); + + // Stuff used by the children bankStatementReconciliationLine + this.max_move_lines_displayed = 5; + this.animation_speed = 100; // "Blocking" animations + this.aestetic_animation_speed = 300; // eye candy + this.map_currency_id_rounding = {}; + this.presets = {}; + // We'll need to get the code of an account selected in a many2one (whose value is the id) + this.map_account_id_code = {}; + // The same move line cannot be selected for multiple resolutions + this.excluded_move_lines_ids = {}; + // Description of the fields to initialize in the "create new line" form + // NB : for presets to work correctly, a field id must be the same string as a preset field + var FieldMany2One = core.form_widget_registry.get('many2one'); + var FieldChar = core.form_widget_registry.get('char'); + var FieldFloat = core.form_widget_registry.get('float'); + + this.create_form_fields = { + account_id: { + id: "account_id", + index: 0, + corresponding_property: "account_id", // a account.move field name + label: _t("Account"), + required: true, + tabindex: 10, + constructor: FieldMany2One, + field_properties: { + relation: "account.account", + string: _t("Account"), + type: "many2one", + domain: [['type','not in',['view', 'closed', 'consolidation']]], + }, + }, + label: { + id: "label", + index: 1, + corresponding_property: "label", + label: _t("Label"), + required: true, + tabindex: 11, + constructor: FieldChar, + field_properties: { + string: _t("Label"), + type: "char", + }, + }, + tax_id: { + id: "tax_id", + index: 2, + corresponding_property: "tax_id", + label: _t("Tax"), + required: false, + tabindex: 12, + constructor: FieldMany2One, + field_properties: { + relation: "account.tax", + string: _t("Tax"), + type: "many2one", + domain: [['type_tax_use','in',['purchase', 'all']], ['parent_id', '=', false]], + }, + }, + amount: { + id: "amount", + index: 3, + corresponding_property: "amount", + label: _t("Amount"), + required: true, + tabindex: 13, + constructor: FieldFloat, + field_properties: { + string: _t("Amount"), + type: "float", + }, + }, + analytic_account_id: { + id: "analytic_account_id", + index: 4, + corresponding_property: "analytic_account_id", + label: _t("Analytic Acc."), + required: false, + tabindex: 14, + group:"analytic.group_analytic_accounting", + constructor: FieldMany2One, + field_properties: { + relation: "account.analytic.account", + string: _t("Analytic Acc."), + type: "many2one", + domain: [['type', '!=', 'view'], ['state', 'not in', ['close','cancelled']]], + }, + }, + }; + }, + + start: function() { + this._super(); + var self = this; + // Retreive statement infos and reconciliation data from the model + var lines_filter = [['journal_entry_ids', '=', false], ['account_id', '=', false]]; + var deferred_promises = []; + + // Working on specified statement(s) + if (self.statement_ids && self.statement_ids.length > 0) { + lines_filter.push(['statement_id', 'in', self.statement_ids]); + + // If only one statement, display its name as title and allow to modify it + if (self.single_statement) { + deferred_promises.push(self.model_bank_statement + .query(["name"]) + .filter([['id', '=', self.statement_ids[0]]]) + .first() + .then(function(title){ + self.title = title.name; + }) + ); + } + // Anyway, find out how many statement lines are reconciled (for the progressbar) + deferred_promises.push(self.model_bank_statement + .call("number_of_lines_reconciled", [self.statement_ids]) + .then(function(num) { + self.already_reconciled_lines = num; + }) + ); + } + + // Get operation templates + deferred_promises.push(new Model("account.statement.operation.template") + .query(['id','name','account_id','label','amount_type','amount','tax_id','analytic_account_id']) + .all().then(function (data) { + _(data).each(function(preset){ + self.presets[preset.id] = preset; + }); + }) + ); + + // Get the function to format currencies + deferred_promises.push(new Model("res.currency") + .call("get_format_currencies_js_function") + .then(function(data) { + self.formatCurrency = new Function("amount, currency_id", data); + }) + ); + + // Get statement lines + deferred_promises.push(self.model_bank_statement_line + .query(['id']) + .filter(lines_filter) + .order_by('statement_id, id') + .all().then(function (data) { + self.st_lines = _(data).map(function(o){ return o.id; }); + }) + ); + + // When queries are done, render template and reconciliation lines + return $.when.apply($, deferred_promises).then(function(){ + + // If there is no statement line to reconcile, stop here + if (self.st_lines.length === 0) { + self.$el.prepend(QWeb.render("bank_statement_nothing_to_reconcile")); + return; + } + + // Create a dict account id -> account code for display facilities + new Model("account.account") + .query(['id', 'code']) + .all().then(function(data) { + _.each(data, function(o) { self.map_account_id_code[o.id] = o.code; }); + }); + + // Create a dict currency id -> rounding factor + new Model("res.currency") + .query(['id', 'rounding']) + .all().then(function(data) { + _.each(data, function(o) { self.map_currency_id_rounding[o.id] = o.rounding; }); + }); + + new Model("ir.model.data") + .call("xmlid_to_res_id", ["account.menu_bank_reconcile_bank_statements"]) + .then(function(data) { + self.reconciliation_menu_id = data; + self.doReloadMenuReconciliation(); + }); + + // Bind keyboard events TODO : méthode standard ? + $("body").on("keypress", function (e) { + self.keyboardShortcutsHandler(e); + }); + + // Render and display + self.$el.prepend(QWeb.render("bank_statement_reconciliation", { + title: self.title, + single_statement: self.single_statement, + total_lines: self.already_reconciled_lines+self.st_lines.length + })); + self.updateProgressbar(); + var reconciliations_to_show = self.st_lines.slice(0, self.num_reconciliations_fetched_in_batch); + self.last_displayed_reconciliation_index = reconciliations_to_show.length; + self.$(".reconciliation_lines_container").css("opacity", 0); + + // Display the reconciliations + return self.model_bank_statement_line + .call("get_data_for_reconciliations", [reconciliations_to_show]) + .then(function (data) { + var child_promises = []; + var datum; + while ((datum = data.shift()) !== undefined) + child_promises.push(self.displayReconciliation(datum.st_line.id, 'inactive', false, true, datum.st_line, datum.reconciliation_proposition)); + $.when.apply($, child_promises).then(function(){ + self.$(".reconciliation_lines_container").animate({opacity: 1}, self.aestetic_animation_speed); + self.getChildren()[0].set("mode", "match"); + self.updateShowMoreButton(); + if (self.notifications) { + self.displayNotifications(self.notifications); + } + }); + }); + }); + }, + + doActionClickHandler: function(e) { + var name = e.currentTarget.dataset.action_nam; + var model = e.currentTarget.dataset.model; + var ids = e.currentTarget.dataset.ids.split(",").map(Number); + this.action_manager.do_action({ + name: name, + res_model: model, + domain: [['id', 'in', ids]], + views: [[false, 'list'], [false, 'form']], + type: 'ir.actions.act_window', + view_type: "list", + view_mode: "list" + }); + }, + + displayNotifications: function(notifications, speed) { + speed = speed === undefined ? this.aestetic_animation_speed : speed; + for (var i=0; i= 1) time_taken = Math.floor(sec_taken/60) +"' "+ sec_taken%60 +"''"; + else time_taken = sec_taken%60 +" seconds"; + + var title; + if (sec_per_item < 5) title = _t("Whew, that was fast !") + " "; + else title = _t("Congrats, you're all done !") + " "; + + if (self.lines_reconciled_with_ctrl_enter === self.reconciled_lines) + achievements.push({ + title: _t("Efficiency at its finest"), + desc: _t("Only use the ctrl-enter shortcut to validate reconciliations."), + icon: "fa-keyboard-o"} + ); + + if (sec_per_item < 5) + achievements.push({ + title: _t("Fast reconciler"), + desc: _t("Take on average less than 5 seconds to reconcile a transaction."), + icon: "fa-bolt"} + ); + + // Render it + self.$(".protip").hide(); + self.updateShowMoreButton(); + self.$(".oe_form_sheet").append(QWeb.render("bank_statement_reconciliation_done_message", { + title: title, + time_taken: time_taken, + sec_per_item: sec_per_item, + transactions_done: self.reconciled_lines, + done_with_ctrl_enter: self.lines_reconciled_with_ctrl_enter, + achievements: achievements, + single_statement: self.single_statement, + multiple_statements: self.multiple_statements, + })); + + // Animate it + var container = $("
"); + self.$(".done_message").wrap(container).css("opacity", 0).css("position", "relative").css("left", "-50%"); + self.$(".done_message").animate({opacity: 1, left: 0}, self.aestetic_animation_speed*2, "easeOutCubic"); + self.$(".done_message").animate({opacity: 1}, self.aestetic_animation_speed*3, "easeOutCubic"); + + // Make it interactive + self.$(".achievement").popover({'placement': 'top', 'container': self.el, 'trigger': 'hover'}); + + if (self.$(".button_back_to_statement").length !== 0) { + self.$(".button_back_to_statement").click(function() { + self.goBackToStatementsTreeView(); + }); + } + + if (self.$(".button_close_statement").length !== 0) { + self.$(".button_close_statement").hide(); + self.model_bank_statement + .query(["balance_end_real", "balance_end"]) + .filter([['id', 'in', self.statement_ids]]) + .all() + .then(function(data){ + if (_.all(data, function(o) { return o.balance_end_real === o.balance_end; })) { + self.$(".button_close_statement").show(); + self.$(".button_close_statement").click(function() { + self.$(".button_close_statement").attr("disabled", "disabled"); + self.model_bank_statement + .call("button_confirm_bank", [self.statement_ids]) + .then(function () { + self.goBackToStatementsTreeView(); + }, function() { + self.$(".button_close_statement").removeAttr("disabled"); + }); + }); + } + }); + } + }, + + showMoreButtonClickHandler: function() { + this.displayReconciliations(this.num_reconciliations_fetched_in_batch); + }, + + updateShowMoreButton: function() { + var items_remaining = this.st_lines.length - this.last_displayed_reconciliation_index; + if (items_remaining > 0) + this.$(".show_more").show().find(".num_items_remaining").text(items_remaining); + else + this.$(".show_more").hide(); + }, + + updateProgressbar: function() { + var self = this; + var done = self.already_reconciled_lines + self.reconciled_lines; + var total = self.already_reconciled_lines + self.st_lines.length; + var prog_bar = self.$(".progress .progress-bar"); + prog_bar.attr("aria-valuenow", done); + prog_bar.css("width", (done/total*100)+"%"); + self.$(".progress .progress-text .valuenow").text(done); + }, + + /* reloads the needaction badge */ + doReloadMenuReconciliation: function () { + var menu = web_client.menu; + if (!menu || !this.reconciliation_menu_id) { + return $.when(); + } + return menu.rpc("/web/menu/load_needaction", {'menu_ids': [this.reconciliation_menu_id]}).done(function(r) { + menu.on_needaction_loaded(r); + }).then(function () { + menu.trigger("need_action_reloaded"); + }); + }, +}); +core.action_registry.add('bank_statement_reconciliation_view', bankStatementReconciliation); + +var bankStatementReconciliationLine = Widget.extend({ + className: 'oe_bank_statement_reconciliation_line', + + events: { + "click .change_partner": "changePartnerClickHandler", + "click .button_ok": "persistAndDestroy", + "click .mv_line": "moveLineClickHandler", + "click .initial_line": "initialLineClickHandler", + "click .line_open_balance": "lineOpenBalanceClickHandler", + "click .pager_control_left:not(.disabled)": "pagerControlLeftHandler", + "click .pager_control_right:not(.disabled)": "pagerControlRightHandler", + "keyup .filter": "filterHandler", + "click .line_info_button": function(e){e.stopPropagation();}, // small usability hack + "click .add_line": "addLineBeingEdited", + "click .preset": "presetClickHandler", + "click .do_partial_reconcile_button": "doPartialReconcileButtonClickHandler", + "click .undo_partial_reconcile_button": "undoPartialReconcileButtonClickHandler", + }, + + init: function(parent, context) { + this._super(parent); + + this.formatCurrency = this.getParent().formatCurrency; + if (context.initial_data_provided) { + // Process data + _.each(context.reconciliation_proposition, function(line) { + this.decorateMoveLine(line, context.st_line.currency_id); + }, this); + this.set("mv_lines_selected", context.reconciliation_proposition); + this.st_line = context.st_line; + this.partner_id = context.st_line.partner_id; + this.decorateStatementLine(this.st_line); + + // Exclude selected move lines + if (this.getParent().excluded_move_lines_ids[this.partner_id] === undefined) + this.getParent().excluded_move_lines_ids[this.partner_id] = []; + this.getParent().excludeMoveLines(this, this.partner_id, context.reconciliation_proposition); + } else { + this.set("mv_lines_selected", []); + this.st_line = undefined; + this.partner_id = undefined; + } + + this.context = context; + this.st_line_id = context.st_line_id; + this.max_move_lines_displayed = this.getParent().max_move_lines_displayed; + this.animation_speed = this.getParent().animation_speed; + this.aestetic_animation_speed = this.getParent().aestetic_animation_speed; + this.model_bank_statement_line = new Model("account.bank.statement.line"); + this.model_res_users = new Model("res.users"); + this.model_tax = new Model("account.tax"); + this.map_currency_id_rounding = this.getParent().map_currency_id_rounding; + this.map_account_id_code = this.getParent().map_account_id_code; + this.presets = this.getParent().presets; + this.is_valid = true; + this.is_consistent = true; // Used to prevent bad server requests + this.is_rapprochement; // matching a statement line with an already reconciled journal item + this.can_fetch_more_move_lines; // Tell if we can show more move lines + this.filter = ""; + // In rare cases like when deleting a statement line's partner we don't want the server to + // look for a reconciliation proposition (in this particular case it might find a move line + // matching the statement line and decide to set the statement line's partner accordingly) + this.do_load_reconciliation_proposition = true; + + this.set("mode", undefined); + this.on("change:mode", this, this.modeChanged); + this.set("balance", undefined); // Debit is +, credit is - + this.on("change:balance", this, this.balanceChanged); + this.set("pager_index", 0); + this.on("change:pager_index", this, this.pagerChanged); + // NB : mv_lines represent the counterpart that will be created to reconcile existing move lines, so debit and credit are inverted + this.set("mv_lines", []); + this.on("change:mv_lines", this, this.mvLinesChanged); + this.mv_lines_deselected = []; // deselected lines are displayed on top of the match table + this.on("change:mv_lines_selected", this, this.mvLinesSelectedChanged); + this.set("lines_created", []); + this.set("line_created_being_edited", [{'id': 0}]); + this.on("change:lines_created", this, this.createdLinesChanged); + this.on("change:line_created_being_edited", this, this.createdLinesChanged); + }, + + start: function() { + var self = this; + return self._super().then(function() { + // no animation while loading + self.animation_speed = 0; + self.aestetic_animation_speed = 0; + + self.is_consistent = false; + if (self.context.animate_entrance) { + self.$el.fadeOut(0); + self.$el.slideUp(0); + } + return $.when(self.loadData()).then(function(){ + return $.when(self.render()).then(function(){ + self.is_consistent = true; + // Make an entrance + self.animation_speed = self.getParent().animation_speed; + self.aestetic_animation_speed = self.getParent().aestetic_animation_speed; + if (self.context.animate_entrance) { + return self.$el.stop(true, true).fadeIn({ duration: self.aestetic_animation_speed, queue: false }).css('display', 'none').slideDown(self.aestetic_animation_speed); + } + }); + }); + }); + }, + + loadData: function() { + var self = this; + if (self.context.initial_data_provided) + return; + + // Get ids of selected move lines (to exclude them from reconciliation proposition) + var excluded_move_lines_ids = []; + if (self.do_load_reconciliation_proposition) { + _.each(self.getParent().excluded_move_lines_ids, function(o){ + excluded_move_lines_ids = excluded_move_lines_ids.concat(o); + }); + } + // Load statement line + return self.model_bank_statement_line + .call("get_data_for_reconciliations", [[self.st_line_id], excluded_move_lines_ids, self.do_load_reconciliation_proposition]) + .then(function (data) { + self.st_line = data[0].st_line; + self.decorateStatementLine(self.st_line); + self.partner_id = data[0].st_line.partner_id; + if (self.getParent().excluded_move_lines_ids[self.partner_id] === undefined) + self.getParent().excluded_move_lines_ids[self.partner_id] = []; + var mv_lines = []; + _.each(data[0].reconciliation_proposition, function(line) { + self.decorateMoveLine(line, self.st_line.currency_id); + mv_lines.push(line); + }, self); + self.set("mv_lines_selected", self.get("mv_lines_selected").concat(mv_lines)); + }); + }, + + render: function() { + var self = this; + var presets_array = []; + for (var id in self.presets) + if (self.presets.hasOwnProperty(id)) + presets_array.push(self.presets[id]); + self.$el.prepend(QWeb.render("bank_statement_reconciliation_line", { + line: self.st_line, + mode: self.context.mode, + presets: presets_array + })); + + // Stuff that require the template to be rendered + self.$(".match").slideUp(0); + self.$(".create").slideUp(0); + if (self.st_line.no_match) self.$el.addClass("no_match"); + self.bindPopoverTo(self.$(".line_info_button")); + self.createFormWidgets(); + // Special case hack : no identified partner + if (self.st_line.has_no_partner) { + self.$el.css("opacity", "0"); + self.updateBalance(); + self.$(".change_partner_container").show(0); + self.$(".match").slideUp(0); + self.$el.addClass("no_partner"); + self.set("mode", self.context.mode); + self.balanceChanged(); + self.updateAccountingViewMatchedLines(); + self.animation_speed = self.getParent().animation_speed; + self.aestetic_animation_speed = self.getParent().aestetic_animation_speed; + self.$el.animate({opacity: 1}, self.aestetic_animation_speed); + return; + } + + // TODO : the .on handler's returned deferred is lost + return $.when(self.set("mode", self.context.mode)).then(function(){ + // Make sure the display is OK + self.balanceChanged(); + self.createdLinesChanged(); + self.updateAccountingViewMatchedLines(); + }); + }, + + restart: function(mode) { + var self = this; + mode = (mode === undefined ? 'inactive' : mode); + self.context.animate_entrance = false; + self.$el.css("height", self.$el.outerHeight()); + // Destroy everything + _.each(self.getChildren(), function(o){ o.destroy(); }); + self.is_consistent = false; + return $.when(self.$el.animate({opacity: 0}, self.animation_speed)).then(function() { + self.getParent().unexcludeMoveLines(self, self.partner_id, self.get("mv_lines_selected")); + $.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy'); }); + self.$el.empty(); + self.$el.removeClass("no_partner"); + self.context.mode = mode; + self.context.initial_data_provided = false; + self.is_valid = true; + self.is_consistent = true; + self.filter = ""; + self.set("balance", undefined, {silent: true}); + self.set("mode", undefined, {silent: true}); + self.set("pager_index", 0, {silent: true}); + self.set("mv_lines", [], {silent: true}); + self.set("mv_lines_selected", [], {silent: true}); + self.mv_lines_deselected = []; + self.set("lines_created", [], {silent: true}); + self.set("line_created_being_edited", [{'id': 0}], {silent: true}); + // Rebirth + return $.when(self.start()).then(function() { + self.$el.css("height", "auto"); + self.is_consistent = true; + self.$el.animate({opacity: 1}, self.animation_speed); + }); + }); + }, + + /* create form widgets, append them to the dom and bind their events handlers */ + createFormWidgets: function() { + var self = this; + var create_form_fields = self.getParent().create_form_fields; + var create_form_fields_arr = []; + for (var key in create_form_fields) + if (create_form_fields.hasOwnProperty(key)) + create_form_fields_arr.push(create_form_fields[key]); + create_form_fields_arr.sort(function(a, b){ return b.index - a.index; }); + + // field_manager + var dataset = new data.DataSet(this, "account.account", self.context); + dataset.ids = []; + dataset.arch = { + attrs: { string: "Stéphanie de Monaco", version: "7.0", class: "oe_form_container" }, + children: [], + tag: "form" + }; + + var field_manager = new FormView ( + this, dataset, false, { + initial_mode: 'edit', + disable_autofocus: false, + $buttons: $(), + $pager: $() + }); + + field_manager.load_form(dataset); + + // fields default properties + var Default_field = function() { + this.context = {}; + this.domain = []; + this.help = ""; + this.readonly = false; + this.required = true; + this.selectable = true; + this.states = {}; + this.views = {}; + }; + var Default_node = function(field_name) { + this.tag = "field"; + this.children = []; + this.required = true; + this.attrs = { + invisible: "False", + modifiers: '{"required":true}', + name: field_name, + nolabel: "True", + }; + }; + + // Append fields to the field_manager + field_manager.fields_view.fields = {}; + for (var i=0; i 0) balance_type = "greater"; + else if (self.get("balance") * self.st_line.amount < 0) balance_type = "lower"; + + // Adjust to different cases + if (balance_type === "equal") { + displayValidState(true); + } else if (balance_type === "greater") { + createOpenBalance("Create Write-off"); + } else if (balance_type === "lower") { + if (self.st_line.has_no_partner) { + createOpenBalance("Choose counterpart"); + } else { + displayValidState(false, "Keep open"); + createOpenBalance("Open balance"); + } + } + + // Show or hide partial reconciliation + if (self.get("mv_lines_selected").length > 0) { + var propose_partial = self.getCreatedLines().length === 0 + && self.get("mv_lines_selected").length === 1 + && balance_type === "greater" + && ! self.get("mv_lines_selected")[0].partial_reconcile + && ! self.is_rapprochement; + self.get("mv_lines_selected")[0].propose_partial_reconcile = propose_partial; + self.updateAccountingViewMatchedLines(); + } + + function displayValidState(higlight_ok_button, ok_button_text) { + self.is_valid = true; + self.$(".tip_reconciliation_not_balanced").hide(); + self.$(".button_ok").removeAttr("disabled"); + if (higlight_ok_button) self.$(".button_ok").addClass("btn-primary"); + if (ok_button_text !== undefined) self.$(".button_ok").text(ok_button_text); + } + + function createOpenBalance(name) { + var balance = self.get("balance"); + var amount = self.formatCurrency(Math.abs(balance), self.st_line.currency_id); + var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", { + debit: balance > 0 ? amount : "", + credit: balance < 0 ? amount : "", + account_code: self.map_account_id_code[self.st_line.open_balance_account_id] + })); + if (name !== undefined) + $line.find(".cell_label").text(name); + self.$(".tbody_open_balance").empty().append($line); + } + }, + + modeChanged: function(o, val) { + var self = this; + + self.$(".action_pane.active").removeClass("active"); + + if (val.oldValue === "create") + self.addLineBeingEdited(); + + if (self.get("mode") === "inactive") { + self.$(".match").slideUp(self.animation_speed); + self.$(".create").slideUp(self.animation_speed); + self.el.dataset.mode = "inactive"; + + } else if (self.get("mode") === "match") { + // TODO : remove this old_animation_speed / new_animation_speed hack + // when .on handler's returned deferred's no longer lost + var old_animation_speed = self.animation_speed; + return $.when(self.updateMatches()).then(function() { + var new_animation_speed = self.animation_speed; + self.animation_speed = old_animation_speed; + if (self.$el.hasClass("no_match")) { + self.animation_speed = 0; + self.set("mode", "create"); + return; + } + self.$(".match").slideDown(self.animation_speed); + self.$(".create").slideUp(self.animation_speed); + self.el.dataset.mode = "match"; + self.animation_speed = new_animation_speed; + }); + + } else if (self.get("mode") === "create") { + self.initializeCreateForm(); + self.$(".match").slideUp(self.animation_speed); + self.$(".create").slideDown(self.animation_speed); + self.el.dataset.mode = "create"; + } + }, + + pagerChanged: function() { + this.updateMatches(); + }, + + mvLinesChanged: function() { + var self = this; + // If pager_index is out of range, set it to display the last page + if (self.get("pager_index") !== 0 && self.get("mv_lines").length === 0 && ! self.can_fetch_more_move_lines) { + self.set("pager_index", 0); + } + + // If there is no match to display, disable match view and pass in mode inactive + if (self.get("mv_lines").length + self.mv_lines_deselected.length === 0 && !self.can_fetch_more_move_lines && self.filter === "") { + self.$el.addClass("no_match"); + if (self.get("mode") === "match") { + self.set("mode", "inactive"); + } + } else { + self.$el.removeClass("no_match"); + } + + _.each(self.get("mv_lines"), function(line) { + if (line.partial_reconciliation_siblings_ids.length > 0) { + var correct_format = _.collect(line.partial_reconciliation_siblings_ids, function(o) { return {'id': o}; }); + self.getParent().excludeMoveLines(self, self.partner_id, correct_format); + } + }); + + self.updateMatchView(); + self.updatePagerControls(); + }, + + mvLinesSelectedChanged: function(elt, val) { + var self = this; + + var added_lines = _.difference(val.newValue, val.oldValue); + var removed_lines = _.difference(val.oldValue, val.newValue); + + self.getParent().excludeMoveLines(self, self.partner_id, added_lines); + self.getParent().unexcludeMoveLines(self, self.partner_id, removed_lines); + + self.is_rapprochement = added_lines.length > 0 && added_lines[0].already_paid; + + $.when(self.updateMatches()).then(function(){ + self.updateAccountingViewMatchedLines(); + self.updateBalance(); + }); + }, + + // Generic function for updating the line_created_being_edited + formCreateInputChanged: function(elt, val) { + var self = this; + var line_created_being_edited = self.get("line_created_being_edited"); + line_created_being_edited[0][elt.corresponding_property] = val.newValue; + line_created_being_edited[0].currency_id = self.st_line.currency_id; + + // Specific cases + if (elt === self.account_id_field) + line_created_being_edited[0].account_num = self.map_account_id_code[elt.get("value")]; + + // Update tax line + var deferred_tax = new $.Deferred(); + if (elt === self.tax_id_field || elt === self.amount_field) { + var amount = self.amount_field.get("value"); + var tax_id = self.tax_id_field.get("value"); + if (amount && tax_id) { + deferred_tax = $.when(self.model_tax + .call("compute_for_bank_reconciliation", [tax_id, amount])) + .then(function(data){ + line_created_being_edited[0].amount_before_tax = (data.total.toFixed(3) === amount.toFixed(3) ? data.total : data.total_included); + line_created_being_edited[0].amount = data.total; + var current_line_cursor = 1; + $.each(data.taxes, function(index, tax){ + if (tax.amount !== 0.0) { + var tax_account_id = (amount > 0 ? tax.account_collected_id : tax.account_paid_id); + tax_account_id = tax_account_id !== false ? tax_account_id: line_created_being_edited[0].account_id; + line_created_being_edited[current_line_cursor] = { + id: line_created_being_edited[0].id, + account_id: tax_account_id, + account_num: self.map_account_id_code[tax_account_id], + label: tax.name, + amount: tax.amount, + no_remove_action: true, + currency_id: self.st_line.currency_id, + is_tax_line: true + }; + current_line_cursor = current_line_cursor + 1; + } + }); + } + ); + } else { + line_created_being_edited.length = 1; + deferred_tax.resolve(); + } + } else { deferred_tax.resolve(); } + + $.when(deferred_tax).then(function(){ + // Format amounts + var rounding = 1/self.map_currency_id_rounding[self.st_line.currency_id]; + $.each(line_created_being_edited, function(index, val) { + if (val.amount) { + line_created_being_edited[index].amount = Math.round(val.amount*rounding)/rounding; + line_created_being_edited[index].amount_str = self.formatCurrency(Math.abs(val.amount), val.currency_id); + } + if (val.amount_before_tax) + line_created_being_edited[index].amount_before_tax = Math.round(val.amount_before_tax*rounding)/rounding; + }); + self.set("line_created_being_edited", line_created_being_edited); + self.createdLinesChanged(); // TODO For some reason, previous line doesn't trigger change handler + }); + }, + + createdLinesChanged: function() { + var self = this; + self.updateAccountingViewCreatedLines(); + self.updateBalance(); + + if (self.islineCreatedBeingEditedValid()) self.$(".add_line").show(); + else self.$(".add_line").hide(); + }, + + + /** Model */ + + doPartialReconcileButtonClickHandler: function(e) { + var self = this; + + var line_id = $(e.currentTarget).closest("tr").data("lineid"); + var line = _.find(self.get("mv_lines_selected"), function(o) { return o.id == line_id; }); + self.partialReconcileLine(line); + + $(e.currentTarget).popover('destroy'); + self.updateAccountingViewMatchedLines(); + self.updateBalance(); + e.stopPropagation(); + }, + + partialReconcileLine: function(line) { + var self = this; + var balance = self.get("balance"); + line.initial_amount = line.debit !== 0 ? line.debit : -1 * line.credit; + if (balance < 0) { + line.debit += balance; + line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id); + } else { + line.credit -= balance; + line.credit_str = self.formatCurrency(line.credit, self.st_line.currency_id); + } + line.propose_partial_reconcile = false; + line.partial_reconcile = true; + }, + + undoPartialReconcileButtonClickHandler: function(e) { + var self = this; + + var line_id = $(e.currentTarget).closest("tr").data("lineid"); + var line = _.find(self.get("mv_lines_selected"), function(o) { return o.id == line_id; }); + self.unpartialReconcileLine(line); + + $(e.currentTarget).popover('destroy'); + self.updateAccountingViewMatchedLines(); + self.updateBalance(); + e.stopPropagation(); + }, + + unpartialReconcileLine: function(line) { + var self = this; + if (line.initial_amount > 0) { + line.debit = line.initial_amount; + line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id); + } else { + line.credit = -1 * line.initial_amount; + line.credit_str = self.formatCurrency(line.credit, self.st_line.currency_id); + } + line.propose_partial_reconcile = true; + line.partial_reconcile = false; + }, + + updateBalance: function() { + var self = this; + var mv_lines_selected = self.get("mv_lines_selected"); + var lines_selected_num = mv_lines_selected.length; + + // Undo partial reconciliation if necessary + if (lines_selected_num !== 1) { + _.each(mv_lines_selected, function(line) { + if (line.partial_reconcile === true) self.unpartialReconcileLine(line); + if (line.propose_partial_reconcile === true) line.propose_partial_reconcile = false; + }); + self.updateAccountingViewMatchedLines(); + } + + // Compute balance + var balance = 0; + balance -= self.st_line.amount; + _.each(mv_lines_selected, function(o) { + balance = balance - o.debit + o.credit; + }); + _.each(self.getCreatedLines(), function(o) { + balance += o.amount; + }); + // Dealing with floating-point + balance = Math.round(balance*1000)/1000; + + self.set("balance", balance); + + }, + + // Loads move lines according to the widget's state + updateMatches: function() { + var self = this; + var deselected_lines_num = self.mv_lines_deselected.length; + var offset = self.get("pager_index") * self.max_move_lines_displayed - deselected_lines_num; + if (offset < 0) offset = 0; + var limit = (self.get("pager_index")+1) * self.max_move_lines_displayed - deselected_lines_num; + if (limit > self.max_move_lines_displayed) limit = self.max_move_lines_displayed; + var excluded_ids = _.collect(self.get("mv_lines_selected").concat(self.mv_lines_deselected), function(o) { return o.id; }); + var globally_excluded_ids = []; + if (self.st_line.has_no_partner) + _.each(self.getParent().excluded_move_lines_ids, function(o) { globally_excluded_ids = globally_excluded_ids.concat(o); }); + else + globally_excluded_ids = self.getParent().excluded_move_lines_ids[self.partner_id]; + if (globally_excluded_ids !== undefined) + for (var i=0; i 0) { + return self.model_bank_statement_line + .call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, offset, limit]) + .then(function (lines) { + _.each(lines, function(line) { self.decorateMoveLine(line, self.st_line.currency_id); }, self); + // If we could fetch 1 more item than what we'll display, that means there are move lines left to be displayed (so we enable the pager) + self.can_fetch_more_move_lines = (lines.length === limit); + self.set("mv_lines", lines.slice(0, limit-1)); + }); + } else { + self.set("mv_lines", []); + } + }, + + // Changes the partner_id of the statement_line in the DB and reloads the widget + changePartner: function(partner_id) { + var self = this; + self.is_consistent = false; + return self.model_bank_statement_line + // Update model + .call("write", [[self.st_line_id], {'partner_id': partner_id}]) + .then(function () { + self.do_load_reconciliation_proposition = false; // of the server might set the statement line's partner + self.animation_speed = 0; + return $.when(self.restart(self.get("mode"))).then(function(){ + self.do_load_reconciliation_proposition = true; + self.is_consistent = true; + self.set("mode", "match"); + }); + }); + }, + + // Returns an object that can be passed to process_reconciliation() + prepareSelectedMoveLineForPersisting: function(line) { + return { + name: line.name, + debit: line.debit, + credit: line.credit, + counterpart_move_line_id: line.id, + already_paid: line.already_paid, + }; + }, + + // idem + prepareCreatedMoveLineForPersisting: function(line) { + var dict = {}; + if (dict['account_id'] === undefined) + dict['account_id'] = line.account_id; + dict['name'] = line.label; + var amount = line.tax_id ? line.amount_before_tax: line.amount; + if (amount > 0) dict['credit'] = amount; + if (amount < 0) dict['debit'] = -1 * amount; + if (line.tax_id) dict['account_tax_id'] = line.tax_id; + if (line.analytic_account_id) dict['analytic_account_id'] = line.analytic_account_id; + + return dict; + }, + + // idem + prepareOpenBalanceForPersisting: function() { + var balance = this.get("balance"); + var dict = {}; + + dict['account_id'] = this.st_line.open_balance_account_id; + dict['name'] = this.st_line.name + ' : ' + _t("Open balance"); + if (balance > 0) dict['debit'] = balance; + if (balance < 0) dict['credit'] = -1*balance; + + return dict; + }, + + makeMoveLineDicts: function() { + var self = this; + var mv_line_dicts = []; + _.each(self.get("mv_lines_selected"), function(l) { mv_line_dicts.push(self.prepareSelectedMoveLineForPersisting(l)); }); + var created_lines = _.filter(self.getCreatedLines(), function(l) { return ! l.is_tax_line; }); // Tax lines are created via account_tax_id + _.each(created_lines, function(l) { mv_line_dicts.push(self.prepareCreatedMoveLineForPersisting(l)); }); + if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting()); + return mv_line_dicts; + }, + + // Persist data, notify parent view and terminate widget + persistAndDestroy: function(speed) { + var self = this; + speed = (isNaN(speed) ? self.animation_speed : speed); + if (! self.is_consistent) return; + + // Sliding animation + var height = self.$el.outerHeight(); + var container = $("
"); + container.css("height", height) + .css("marginTop", self.$el.css("marginTop")) + .css("marginBottom", self.$el.css("marginBottom")); + self.$el.wrap(container); + var deferred_animation = self.$el.parent().slideUp(speed*height/150); + + // RPC + self.$(".button_ok").attr("disabled", "disabled"); + return self.model_bank_statement_line + .call("process_reconciliation", [self.st_line_id, self.makeMoveLineDicts()]) + .done(function () { + self.getParent().unexcludeMoveLines(self, self.partner_id, self.get("mv_lines_selected")); + $.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy'); }); + return $.when(deferred_animation).then(function(){ + self.$el.parent().remove(); + var parent = self.getParent(); + return $.when(self.destroy()).then(function() { + parent.childValidated(self); + }); + }); + }).fail(function(){ + self.$el.parent().slideDown(speed*height/150, function(){ + self.$el.unwrap(); + }); + }).always(function() { + self.$(".button_ok").removeAttr("disabled"); + }); + }, +}); + +var ReconciliationListView = ListView.extend({ + init: function() { + this._super.apply(this, arguments); + var self = this; + this.current_partner = null; + this.on('record_selected', this, function() { + if (self.get_selected_ids().length === 0) { + self.$(".oe_account_recon_reconcile").attr("disabled", ""); + } else { + self.$(".oe_account_recon_reconcile").removeAttr("disabled"); + } + }); + }, + load_list: function() { + var self = this; + var tmp = this._super.apply(this, arguments); + if (this.partners) { + this.$el.prepend(QWeb.render("AccountReconciliation", {widget: this})); + this.$(".oe_account_recon_previous").click(function() { + self.current_partner = (((self.current_partner - 1) % self.partners.length) + self.partners.length) % self.partners.length; + self.search_by_partner(); + }); + this.$(".oe_account_recon_next").click(function() { + self.current_partner = (self.current_partner + 1) % self.partners.length; + self.search_by_partner(); + }); + this.$(".oe_account_recon_reconcile").click(function() { + self.reconcile(); + }); + this.$(".oe_account_recom_mark_as_reconciled").click(function() { + self.mark_as_reconciled(); + }); + } + return tmp; + }, + do_search: function(domain, context, group_by) { + var self = this; + this.last_domain = domain; + this.last_context = context; + this.last_group_by = group_by; + this.old_search = _.bind(this._super, this); + var mod = new Model("account.move.line", context, domain); + return mod.call("list_partners_to_reconcile", []).then(function(result) { + var current = self.current_partner !== null ? self.partners[self.current_partner][0] : null; + self.partners = result; + var index = _.find(_.range(self.partners.length), function(el) { + if (current === self.partners[el][0]) + return true; + }); + if (index !== undefined) + self.current_partner = index; + else + self.current_partner = self.partners.length === 0 ? null : 0; + self.search_by_partner(); + }); + }, + search_by_partner: function() { + var self = this; + var fct = function() { + return self.old_search(new data.CompoundDomain(self.last_domain, + [["partner_id", "in", self.current_partner === null ? [] : + [self.partners[self.current_partner][0]] ]]), self.last_context, self.last_group_by); + }; + if (self.current_partner === null) { + self.last_reconciliation_date = _t("Never"); + return fct(); + } else { + return new Model("res.partner").call("read", + [self.partners[self.current_partner][0], ["last_reconciliation_date"]]).then(function(res) { + self.last_reconciliation_date = + formats.format_value(res.last_reconciliation_date, {"type": "datetime"}, _t("Never")); + return fct(); + }); + } + }, + reconcile: function() { + var self = this; + var ids = this.get_selected_ids(); + if (ids.length === 0) { + new Dialog(this, { + title: _t("Warning"), + size: 'medium', + }, $("
").text(_t("You must choose at least one record."))).open(); + return false; + } + + new Model("ir.model.data").call("get_object_reference", ["account", "action_view_account_move_line_reconcile"]).then(function(result) { + var additional_context = _.extend({ + active_id: ids[0], + active_ids: ids, + active_model: self.model + }); + return self.rpc("/web/action/load", { + action_id: result[1], + context: additional_context + }).done(function (result) { + result.context = pyeval.eval('contexts', [result.context, additional_context]); + result.flags = result.flags || {}; + result.flags.new_window = true; + return self.do_action(result, { + on_close: function () { + self.do_search(self.last_domain, self.last_context, self.last_group_by); + } + }); + }); + }); + }, + mark_as_reconciled: function() { + var self = this; + var id = self.partners[self.current_partner][0]; + new Model("res.partner").call("mark_as_reconciled", [[id]]).then(function() { + self.do_search(self.last_domain, self.last_context, self.last_group_by); + }); + }, + do_select: function (ids, records) { + this.trigger('record_selected'); + this._super.apply(this, arguments); + }, +}); +core.view_registry.add('tree_account_reconciliation', ReconciliationListView); + + +return { + bankStatementReconciliation: bankStatementReconciliation, + bankStatementReconciliationLine: bankStatementReconciliationLine, + ReconciliationListView: ReconciliationListView, +}; + +}); diff --git a/addons/account/views/account.xml b/addons/account/views/account.xml index 45102cc824d..a7042b41bd9 100644 --- a/addons/account/views/account.xml +++ b/addons/account/views/account.xml @@ -7,9 +7,9 @@ - - - + + +