diff --git a/addons/account/static/src/css/account_bank_statement_reconciliation.css b/addons/account/static/src/css/account_bank_statement_reconciliation.css
index 05caeab2655..040e55eb45f 100644
--- a/addons/account/static/src/css/account_bank_statement_reconciliation.css
+++ b/addons/account/static/src/css/account_bank_statement_reconciliation.css
@@ -6,18 +6,29 @@
-o-user-select: none;
user-select: none;
cursor: default;
+ height: 100%;
/* icons */ }
+ .openerp .oe_bank_statement_reconciliation .oe_form_sheetbg {
+ border-bottom: 0;
+ padding: 0;
+ height: 100%; }
+ .openerp .oe_bank_statement_reconciliation .oe_form_sheetbg .oe_form_sheet {
+ position: relative;
+ padding: 20px 15px 30px 15px;
+ border-top: 0;
+ border-bottom: 0;
+ height: 100%; }
.openerp .oe_bank_statement_reconciliation h1 {
width: 48%;
padding: 0 0 0 15px;
- margin: 0 0 35px 0;
+ margin: 0 0 25px 0;
float: left;
- font-size: 2.3em; }
+ font-size: 2em; }
.openerp .oe_bank_statement_reconciliation h2 {
font-size: 1.8em; }
.openerp .oe_bank_statement_reconciliation .progress {
width: 49%;
- margin: 6px 15px 0 0;
+ margin: 4px 15px 0 0;
float: right;
position: relative;
display: inline-block; }
@@ -29,9 +40,6 @@
top: 2px;
z-index: 10;
text-shadow: -1px -1px 0 #f5f5f5, 1px -1px 0 #f5f5f5, -1px 1px 0 #f5f5f5, 1px 1px 0 #f5f5f5; }
- .openerp .oe_bank_statement_reconciliation .oe_form_sheet {
- position: relative;
- padding-bottom: 30px; }
.openerp .oe_bank_statement_reconciliation .protip {
margin: 0;
position: absolute;
@@ -100,7 +108,12 @@
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
- transform: rotate(0deg); }
+ transform: rotate(0deg);
+ -webkit-transition-duration: 300ms;
+ -moz-transition-duration: 300ms;
+ -ms-transition-duration: 300ms;
+ -o-transition-duration: 300ms;
+ transition-duration: 300ms; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .visible_toggle, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line[data-mode="match"] .toggle_match, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line[data-mode="create"] .toggle_create {
visibility: visible !important;
-webkit-transform: rotate(90deg);
@@ -136,24 +149,24 @@
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td {
padding: 1px 8px;
vertical-align: middle; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(7), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(7) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_info_popover, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_info_popover {
width: 15px;
padding: 0; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(1) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_action {
text-align: left; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(2), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(2) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_account_code, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_account_code {
width: 80px;
padding-left: 3px; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(3), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(3) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_due_date, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_due_date {
width: 100px; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(5), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(5) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_debit, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_debit {
text-align: right;
width: 15%; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(6), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(6) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_credit, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_credit {
width: 15%;
text-align: right;
padding-right: 3px; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(7), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td:nth-child(7) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_info_popover, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table td.cell_info_popover {
text-align: right; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view tr.line_open_balance, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match table tr.line_open_balance {
color: #bbb; }
@@ -176,8 +189,13 @@
-webkit-transition-property: background-color;
-moz-transition-property: background-color;
-ms-transition-property: background-color;
- transition-property: background-color; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td:nth-child(1), .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td:nth-child(7) {
+ transition-property: background-color;
+ -webkit-transition-duration: 300ms;
+ -moz-transition-duration: 300ms;
+ -ms-transition-duration: 300ms;
+ -o-transition-duration: 300ms;
+ transition-duration: 300ms; }
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td.cell_action, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view .initial_line > td.cell_info_popover {
border-top: none;
background: white !important;
padding-top: 6px;
@@ -188,9 +206,9 @@
font-weight: bold;
height: 26px;
margin: 0 15px 4px 15px; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption .button_ok {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption button {
float: right; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption .button_ok:disabled {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption button:disabled {
opacity: 0.5; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption > span, .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view caption > input {
position: relative;
@@ -198,7 +216,7 @@
/* meh */
font-weight: bold;
cursor: pointer; }
- .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td:nth-child(6) {
+ .openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .accounting_view td.cell_credit {
border-left: 1px solid black; }
.openerp .oe_bank_statement_reconciliation .oe_bank_statement_reconciliation_line .match .match_controls {
padding: 0 0 5px 18px; }
diff --git a/addons/account/static/src/css/account_bank_statement_reconciliation.scss b/addons/account/static/src/css/account_bank_statement_reconciliation.scss
index 248536b7a9b..92788e3bb51 100644
--- a/addons/account/static/src/css/account_bank_statement_reconciliation.scss
+++ b/addons/account/static/src/css/account_bank_statement_reconciliation.scss
@@ -3,6 +3,8 @@ $mainTableBordersPadding: 3px;
$lightBorder: 1px solid #bbb;
$accountingBorder: 1px solid #000;
$initialLineBackground: #f0f0f0;
+// Warning, this value is also specified in the instance.web.account.abstractReconciliation widget
+$aestetic_animation_speed: 300ms;
.openerp .oe_bank_statement_reconciliation {
@@ -13,13 +15,28 @@ $initialLineBackground: #f0f0f0;
-o-user-select: none;
user-select: none;
cursor: default;
+ height: 100%;
+
+ .oe_form_sheetbg {
+ border-bottom: 0;
+ padding: 0;
+ height: 100%;
+
+ .oe_form_sheet {
+ position: relative;
+ padding: 20px 15px 30px 15px;
+ border-top: 0;
+ border-bottom: 0;
+ height: 100%;
+ }
+ }
h1 {
width: 48%;
padding: 0 0 0 $actionColWidth;
- margin: 0 0 35px 0;
+ margin: 0 0 25px 0;
float: left;
- font-size: 2.3em;
+ font-size: 2em;
}
h2 {
@@ -28,7 +45,7 @@ $initialLineBackground: #f0f0f0;
.progress {
width: 49%;
- margin: 6px $actionColWidth 0 0;
+ margin: 4px $actionColWidth 0 0;
float: right;
position: relative;
display: inline-block;
@@ -48,11 +65,6 @@ $initialLineBackground: #f0f0f0;
}
}
- .oe_form_sheet {
- position: relative;
- padding-bottom: 30px;
- }
-
.protip {
margin: 0;
position: absolute;
@@ -145,6 +157,11 @@ $initialLineBackground: #f0f0f0;
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
+ -webkit-transition-duration: $aestetic_animation_speed;
+ -moz-transition-duration: $aestetic_animation_speed;
+ -ms-transition-duration: $aestetic_animation_speed;
+ -o-transition-duration: $aestetic_animation_speed;
+ transition-duration: $aestetic_animation_speed;
}
.visible_toggle {
@@ -228,40 +245,40 @@ $initialLineBackground: #f0f0f0;
vertical-align: middle;
}
- td:nth-child(1), td:nth-child(7) {
+ td.cell_action, td.cell_info_popover {
width: $actionColWidth;
padding: 0;
}
- td:nth-child(1) {
+ td.cell_action {
text-align: left;
}
- td:nth-child(2) {
+ td.cell_account_code {
width: 80px;
padding-left: $mainTableBordersPadding;
}
- td:nth-child(3) {
+ td.cell_due_date {
width: 100px;
}
- td:nth-child(4) {
+ td.cell_label {
}
- td:nth-child(5) {
+ td.cell_debit {
text-align: right;
width: 15%;
}
- td:nth-child(6) {
+ td.cell_credit {
width: 15%;
text-align: right;
padding-right: $mainTableBordersPadding;
}
- td:nth-child(7) {
+ td.cell_info_popover {
text-align: right;
}
@@ -301,8 +318,13 @@ $initialLineBackground: #f0f0f0;
-moz-transition-property: background-color;
-ms-transition-property: background-color;
transition-property: background-color;
+ -webkit-transition-duration: $aestetic_animation_speed;
+ -moz-transition-duration: $aestetic_animation_speed;
+ -ms-transition-duration: $aestetic_animation_speed;
+ -o-transition-duration: $aestetic_animation_speed;
+ transition-duration: $aestetic_animation_speed;
- &:nth-child(1), &:nth-child(7) {
+ &.cell_action, &.cell_info_popover {
border-top: none;
background: white !important;
// Hack pour l'alignement au px près
@@ -318,7 +340,7 @@ $initialLineBackground: #f0f0f0;
height: 26px;
margin: 0 $actionColWidth 4px $actionColWidth;
- .button_ok {
+ button {
float: right;
&:disabled {
@@ -334,7 +356,7 @@ $initialLineBackground: #f0f0f0;
}
// accounting "T"
- td:nth-child(6) { border-left: $accountingBorder; }
+ td.cell_credit { border-left: $accountingBorder; }
}
@@ -434,4 +456,4 @@ $initialLineBackground: #f0f0f0;
}
}
}
-}
+}
\ No newline at end of file
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
new file mode 100644
index 00000000000..fbc55346772
--- /dev/null
+++ b/addons/account/static/src/js/account_tour_bank_statement_reconciliation.js
@@ -0,0 +1,147 @@
+(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
index 889538cedb4..e8f364127a1 100644
--- a/addons/account/static/src/js/account_widgets.js
+++ b/addons/account/static/src/js/account_widgets.js
@@ -13,7 +13,8 @@ openerp.account = function (instance) {
init: function(parent, context) {
this._super(parent);
this.max_reconciliations_displayed = 10;
- this.statement_id = context.context.statement_id;
+ 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.title = context.context.title || _t("Reconciliation");
this.st_lines = [];
this.last_displayed_reconciliation_index = undefined; // Flow control
@@ -37,7 +38,7 @@ openerp.account = function (instance) {
// 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 = [];
+ 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 = {
@@ -118,48 +119,35 @@ openerp.account = function (instance) {
start: function() {
this._super();
var self = this;
-
- // Inject variable styles
- var style = document.createElement("style");
- style.appendChild(document.createTextNode(""));
- document.head.appendChild(style);
- var css_selector = ".oe_bank_statement_reconciliation_line .toggle_match, .oe_bank_statement_reconciliation_line .toggle_create, .oe_bank_statement_reconciliation_line .initial_line > td";
- if(style.sheet.insertRule) {
- style.sheet.insertRule(css_selector + " { -webkit-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
- style.sheet.insertRule(css_selector + " { -moz-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
- style.sheet.insertRule(css_selector + " { -ms-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
- style.sheet.insertRule(css_selector + " { -o-transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
- style.sheet.insertRule(css_selector + " { transition-duration: "+self.aestetic_animation_speed+"ms; }", 0);
- } else {
- style.sheet.addRule(css_selector, "-webkit-transition-duration: "+self.aestetic_animation_speed+"ms;");
- style.sheet.addRule(css_selector, "-moz-transition-duration: "+self.aestetic_animation_speed+"ms;");
- style.sheet.addRule(css_selector, "-ms-transition-duration: "+self.aestetic_animation_speed+"ms;");
- style.sheet.addRule(css_selector, "-o-transition-duration: "+self.aestetic_animation_speed+"ms;");
- style.sheet.addRule(css_selector, "-webkit-transition-duration: "+self.aestetic_animation_speed+"ms;");
- }
-
// Retreive statement infos and reconciliation data from the model
var lines_filter = [['journal_entry_id', '=', false], ['account_id', '=', false]];
var deferred_promises = [];
-
- if (self.statement_id) {
- lines_filter.push(['statement_id', '=', self.statement_id]);
+
+ // 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, retreive its name
+ if (self.statement_ids.length === 1) {
+ 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
- .query(["name"])
- .filter([['id', '=', self.statement_id]])
- .first()
- .then(function(title){
- self.title = title.name;
- })
- );
- deferred_promises.push(self.model_bank_statement
- .call("number_of_lines_reconciled", [self.statement_id])
+ .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) {
@@ -169,17 +157,19 @@ openerp.account = function (instance) {
})
);
- deferred_promises.push(self.model_bank_statement
- .call("get_format_currency_js_function", [self.statement_id])
- .then(function(data){
+ // 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('id')
+ .order_by('statement_id, id')
.all().then(function (data) {
self.st_lines = _(data).map(function(o){ return o.id });
})
@@ -200,21 +190,21 @@ openerp.account = function (instance) {
.all().then(function(data) {
_.each(data, function(o) { self.map_account_id_code[o.id] = o.code });
});
-
+
// Create a dict tax id -> amount
new instance.web.Model("account.tax")
.query(['id', 'amount'])
.all().then(function(data) {
_.each(data, function(o) { self.map_tax_id_amount[o.id] = o.amount });
});
-
+
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);
@@ -255,33 +245,58 @@ openerp.account = function (instance) {
}
},
- excludeMoveLines: function(line_ids) {
+ // Adds move line ids to the list of move lines not to fetch for a given partner
+ // This is required because the same move line cannot be selected for multiple reconciliation
+ excludeMoveLines: function(source_child, partner_id, line_ids) {
var self = this;
+
+ var excluded_ids = this.excluded_move_lines_ids[partner_id];
+ var excluded_move_lines_changed = false;
_.each(line_ids, function(line_id){
- line_id = parseInt(line_id);
- if (self.excluded_move_lines_ids.indexOf(line_id) === -1) {
- self.excluded_move_lines_ids.push(line_id);
+ if (excluded_ids.indexOf(line_id) === -1) {
+ excluded_ids.push(line_id);
+ excluded_move_lines_changed = true;
}
});
- //update all children view
+ if (! excluded_move_lines_changed)
+ return;
+
+ // Function that finds if an array of line objects contains at least a line identified by its id
+ var contains_lines = function(lines_array, line_ids) {
+ for (var i = 0; i < lines_array.length; i++)
+ for (var j = 0; j < line_ids.length; j++)
+ if (lines_array[i].id === line_ids[j])
+ return true;
+ return false;
+ };
+
+ // Update children if needed
_.each(self.getChildren(), function(child){
- child.render();
+ if (child.partner_id === partner_id && child !== source_child) {
+ if (contains_lines(child.get("mv_lines_selected"), line_ids)) {
+ child.set("mv_lines_selected", _.filter(child.get("mv_lines_selected"), function(o){ return line_ids.indexOf(o.id) === -1 }));
+ } else if (contains_lines(child.mv_lines_deselected, line_ids)) {
+ child.mv_lines_deselected = _.filter(child.mv_lines_deselected, function(o){ return line_ids.indexOf(o.id) === -1 });
+ child.updateMatches();
+ } else if (contains_lines(child.get("mv_lines"), line_ids)) {
+ child.updateMatches();
+ }
+ }
});
},
-
- unexcludeMoveLines: function(line_ids) {
+
+ unexcludeMoveLines: function(source_child, partner_id, line_ids) {
var self = this;
- var index = -1;
- _.each(line_ids, function(line_id){
- line_id = parseInt(line_id);
- index = self.excluded_move_lines_ids.indexOf(line_id);
- if (index > -1) {
- self.excluded_move_lines_ids.splice(index,1);
- }
- });
- //update all children view
+
+ var initial_excluded_lines_num = this.excluded_move_lines_ids[partner_id].length;
+ this.excluded_move_lines_ids[partner_id] = _.difference(this.excluded_move_lines_ids[partner_id], line_ids);
+ if (this.excluded_move_lines_ids[partner_id].length === initial_excluded_lines_num)
+ return;
+
+ // Update children if needed
_.each(self.getChildren(), function(child){
- child.render();
+ if (child.partner_id === partner_id && child !== source_child && (child.get("mode") === "match" || child.$el.hasClass("no_match")))
+ child.updateMatches();
});
},
@@ -313,6 +328,11 @@ openerp.account = function (instance) {
if (self.last_displayed_reconciliation_index < self.st_lines.length) {
self.displayReconciliation(self.st_lines[self.last_displayed_reconciliation_index++], 'inactive');
}
+ // Congratulate the user if the work is done
+ if (self.reconciled_lines === self.st_lines.length) {
+ self.displayDoneMessage();
+ }
+
// Put the first line in match mode
if (self.reconciled_lines !== self.st_lines.length) {
var first_child = self.getChildren()[0];
@@ -320,15 +340,12 @@ openerp.account = function (instance) {
first_child.set("mode", "match");
}
}
- // Congratulate the user if the work is done
- if (self.reconciled_lines === self.st_lines.length) {
- self.displayDoneMessage();
- }
},
displayDoneMessage: function() {
var self = this;
+ var is_single_statement = self.statement_ids !== undefined && self.statement_ids.length === 1;
var sec_taken = Math.round((Date.now()-self.time_widget_loaded)/1000);
var sec_per_item = Math.round(sec_taken/self.reconciled_lines);
var achievements = [];
@@ -364,7 +381,7 @@ openerp.account = function (instance) {
transactions_done: self.reconciled_lines,
done_with_ctrl_enter: self.lines_reconciled_with_ctrl_enter,
achievements: achievements,
- has_statement_id: self.statement_id !== undefined,
+ has_statement_id: is_single_statement,
}));
// Animate it
@@ -383,11 +400,11 @@ openerp.account = function (instance) {
});
});
- if (self.$(".button_close_statement").length !== 0) {
+ if (is_single_statement && self.$(".button_close_statement").length !== 0) {
self.$(".button_close_statement").hide();
self.model_bank_statement
.query(["balance_end_real", "balance_end"])
- .filter([['id', '=', self.statement_id]])
+ .filter([['id', '=', self.statement_ids[0]]])
.first()
.then(function(data){
if (data.balance_end_real === data.balance_end) {
@@ -395,7 +412,7 @@ openerp.account = function (instance) {
self.$(".button_close_statement").click(function() {
self.$(".button_close_statement").attr("disabled", "disabled");
self.model_bank_statement
- .call("button_confirm_bank", [[self.statement_id]])
+ .call("button_confirm_bank", [[self.statement_ids[0]]])
.then(function () {
self.do_action({
type: 'ir.actions.client',
@@ -456,9 +473,12 @@ openerp.account = function (instance) {
init: function(parent, context) {
this._super(parent);
+ this.formatCurrency = this.getParent().formatCurrency;
if (context.initial_data_provided) {
// Process data
- _(context.reconciliation_proposition).each(this.decorateMoveLine.bind(this));
+ _.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;
@@ -466,7 +486,9 @@ openerp.account = function (instance) {
// Exclude selected move lines
var selected_line_ids = _(context.reconciliation_proposition).map(function(o){ return o.id });
- this.getParent().excludeMoveLines(selected_line_ids);
+ 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, selected_line_ids);
} else {
this.set("mv_lines_selected", []);
this.st_line = undefined;
@@ -483,29 +505,31 @@ openerp.account = function (instance) {
this.model_tax = new instance.web.Model("account.tax");
this.map_account_id_code = this.getParent().map_account_id_code;
this.map_tax_id_amount = this.getParent().map_tax_id_amount;
- this.formatCurrency = this.getParent().formatCurrency;
this.presets = this.getParent().presets;
this.is_valid = true;
this.is_consistent = true; // Used to prevent bad server requests
+ this.total_move_lines_num = undefined; // Used for pagers
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("balance", undefined); // Debit is +, credit is -
- this.on("change:balance", this, this.balanceChanged);
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);
-
- //all lines associated to current reconciliation
- this.propositions_lines = undefined;
},
start: function() {
@@ -516,85 +540,97 @@ openerp.account = function (instance) {
self.aestetic_animation_speed = 0;
self.is_consistent = false;
- if (self.context.animate_entrance) self.$el.css("opacity", "0");
-
- // Fetch data
- var deferred_fetch_data = new $.Deferred();
- if (! self.context.initial_data_provided) {
- // Load statement line
- self.model_bank_statement_line
- .call("get_statement_line_for_reconciliation", [self.st_line_id])
- .then(function (data) {
- self.st_line = data;
- self.decorateStatementLine(self.st_line);
- self.partner_id = data.partner_id;
- $.when(self.loadReconciliationProposition()).then(function(){
- deferred_fetch_data.resolve();
- });
- });
- } else {
- deferred_fetch_data.resolve();
+ if (self.context.animate_entrance) {
+ self.$el.fadeOut(0);
+ self.$el.slideUp(0);
}
-
- // Display the widget
- return $.when(deferred_fetch_data).then(function(){
- //load all lines that can be usefull for counterparts
- var deferred_total_move_lines_num = self.model_bank_statement_line
- .call("get_move_lines_counterparts_id", [self.st_line.id, []])
- .then(function(lines){
- _(lines).each(self.decorateMoveLine.bind(self));
- self.propositions_lines = lines;
- });
- return deferred_total_move_lines_num;
- }).then(function(){
- // Render template
- 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");
- if (self.context.mode !== "match") self.render();
- 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.change_partner_field.$el.find("input").attr("placeholder", _t("Select Partner"));
- self.$(".match").slideUp(0);
- self.$el.addClass("no_partner");
- self.set("mode", self.context.mode);
- 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 $.when(self.loadData()).then(function(){
+ return $.when(self.render()).then(function(){
self.is_consistent = true;
- return;
- }
-
- // TODO : the .on handler's returned deferred is lost
- return $.when(self.set("mode", self.context.mode)).then(function(){
- self.is_consistent = true;
-
- // Make sure the display is OK
- self.balanceChanged();
- self.createdLinesChanged();
- self.updateAccountingViewMatchedLines();
-
// 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.animate({opacity: 1}, self.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);
@@ -603,7 +639,7 @@ openerp.account = function (instance) {
_.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(_.map(self.get("mv_lines_selected"), function(o){ return o.id }));
+ self.getParent().unexcludeMoveLines(self, self.partner_id, _.map(self.get("mv_lines_selected"), function(o){ return o.id }));
$.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy') });
self.$el.empty();
self.$el.removeClass("no_partner");
@@ -617,6 +653,7 @@ openerp.account = function (instance) {
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
@@ -749,6 +786,7 @@ openerp.account = function (instance) {
self.change_partner_field.on("change:value", self.change_partner_field, function() {
self.changePartner(this.get_value());
});
+ self.change_partner_field.$el.find("input").attr("placeholder", _t("Select Partner"));
field_manager.do_show();
},
@@ -761,20 +799,19 @@ openerp.account = function (instance) {
},
// adds fields, prefixed with q_, to the move line for qweb rendering
- decorateMoveLine: function(line){
+ decorateMoveLine: function(line, currency_id) {
line.partial_reconcile = false;
line.propose_partial_reconcile = false;
+ line['credit'] = [line['debit'], line['debit'] = line['credit']][0];
line.q_due_date = (line.date_maturity === false ? line.date : line.date_maturity);
line.q_amount = (line.debit !== 0 ? "- "+line.q_debit : "") + (line.credit !== 0 ? line.q_credit : "");
- line.q_popover = QWeb.render("bank_statement_reconciliation_move_line_details", {line: line});
line.q_label = line.name;
-
- if (line.has_no_partner){
- line.q_label = line.partner_name + ': ' +line.q_label;
- }
- // WARNING : pretty much of a ugly hack
- // The value of account_move.ref is either the move's communication or it's name without the slashes
- if (line.ref && line.ref !== line.name.replace(/\//g,''))
+ line.debit_str = this.formatCurrency(line.debit, currency_id);
+ line.credit_str = this.formatCurrency(line.credit, currency_id);
+ line.q_popover = QWeb.render("bank_statement_reconciliation_move_line_details", {line: line});
+ if (line.has_no_partner)
+ line.q_label = line.partner_name + ': ' + line.q_label;
+ if (line.ref && line.ref !== line.name)
line.q_label += " : " + line.ref;
},
@@ -819,31 +856,77 @@ openerp.account = function (instance) {
selectMoveLine: function(mv_line) {
var self = this;
var line_id = mv_line.dataset.lineid;
- var line = _.find(self.propositions_lines, function(o){ return o.id == line_id});
- $(mv_line).attr('data-selected','true');
- self.getParent().excludeMoveLines([line_id]);
- self.set("mv_lines_selected", self.get("mv_lines_selected").concat(line));
+
+ // find the line in mv_lines or mv_lines_deselected
+ var line = _.find(self.get("mv_lines"), function(o){ return o.id == line_id});
+ if (! line) {
+ line = _.find(self.mv_lines_deselected, function(o){ return o.id == line_id });
+ self.mv_lines_deselected = _.filter(self.mv_lines_deselected, function(o) { return o.id != line_id });
+ }
+ if (! line) return; // If no line found, we've got a syncing problem (let's turn a deaf ear)
+
+ // Warn the user if he's selecting lines from both a payable and a receivable account
+ var last_selected_line = _.last(self.get("mv_lines_selected"));
+ if (last_selected_line && last_selected_line.account_type != line.account_type) {
+ new instance.web.Dialog(this, {
+ title: _t("Warning"),
+ size: 'medium',
+ }, $("
").text(_.str.sprintf(_t("You are selecting transactions from both a payable and a receivable account.\n\nIn order to proceed, you first need to deselect the %s transactions."), last_selected_line.account_type))).open();
+ return;
+ }
+
+ // If statement line has no partner, give it the partner of the selected move line
+ if (!this.st_line.partner_id && line.partner_id) {
+ self.changePartner(line.partner_id, function() {
+ self.selectMoveLine(mv_line);
+ });
+ } else {
+ self.set("mv_lines_selected", self.get("mv_lines_selected").concat(line));
+ // $(mv_line).attr('data-selected','true');
+ // self.set("mv_lines_selected", self.get("mv_lines_selected").concat(line));
+ // this.set("mv_lines", _.reject(this.get("mv_lines"), function(o){return o.id == line_id}));
+ // this.getParent().excludeMoveLines([line_id]);
+ }
},
deselectMoveLine: function(mv_line) {
var self = this;
var line_id = mv_line.dataset.lineid;
- var line = _.find(self.propositions_lines, function(o){ return o.id == line_id});
- $(mv_line).attr('data-selected','false');
- self.getParent().unexcludeMoveLines([line_id]);
- self.set("mv_lines_selected",_.filter(self.get("mv_lines_selected"), function(o) { return o.id != line_id }));
+ var line = _.find(self.get("mv_lines_selected"), function(o){ return o.id == line_id});
+ if (! line) return; // If no line found, we've got a syncing problem (let's turn a deaf ear)
+
+ // add the line to mv_lines_deselected and remove it from mv_lines_selected
+ self.mv_lines_deselected.unshift(line);
+ var mv_lines_selected = _.filter(self.get("mv_lines_selected"), function(o) { return o.id != line_id });
+
+ // remove partial reconciliation stuff if necessary
+ if (line.partial_reconcile === true) self.unpartialReconcileLine(line);
+ if (line.propose_partial_reconcile === true) line.propose_partial_reconcile = false;
+
+ self.$el.removeClass("no_match");
+ self.set("mode", "match");
+ self.set("mv_lines_selected", mv_lines_selected);
+
+
+ // $(mv_line).attr('data-selected','false');
+ // this.set("mv_lines", this.get("mv_lines").concat(line));
+ // this.getParent().unexcludeMoveLines([line_id]);
},
/** Matches pagination */
pagerControlLeftHandler: function() {
var self = this;
+ if (self.$(".pager_control_left").hasClass("disabled")) { return; /* shouldn't happen, anyway*/ }
+ if (self.total_move_lines_num < 0) { return; }
self.set("pager_index", self.get("pager_index")-1 );
},
-
+
pagerControlRightHandler: function() {
var self = this;
var new_index = self.get("pager_index")+1;
+ if (self.$(".pager_control_right").hasClass("disabled")) { return; /* shouldn't happen, anyway*/ }
+ if ((new_index * self.max_move_lines_displayed) >= self.total_move_lines_num) { return; }
self.set("pager_index", new_index );
},
@@ -851,8 +934,10 @@ openerp.account = function (instance) {
var self = this;
self.set("pager_index", 0);
self.filter = self.$(".filter").val();
- self.render();
+ window.clearTimeout(self.apply_filter_timeout);
+ self.apply_filter_timeout = window.setTimeout(self.proxy('updateMatches'), 200);
},
+
/** Creating */
@@ -862,6 +947,7 @@ openerp.account = function (instance) {
_.each(self.create_form, function(field) {
field.set("value", false);
});
+ self.label_field.set("value", self.st_line.name);
self.amount_field.set("value", -1*self.get("balance"));
self.account_id_field.focus();
},
@@ -894,26 +980,31 @@ openerp.account = function (instance) {
var self = this;
self.initializeCreateForm();
var preset = self.presets[e.currentTarget.dataset.presetid];
+ // Hack : set_value of a field calls a handler that returns a deferred because it could make a RPC call
+ // to compute the tax before it updates the line being edited. Unfortunately this deferred is lost.
+ // Hence this ugly hack to avoid concurrency problem that arose when setting amount (in initializeCreateForm), then tax, then another amount
+ if (preset.tax && self.tax_field) self.tax_field.set_value(false);
+ if (preset.amount && self.amount_field) self.amount_field.set_value(false);
+
for (var key in preset) {
if (! preset.hasOwnProperty(key) || key === "amount") continue;
- if (self.hasOwnProperty(key+"_field"))
+ if (preset[key] && self.hasOwnProperty(key+"_field"))
self[key+"_field"].set_value(preset[key]);
}
- var sign = self.amount_field.get_value() < 0 ? -1 : 1;
if (preset.amount && self.amount_field) {
if (preset.amount_type === "fixed")
- self.amount_field.set_value(sign * preset.amount);
+ self.amount_field.set_value(preset.amount);
else if (preset.amount_type === "percentage_of_total")
- self.amount_field.set_value(sign * self.st_line.amount * preset.amount / 100);
+ self.amount_field.set_value(self.st_line.amount * preset.amount / 100);
else if (preset.amount_type === "percentage_of_balance") {
self.amount_field.set_value(0);
self.updateBalance();
- self.amount_field.set_value(sign * Math.abs(self.get("balance")) * preset.amount / 100);
+ self.amount_field.set_value(-1 * self.get("balance") * preset.amount / 100);
}
}
},
-
+
/** Display */
initialLineClickHandler: function() {
@@ -937,9 +1028,11 @@ openerp.account = function (instance) {
partnerNameClickHandler: function() {
var self = this;
- self.$(".partner_name").hide();
- self.change_partner_field.$el.find("input").attr("placeholder", self.st_line.partner_name);
- self.$(".change_partner_container").show();
+ // Delete statement line's partner
+ return self.changePartner('', function() {
+ self.$(".partner_name").hide();
+ self.$(".change_partner_container").show();
+ });
},
@@ -979,40 +1072,43 @@ openerp.account = function (instance) {
var self = this;
var table = self.$(".match table");
var nothing_displayed = true;
-
+
// Display move lines
$.each(self.$(".match table .bootstrap_popover"), function(){ $(this).popover('destroy') });
table.empty();
var slice_start = self.get("pager_index") * self.max_move_lines_displayed;
var slice_end = (self.get("pager_index")+1) * self.max_move_lines_displayed;
-
- var visible = 0
- _(self.get("mv_lines")).each(function(line){
- if (visible >= slice_start && visible < slice_end) {
- var $line = $(QWeb.render("bank_statement_reconciliation_move_line", {line: line, selected: false}));
- self.bindPopoverTo($line.find(".line_info_button"));
- table.append($line);
- nothing_displayed = false;
- }
- visible = visible + 1;
+ _( _.filter(self.mv_lines_deselected, function(o){
+ return o.name.indexOf(self.filter) !== -1 || o.ref.indexOf(self.filter) !== -1 })
+ .slice(slice_start, slice_end)).each(function(line){
+ var $line = $(QWeb.render("bank_statement_reconciliation_move_line", {line: line, selected: false}));
+ self.bindPopoverTo($line.find(".line_info_button"));
+ table.append($line);
+ nothing_displayed = false;
});
- if (nothing_displayed)
+ _(self.get("mv_lines")).each(function(line){
+ var $line = $(QWeb.render("bank_statement_reconciliation_move_line", {line: line, selected: false}));
+ self.bindPopoverTo($line.find(".line_info_button"));
+ table.append($line);
+ nothing_displayed = false;
+ });
+ if (nothing_displayed && this.filter !== "")
table.append(QWeb.render("filter_no_match", {filter_str: self.filter}));
},
updatePagerControls: function() {
var self = this;
+
if (self.get("pager_index") === 0)
self.$(".pager_control_left").addClass("disabled");
else
self.$(".pager_control_left").removeClass("disabled");
- if (self.get('mv_lines').length <= ((self.get("pager_index")+1) * self.max_move_lines_displayed))
+ if (self.total_move_lines_num <= ((self.get("pager_index")+1) * self.max_move_lines_displayed))
self.$(".pager_control_right").addClass("disabled");
else
self.$(".pager_control_right").removeClass("disabled");
},
-
/** Properties changed */
// Updates the validation button and the "open balance" line
@@ -1034,8 +1130,12 @@ openerp.account = function (instance) {
self.is_valid = false;
var debit = (balance > 0 ? self.formatCurrency(balance, self.st_line.currency_id) : "");
var credit = (balance < 0 ? self.formatCurrency(-1*balance, self.st_line.currency_id) : "");
- var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {debit: debit, credit: credit, account_code: self.map_account_id_code[self.st_line.open_balance_account_id]}));
- $line.find('.js_open_balance')[0].innerHTML = "Choose counterpart";
+ var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {
+ debit: debit,
+ credit: credit,
+ account_code: self.map_account_id_code[self.st_line.open_balance_account_id]
+ }));
+ $line.find('.js_open_balance')[0].innerHTML = _t("Choose counterpart");
self.$(".tbody_open_balance").append($line);
}
return;
@@ -1049,7 +1149,11 @@ openerp.account = function (instance) {
self.$(".button_ok").text("Keep open");
var debit = (balance > 0 ? self.formatCurrency(balance, self.st_line.currency_id) : "");
var credit = (balance < 0 ? self.formatCurrency(-1*balance, self.st_line.currency_id) : "");
- var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {debit: debit, credit: credit, account_code: self.map_account_id_code[self.st_line.open_balance_account_id]}));
+ var $line = $(QWeb.render("bank_statement_reconciliation_line_open_balance", {
+ debit: debit,
+ credit: credit,
+ account_code: self.map_account_id_code[self.st_line.open_balance_account_id]
+ }));
self.$(".tbody_open_balance").append($line);
}
},
@@ -1059,15 +1163,21 @@ openerp.account = function (instance) {
self.$(".action_pane.active").removeClass("active");
- // Special case hack : if no_partner and mode == inactive
+ // Special case hack : if no_partner, either inactive or create
if (self.st_line.has_no_partner) {
if (self.get("mode") === "inactive") {
self.$(".match").slideUp(self.animation_speed);
self.$(".create").slideUp(self.animation_speed);
self.$(".toggle_match").removeClass("visible_toggle");
self.el.dataset.mode = "inactive";
- return;
- }
+ } else {
+ self.initializeCreateForm();
+ self.$(".match").slideUp(self.animation_speed);
+ self.$(".create").slideDown(self.animation_speed);
+ self.$(".toggle_match").addClass("visible_toggle");
+ self.el.dataset.mode = "create";
+ }
+ return;
}
if (self.get("mode") === "inactive") {
@@ -1076,7 +1186,7 @@ openerp.account = function (instance) {
self.el.dataset.mode = "inactive";
} else if (self.get("mode") === "match") {
- return $.when(self.render()).then(function() {
+ return $.when(self.updateMatches()).then(function() {
if (self.$el.hasClass("no_match")) {
self.set("mode", "inactive");
return;
@@ -1095,15 +1205,18 @@ openerp.account = function (instance) {
},
pagerChanged: function() {
- var self = this;
- self.render();
+ 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.total_move_lines_num <= (self.get("pager_index") * self.max_move_lines_displayed)) {
+ self.set("pager_index", Math.ceil(self.total_move_lines_num/self.max_move_lines_displayed)-1);
+ }
+
// If there is no match to display, disable match view and pass in mode inactive
- if (self.get("mv_lines").length === 0 && self.filter === "") {
+ if (self.total_move_lines_num + self.mv_lines_deselected.length === 0 && self.filter === "") {
self.$el.addClass("no_match");
if (self.get("mode") === "match") {
self.set("mode", "inactive");
@@ -1118,23 +1231,24 @@ openerp.account = function (instance) {
mvLinesSelectedChanged: function(elt, val) {
var self = this;
-
+
var added_lines_ids = _.map(_.difference(val.newValue, val.oldValue), function(o){ return o.id });
var removed_lines_ids = _.map(_.difference(val.oldValue, val.newValue), function(o){ return o.id });
-
- self.getParent().excludeMoveLines(added_lines_ids);
- self.getParent().unexcludeMoveLines(removed_lines_ids);
-
- self.updateAccountingViewMatchedLines();
- self.updateBalance();
+
+ self.getParent().excludeMoveLines(self, self.partner_id, added_lines_ids);
+ self.getParent().unexcludeMoveLines(self, self.partner_id, removed_lines_ids);
+
+ $.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
@@ -1155,17 +1269,26 @@ openerp.account = function (instance) {
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};
+ 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[0].amount = amount;
- delete line_created_being_edited[1];
+ line_created_being_edited.length = 1;
deferred_tax.resolve();
}
} else { deferred_tax.resolve(); }
@@ -1211,7 +1334,7 @@ openerp.account = function (instance) {
var balance = self.get("balance");
line.initial_amount = line.debit !== 0 ? line.debit : -1 * line.credit;
if (balance < 0) {
- line.debit -= balance;
+ line.debit += balance;
line.debit_str = self.formatCurrency(line.debit, self.st_line.currency_id);
} else {
line.credit -= balance;
@@ -1250,6 +1373,19 @@ openerp.account = function (instance) {
updateBalance: function() {
var self = this;
var mv_lines_selected = self.get("mv_lines_selected");
+ var lines_selected_num = mv_lines_selected.length;
+ var lines_created_num = self.getCreatedLines().length;
+
+ // Undo partial reconciliation if necessary
+ if (lines_selected_num !== 1 || lines_created_num !== 0) {
+ _.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) {
@@ -1258,57 +1394,71 @@ openerp.account = function (instance) {
_.each(self.getCreatedLines(), function(o) {
balance += o.amount;
});
+ // Should work as long as currency's rounding factor is > 0.001 (ie: don't use gold kilos as a currency)
+ balance = Math.round(balance*1000)/1000;
self.set("balance", balance);
// Propose partial reconciliation if necessary
- var lines_selected_num = mv_lines_selected.length;
- var lines_created_num = self.getCreatedLines().length;
if (lines_selected_num === 1 && lines_created_num === 0 && self.st_line.amount * balance > 0) {
mv_lines_selected[0].propose_partial_reconcile = true;
self.updateAccountingViewMatchedLines();
}
- if (lines_selected_num !== 1 || lines_created_num !== 0) {
- // remove partial reconciliation stuff if necessary
- _.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();
- }
- },
-
- loadReconciliationProposition: function() {
- var self = this;
- return self.model_bank_statement_line
- .call("get_reconciliation_proposition", [self.st_line.id, self.getParent().excluded_move_lines_ids])
- .then(function (lines) {
- _(lines).each(self.decorateMoveLine.bind(self));
- self.set("mv_lines_selected", self.get("mv_lines_selected").concat(lines));
- });
},
- render: function() {
+ // Loads move lines according to the widget's state
+ updateMatches: function() {
+ if (this.st_line.has_no_partner) return;
var self = this;
- var lines_to_show = [];
- _.each(self.propositions_lines, function(line){
- var filter = (line.q_label.toLowerCase().indexOf(self.filter.toLowerCase()) > -1 || line.account_code.toLowerCase().indexOf(self.filter.toLowerCase()) > -1);
- if (self.getParent().excluded_move_lines_ids.indexOf(line.id) === -1 && filter) {
- lines_to_show.push(line);
- }
+ var deselected_lines_num = self.mv_lines_deselected.length;
+ var move_lines_num = 0;
+ 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 });
+ excluded_ids = excluded_ids.concat(self.getParent().excluded_move_lines_ids[self.partner_id]);
+
+ var deferred_move_lines;
+ var move_lines = [];
+ if (limit > 0) {
+ // Load move lines
+ deferred_move_lines = 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);
+ move_lines.push(line);
+ }, self);
+ });
+ }
+
+ // Fetch the number of move lines corresponding to this statement line and this filter
+ var deferred_total_move_lines_num = self.model_bank_statement_line
+ .call("get_move_lines_for_reconciliation_by_statement_line_id", [self.st_line.id, excluded_ids, self.filter, 0, undefined, true])
+ .then(function(num){
+ move_lines_num = num;
+ });
+
+ return $.when(deferred_move_lines, deferred_total_move_lines_num).then(function(){
+ self.total_move_lines_num = move_lines_num + deselected_lines_num;
+ self.set("mv_lines", move_lines);
});
- self.set("mv_lines", lines_to_show);
},
-
+
// Changes the partner_id of the statement_line in the DB and reloads the widget
- changePartner: function(partner_id) {
+ changePartner: function(partner_id, callback) {
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
return $.when(self.restart(self.get("mode"))).then(function(){
+ self.do_load_reconciliation_proposition = true;
self.is_consistent = true;
+ self.set("mode", "match");
+ if (callback) callback();
});
});
},
@@ -1353,16 +1503,13 @@ openerp.account = function (instance) {
},
// Persist data, notify parent view and terminate widget
- persistAndDestroy: function() {
+ persistAndDestroy: function(speed) {
var self = this;
+ speed = (isNaN(speed) ? self.animation_speed : speed);
if (! self.is_consistent) return;
-
- // Prepare data
- var mv_line_dicts = [];
- _.each(self.get("mv_lines_selected"), function(o) { mv_line_dicts.push(self.prepareSelectedMoveLineForPersisting(o)) });
- _.each(self.getCreatedLines(), function(o) { mv_line_dicts.push(self.prepareCreatedMoveLineForPersisting(o)) });
- if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting());
-
+
+ self.getParent().unexcludeMoveLines(self, self.partner_id, _.map(self.get("mv_lines_selected"), function(o){ return o.id }));
+
// Sliding animation
var height = self.$el.outerHeight();
var container = $("
");
@@ -1370,11 +1517,10 @@ openerp.account = function (instance) {
.css("marginTop", self.$el.css("marginTop"))
.css("marginBottom", self.$el.css("marginBottom"));
self.$el.wrap(container);
- var deferred_animation = self.$el.parent().slideUp(self.animation_speed*height/150);
+ var deferred_animation = self.$el.parent().slideUp(speed*height/150);
// RPC
- return self.model_bank_statement_line
- .call("process_reconciliation", [self.st_line_id, mv_line_dicts])
+ return $.when(self.makeRPCForPersisting())
.then(function () {
$.each(self.$(".bootstrap_popover"), function(){ $(this).popover('destroy') });
return $.when(deferred_animation).then(function(){
@@ -1385,11 +1531,20 @@ openerp.account = function (instance) {
});
});
}, function(){
- self.$el.parent().slideDown(self.animation_speed*height/150, function(){
+ self.$el.parent().slideDown(speed*height/150, function(){
self.$el.unwrap();
});
});
-
+ },
+
+ makeRPCForPersisting: function() {
+ var self = this;
+ var mv_line_dicts = [];
+ _.each(self.get("mv_lines_selected"), function(o) { mv_line_dicts.push(self.prepareSelectedMoveLineForPersisting(o)) });
+ _.each(self.getCreatedLines(), function(o) { mv_line_dicts.push(self.prepareCreatedMoveLineForPersisting(o)) });
+ if (Math.abs(self.get("balance")).toFixed(3) !== "0.000") mv_line_dicts.push(self.prepareOpenBalanceForPersisting());
+ return self.model_bank_statement_line
+ .call("process_reconciliation", [self.st_line_id, mv_line_dicts]);
},
});
diff --git a/addons/account/static/src/xml/account_bank_statement_reconciliation.xml b/addons/account/static/src/xml/account_bank_statement_reconciliation.xml
index 83680f005e3..4e32d97eceb 100644
--- a/addons/account/static/src/xml/account_bank_statement_reconciliation.xml
+++ b/addons/account/static/src/xml/account_bank_statement_reconciliation.xml
@@ -53,22 +53,21 @@
-
+
- |
- |
- |
- :
-
+ | |
+ |
+ |
+
() |
-
+
|
-
+
|
- |
+ |
| |
@@ -115,7 +114,7 @@
Date | |
- Partner | |
+ Partner | |
Transaction | |
Description | |
Amount | () |
@@ -123,40 +122,26 @@
-
- |
- |
- |
- |
-
-
-
-
-
- |
-
-
-
-
- |
- |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
-
-
-
+
-
-
-
+
- ID | |
- Account | |
+ id | |
+ Account | |
Journal | |
Period | |
Date | |
@@ -168,28 +153,27 @@
-
- |
- |
- |
- |
- |
- |
- |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
- |
- |
- |
- Open balance |
- |
- |
- |
+ |
+ |
+ |
+ Open balance |
+ |
+ |
+ |
diff --git a/addons/account/tests/__init__.py b/addons/account/tests/__init__.py
index 02e9677ae03..e09bcc6ee99 100644
--- a/addons/account/tests/__init__.py
+++ b/addons/account/tests/__init__.py
@@ -1,7 +1,9 @@
from . import test_tax
from . import test_search
+from . import test_reconciliation
fast_suite = [
test_tax,
test_search,
+ test_reconciliation,
]
diff --git a/addons/account/tests/test_bank_stmt_reconciliation_widget_ui.py b/addons/account/tests/test_bank_stmt_reconciliation_widget_ui.py
new file mode 100644
index 00000000000..fe3a84ffd6c
--- /dev/null
+++ b/addons/account/tests/test_bank_stmt_reconciliation_widget_ui.py
@@ -0,0 +1,8 @@
+import openerp.tests
+
+@openerp.tests.common.at_install(False)
+@openerp.tests.common.post_install(True)
+
+class TestUi(openerp.tests.HttpCase):
+ def test_01_admin_bank_statement_reconciliation(self):
+ self.phantom_js("/", "openerp.Tour.run('bank_statement_reconciliation', 'test')", "openerp.Tour.tours.bank_statement_reconciliation", login="admin")
\ No newline at end of file
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
new file mode 100644
index 00000000000..62d44198153
--- /dev/null
+++ b/addons/account/tests/test_reconciliation.py
@@ -0,0 +1,162 @@
+from openerp.tests.common import TransactionCase
+
+class TestReconciliation(TransactionCase):
+ """Tests for reconciliation (account.tax)
+
+ Test used to check that when doing a sale or purchase invoice in a different currency,
+ the result will be balanced.
+ """
+
+ def setUp(self):
+ super(TestReconciliation, self).setUp()
+ self.account_invoice_model = self.registry('account.invoice')
+ self.account_invoice_line_model = self.registry('account.invoice.line')
+ self.acc_bank_stmt_model = self.registry('account.bank.statement')
+ self.acc_bank_stmt_line_model = self.registry('account.bank.statement.line')
+
+ self.partner_agrolait_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "res_partner_2")[1]
+ self.currency_swiss_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "CHF")[1]
+ self.currency_usd_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "USD")[1]
+ self.account_rcv_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "a_recv")[1]
+ self.account_rsa_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "rsa")[1]
+ self.product_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "product", "product_product_4")[1]
+
+ self.bank_journal_usd_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "bank_journal_usd")[1]
+ self.account_usd_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "usd_bnk")[1]
+
+ self.company_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "base", "main_company")[1]
+ #set expense_currency_exchange_account_id and income_currency_exchange_account_id to a random account
+ self.registry("res.company").write(self.cr, self.uid, [self.company_id], {'expense_currency_exchange_account_id': self.account_rsa_id, 'income_currency_exchange_account_id':self.account_rsa_id})
+
+ def test_balanced_customer_invoice(self):
+ cr, uid = self.cr, self.uid
+ #we create an invoice in CHF
+ invoice_id = self.account_invoice_model.create(cr, uid, {'partner_id': self.partner_agrolait_id,
+ 'reference_type': 'none',
+ 'currency_id': self.currency_swiss_id,
+ 'name': 'invoice to client',
+ 'account_id': self.account_rcv_id,
+ 'type': 'out_invoice'
+ })
+ self.account_invoice_line_model.create(cr, uid, {'product_id': self.product_id,
+ 'quantity': 1,
+ 'price_unit': 100,
+ 'invoice_id': invoice_id,
+ 'name': 'product that cost 100',})
+
+ #validate purchase
+ self.registry('account.invoice').signal_workflow(cr, uid, [invoice_id], 'invoice_open')
+ invoice_record = self.account_invoice_model.browse(cr, uid, [invoice_id])
+
+ #we pay half of it on a journal with currency in dollar (bank statement)
+ bank_stmt_id = self.acc_bank_stmt_model.create(cr, uid, {'journal_id': self.bank_journal_usd_id,})
+
+ bank_stmt_line_id = self.acc_bank_stmt_line_model.create(cr, uid, {'name': 'half payment',
+ 'statement_id': bank_stmt_id,
+ 'partner_id': self.partner_agrolait_id,
+ 'amount': 42,
+ 'amount_currency': 50,
+ 'currency_id': self.currency_swiss_id,})
+
+ #reconcile the payment with the invoice
+ for l in invoice_record.move_id.line_id:
+ if l.account_id.id == self.account_rcv_id:
+ line_id = l
+ break
+ self.acc_bank_stmt_line_model.process_reconciliation(cr, uid, bank_stmt_line_id, [
+ {'counterpart_move_line_id': line_id.id, 'credit':50, 'debit':0, 'name': line_id.name,}])
+
+ #we check that the line is balanced (bank statement line)
+ move_line_ids = self.acc_bank_stmt_model.browse(cr,uid,bank_stmt_id).move_line_ids
+
+ self.assertEquals(len(move_line_ids), 3)
+ checked_line = 0
+ for move_line in move_line_ids:
+ if move_line.account_id.id == self.account_usd_id:
+ self.assertEquals(move_line.debit, 27.47)
+ self.assertEquals(move_line.credit, 0.0)
+ self.assertEquals(move_line.amount_currency, 42)
+ self.assertEquals(move_line.currency_id.id, self.currency_usd_id)
+ checked_line += 1
+ continue
+ if move_line.account_id.id == self.account_rcv_id:
+ self.assertEquals(move_line.debit, 0.0)
+ self.assertEquals(move_line.credit, 38.21)
+ self.assertEquals(move_line.amount_currency, -50)
+ self.assertEquals(move_line.currency_id.id, self.currency_swiss_id)
+ checked_line += 1
+ continue
+ if move_line.account_id.id == self.account_rsa_id:
+ self.assertEquals(move_line.debit, 10.74)
+ self.assertEquals(move_line.credit, 0.0)
+ checked_line += 1
+ continue
+ self.assertEquals(checked_line, 3)
+
+
+
+ def test_balanced_supplier_invoice(self):
+ cr, uid = self.cr, self.uid
+ #we create a supplier invoice in CHF
+ invoice_id = self.account_invoice_model.create(cr, uid, {'partner_id': self.partner_agrolait_id,
+ 'reference_type': 'none',
+ 'currency_id': self.currency_swiss_id,
+ 'name': 'invoice to client',
+ 'account_id': self.account_rcv_id,
+ 'type': 'in_invoice'
+ })
+ self.account_invoice_line_model.create(cr, uid, {'product_id': self.product_id,
+ 'quantity': 1,
+ 'price_unit': 100,
+ 'invoice_id': invoice_id,
+ 'name': 'product that cost 100',})
+
+ #validate purchase
+ self.registry('account.invoice').signal_workflow(cr, uid, [invoice_id], 'invoice_open')
+ invoice_record = self.account_invoice_model.browse(cr, uid, [invoice_id])
+
+ #we pay half of it on a journal with currency in dollar (bank statement)
+ bank_stmt_id = self.acc_bank_stmt_model.create(cr, uid, {'journal_id': self.bank_journal_usd_id,})
+
+ bank_stmt_line_id = self.acc_bank_stmt_line_model.create(cr, uid, {'name': 'half payment',
+ 'statement_id': bank_stmt_id,
+ 'partner_id': self.partner_agrolait_id,
+ 'amount': -42,
+ 'amount_currency': -50,
+ 'currency_id': self.currency_swiss_id,})
+
+ #reconcile the payment with the invoice
+ for l in invoice_record.move_id.line_id:
+ if l.account_id.id == self.account_rcv_id:
+ line_id = l
+ break
+ self.acc_bank_stmt_line_model.process_reconciliation(cr, uid, bank_stmt_line_id, [
+ {'counterpart_move_line_id': line_id.id, 'credit':0, 'debit':50, 'name': line_id.name,}])
+
+ #we check that the line is balanced (bank statement line)
+ move_line_ids = self.acc_bank_stmt_model.browse(cr,uid,bank_stmt_id).move_line_ids
+
+ self.assertEquals(len(move_line_ids), 3)
+ checked_line = 0
+ for move_line in move_line_ids:
+ if move_line.account_id.id == self.account_usd_id:
+ self.assertEquals(move_line.debit, 0.0)
+ self.assertEquals(move_line.credit, 27.47)
+ self.assertEquals(move_line.amount_currency, -42)
+ self.assertEquals(move_line.currency_id.id, self.currency_usd_id)
+ checked_line += 1
+ continue
+ if move_line.account_id.id == self.account_rcv_id:
+ self.assertEquals(move_line.debit, 38.21)
+ self.assertEquals(move_line.credit, 0.0)
+ self.assertEquals(move_line.amount_currency, 50)
+ self.assertEquals(move_line.currency_id.id, self.currency_swiss_id)
+ checked_line += 1
+ continue
+ if move_line.account_id.id == self.account_rsa_id:
+ self.assertEquals(move_line.debit, 0.0)
+ self.assertEquals(move_line.credit, 10.74)
+ checked_line += 1
+ continue
+ self.assertEquals(checked_line, 3)
+
diff --git a/addons/account/views/account.xml b/addons/account/views/account.xml
index 8a9c209baa0..0718615e312 100644
--- a/addons/account/views/account.xml
+++ b/addons/account/views/account.xml
@@ -11,6 +11,7 @@
+
diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py
index a5f1621de98..1bfd5dd1224 100644
--- a/addons/account_voucher/account_voucher.py
+++ b/addons/account_voucher/account_voucher.py
@@ -41,47 +41,6 @@ class res_currency(osv.osv):
return res
-class res_company(osv.osv):
- _inherit = "res.company"
- _columns = {
- 'income_currency_exchange_account_id': fields.many2one(
- 'account.account',
- string="Gain Exchange Rate Account",
- domain="[('type', '=', 'other')]",),
- 'expense_currency_exchange_account_id': fields.many2one(
- 'account.account',
- string="Loss Exchange Rate Account",
- domain="[('type', '=', 'other')]",),
- }
-
-
-class account_config_settings(osv.osv_memory):
- _inherit = 'account.config.settings'
- _columns = {
- 'income_currency_exchange_account_id': fields.related(
- 'company_id', 'income_currency_exchange_account_id',
- type='many2one',
- relation='account.account',
- string="Gain Exchange Rate Account",
- domain="[('type', '=', 'other')]"),
- 'expense_currency_exchange_account_id': fields.related(
- 'company_id', 'expense_currency_exchange_account_id',
- type="many2one",
- relation='account.account',
- string="Loss Exchange Rate Account",
- domain="[('type', '=', 'other')]"),
- }
- def onchange_company_id(self, cr, uid, ids, company_id, context=None):
- res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context)
- if company_id:
- company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
- res['value'].update({'income_currency_exchange_account_id': company.income_currency_exchange_account_id and company.income_currency_exchange_account_id.id or False,
- 'expense_currency_exchange_account_id': company.expense_currency_exchange_account_id and company.expense_currency_exchange_account_id.id or False})
- else:
- res['value'].update({'income_currency_exchange_account_id': False,
- 'expense_currency_exchange_account_id': False})
- return res
-
class account_voucher(osv.osv):
def _check_paid(self, cr, uid, ids, name, args, context=None):
res = {}
diff --git a/addons/account_voucher/account_voucher_view.xml b/addons/account_voucher/account_voucher_view.xml
index ffcc8fc4b1b..2b0508a7542 100644
--- a/addons/account_voucher/account_voucher_view.xml
+++ b/addons/account_voucher/account_voucher_view.xml
@@ -193,26 +193,6 @@
{'state':'posted'}
-
-
-
- account.config.settings.inherit
-
- account.config.settings
- 20
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/l10n_be_coda/wizard/account_coda_import.py b/addons/l10n_be_coda/wizard/account_coda_import.py
index 76f07d2b400..3dec320323f 100644
--- a/addons/l10n_be_coda/wizard/account_coda_import.py
+++ b/addons/l10n_be_coda/wizard/account_coda_import.py
@@ -286,7 +286,7 @@ class account_coda_import(osv.osv_memory):
if 'counterpartyAddress' in line and line['counterpartyAddress'] != '':
note.append(_('Counter Party Address') + ': ' + line['counterpartyAddress'])
partner_id = None
- structured_com = ""
+ structured_com = False
bank_account_id = False
if line['communication_struct'] and 'communication_type' in line and line['communication_type'] == '101':
structured_com = line['communication']
@@ -322,21 +322,16 @@ class account_coda_import(osv.osv_memory):
self.pool.get('account.bank.statement.line').create(cr, uid, data, context=context)
if statement['coda_note'] != '':
self.pool.get('account.bank.statement').write(cr, uid, [statement['id']], {'coda_note': statement['coda_note']}, context=context)
- model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')
+ model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_reconcile_bank_statements')
action = self.pool[model].browse(cr, uid, action_id, context=context)
+ statements_ids = [statement['id'] for statement in statements]
return {
'name': action.name,
- 'view_type': action.view_type,
- 'view_mode': action.view_mode,
- 'res_model': action.res_model,
- 'domain': action.domain,
- 'context': action.context,
- 'type': 'ir.actions.act_window',
- 'search_view_id': action.search_view_id.id,
- 'views': [(v.view_id.id, v.view_mode) for v in action.view_ids]
+ 'tag': action.tag,
+ 'context': {'statement_ids': statements_ids},
+ 'type': 'ir.actions.client',
}
-
def rmspaces(s):
return " ".join(s.split())
diff --git a/openerp/addons/base/res/res_currency.py b/openerp/addons/base/res/res_currency.py
index 697d5ef2d2f..8a155d9a35e 100644
--- a/openerp/addons/base/res/res_currency.py
+++ b/openerp/addons/base/res/res_currency.py
@@ -21,6 +21,7 @@
import re
import time
+import math
from openerp import api, fields as fields2
from openerp import tools
@@ -270,6 +271,22 @@ class res_currency(osv.osv):
# apply rounding
return to_currency.round(to_amount) if round else to_amount
+ def get_format_currencies_js_function(self, cr, uid, context=None):
+ """ Returns a string that can be used to instanciate a javascript function that formats numbers as currencies.
+ That function expects the number as first parameter and the currency id as second parameter. In case of failure it returns undefined."""
+ function = ""
+ for row in self.search_read(cr, uid, domain=[], fields=['id', 'name', 'symbol', 'rounding', 'position'], context=context):
+ digits = int(math.log10(1 / row['rounding']))
+ symbol = row['symbol'] or row['name']
+
+ format_number_str = "openerp.web.format_value(arguments[0], {type: 'float', digits: [69," + str(digits) + "]}, 0.00)"
+ if row['position'] == 'after':
+ return_str = "return " + format_number_str + " + '\\xA0" + symbol + "';"
+ else:
+ return_str = "return '" + symbol + "\\xA0' + " + format_number_str + ";"
+ function += "if (arguments[1] === " + str(row['id']) + ") { " + return_str + " }"
+ return function
+
class res_currency_rate(osv.osv):
_name = "res.currency.rate"
_description = "Currency Rate"
@@ -285,4 +302,3 @@ class res_currency_rate(osv.osv):
_order = "name desc"
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-