2012-09-13 12:08:26 +00:00
openerp . account = function ( instance ) {
2012-11-12 09:36:09 +00:00
openerp . account . quickadd ( instance ) ;
2012-09-13 12:08:26 +00:00
var _t = instance . web . _t ,
_lt = instance . web . _lt ;
2012-09-14 13:06:30 +00:00
var QWeb = instance . web . qweb ;
2012-09-13 16:09:48 +00:00
2012-11-12 09:36:09 +00:00
instance . web . account = instance . web . account || { } ;
2012-09-13 16:09:48 +00:00
2014-05-30 16:47:50 +00:00
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' ,
init : function ( parent , context ) {
this . _super ( parent ) ;
this . max _reconciliations _displayed = 10 ;
2014-09-04 09:32:16 +00:00
if ( context . context . statement _id ) this . statement _ids = [ context . context . statement _id ] ;
if ( context . context . statement _ids ) this . statement _ids = context . context . statement _ids ;
2014-05-30 16:47:50 +00:00
this . title = context . context . title || _t ( "Reconciliation" ) ;
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
// Only for statistical purposes
this . lines _reconciled _with _ctrl _enter = 0 ;
this . time _widget _loaded = Date . now ( ) ;
// Stuff used by the children bankStatementReconciliationLine
2014-06-23 09:47:41 +00:00
this . max _move _lines _displayed = 5 ;
2014-05-30 16:47:50 +00:00
this . animation _speed = 100 ; // "Blocking" animations
this . aestetic _animation _speed = 300 ; // eye candy
this . map _tax _id _amount = { } ;
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
2014-09-04 09:32:16 +00:00
this . excluded _move _lines _ids = { } ;
2014-05-30 16:47:50 +00:00
// 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" ,
2014-07-11 15:15:34 +00:00
domain : [ [ 'type' , 'not in' , [ 'view' , 'closed' , 'consolidation' ] ] ] ,
2014-05-30 16:47:50 +00:00
} ,
} ,
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" ,
2014-07-11 15:15:34 +00:00
domain : [ [ 'type_tax_use' , 'in' , [ 'purchase' , 'all' ] ] , [ 'parent_id' , '=' , false ] ] ,
2014-05-30 16:47:50 +00:00
} ,
} ,
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" ,
} ,
} ,
} ;
} ,
start : function ( ) {
this . _super ( ) ;
var self = this ;
// Retreive statement infos and reconciliation data from the model
2014-07-18 13:43:00 +00:00
var lines _filter = [ [ 'journal_entry_id' , '=' , false ] , [ 'account_id' , '=' , false ] ] ;
2014-05-30 16:47:50 +00:00
var deferred _promises = [ ] ;
2014-09-04 09:32:16 +00:00
// 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)
2014-05-30 16:47:50 +00:00
deferred _promises . push ( self . model _bank _statement
2014-09-04 09:32:16 +00:00
. call ( "number_of_lines_reconciled" , [ self . statement _ids ] )
2014-05-30 16:47:50 +00:00
. then ( function ( num ) {
self . already _reconciled _lines = num ;
} )
) ;
}
2014-09-04 09:32:16 +00:00
// Get operation templates
2014-05-30 16:47:50 +00:00
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 ;
} ) ;
} )
) ;
2014-09-04 09:32:16 +00:00
// Get the function to format currencies
deferred _promises . push ( new instance . web . Model ( "res.currency" )
. call ( "get_format_currencies_js_function" )
. then ( function ( data ) {
2014-06-02 00:28:15 +00:00
self . formatCurrency = new Function ( "amount, currency_id" , data ) ;
2014-05-30 16:47:50 +00:00
} )
) ;
2014-09-04 09:32:16 +00:00
// Get statement lines
2014-05-30 16:47:50 +00:00
deferred _promises . push ( self . model _bank _statement _line
. query ( [ 'id' ] )
. filter ( lines _filter )
2014-09-04 09:32:16 +00:00
. order _by ( 'statement_id, id' )
2014-05-30 16:47:50 +00:00
. 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 } ) ;
} ) ;
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
// 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 } ) ;
} ) ;
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
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 ( ) ;
} ) ;
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
// 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 , total _lines : self . already _reconciled _lines + self . st _lines . length } ) ) ;
self . updateProgressbar ( ) ;
var reconciliations _to _show = self . st _lines . slice ( 0 , self . max _reconciliations _displayed ) ;
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 = [ ] ;
_ . each ( reconciliations _to _show , function ( st _line _id ) {
var datum = data . shift ( ) ;
child _promises . push ( self . displayReconciliation ( st _line _id , 'inactive' , false , true , datum . st _line , datum . reconciliation _proposition ) ) ;
} ) ;
$ . when . apply ( $ , child _promises ) . then ( function ( ) {
self . getChildren ( ) [ 0 ] . set ( "mode" , "match" ) ;
self . $ ( ".reconciliation_lines_container" ) . animate ( { opacity : 1 } , self . aestetic _animation _speed ) ;
} ) ;
} ) ;
} ) ;
} ,
keyboardShortcutsHandler : function ( e ) {
var self = this ;
2014-06-17 09:29:27 +00:00
if ( ( e . which === 13 || e . which === 10 ) && ( e . ctrlKey || e . metaKey ) ) {
2014-05-30 16:47:50 +00:00
$ . each ( self . getChildren ( ) , function ( i , o ) {
if ( o . is _valid && o . persistAndDestroy ( ) ) {
self . lines _reconciled _with _ctrl _enter ++ ;
}
} ) ;
}
} ,
2014-06-20 13:35:46 +00:00
2014-09-04 09:32:16 +00:00
// 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 ) {
2014-05-30 16:47:50 +00:00
var self = this ;
2014-09-04 09:32:16 +00:00
var excluded _ids = this . excluded _move _lines _ids [ partner _id ] ;
var excluded _move _lines _changed = false ;
2014-05-30 16:47:50 +00:00
_ . each ( line _ids , function ( line _id ) {
2014-09-04 09:32:16 +00:00
if ( excluded _ids . indexOf ( line _id ) === - 1 ) {
excluded _ids . push ( line _id ) ;
excluded _move _lines _changed = true ;
2014-05-30 16:47:50 +00:00
}
} ) ;
2014-09-04 09:32:16 +00:00
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
2014-05-30 16:47:50 +00:00
_ . each ( self . getChildren ( ) , function ( child ) {
2014-09-04 09:32:16 +00:00
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 ( ) ;
}
}
2014-05-30 16:47:50 +00:00
} ) ;
} ,
2014-09-04 09:32:16 +00:00
unexcludeMoveLines : function ( source _child , partner _id , line _ids ) {
2014-05-30 16:47:50 +00:00
var self = this ;
2014-09-04 09:32:16 +00:00
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
2014-05-30 16:47:50 +00:00
_ . each ( self . getChildren ( ) , function ( child ) {
2014-09-04 09:32:16 +00:00
if ( child . partner _id === partner _id && child !== source _child && ( child . get ( "mode" ) === "match" || child . $el . hasClass ( "no_match" ) ) )
child . updateMatches ( ) ;
2014-05-30 16:47:50 +00:00
} ) ;
} ,
displayReconciliation : function ( st _line _id , mode , animate _entrance , initial _data _provided , st _line , reconciliation _proposition ) {
var self = this ;
animate _entrance = ( animate _entrance === undefined ? true : animate _entrance ) ;
initial _data _provided = ( initial _data _provided === undefined ? false : initial _data _provided ) ;
var context = {
st _line _id : st _line _id ,
mode : mode ,
animate _entrance : animate _entrance ,
initial _data _provided : initial _data _provided ,
st _line : initial _data _provided ? st _line : undefined ,
reconciliation _proposition : initial _data _provided ? reconciliation _proposition : undefined ,
} ;
var widget = new instance . web . account . bankStatementReconciliationLine ( self , context ) ;
return widget . appendTo ( self . $ ( ".reconciliation_lines_container" ) ) ;
} ,
childValidated : function ( child ) {
var self = this ;
self . reconciled _lines ++ ;
self . updateProgressbar ( ) ;
self . doReloadMenuReconciliation ( ) ;
// Display new line if there are left
if ( self . last _displayed _reconciliation _index < self . st _lines . length ) {
self . displayReconciliation ( self . st _lines [ self . last _displayed _reconciliation _index ++ ] , 'inactive' ) ;
}
2014-09-04 09:32:16 +00:00
// Congratulate the user if the work is done
if ( self . reconciled _lines === self . st _lines . length ) {
self . displayDoneMessage ( ) ;
}
2014-05-30 16:47:50 +00:00
// Put the first line in match mode
if ( self . reconciled _lines !== self . st _lines . length ) {
var first _child = self . getChildren ( ) [ 0 ] ;
if ( first _child . get ( "mode" ) === "inactive" ) {
first _child . set ( "mode" , "match" ) ;
}
}
} ,
displayDoneMessage : function ( ) {
var self = this ;
2014-09-04 09:32:16 +00:00
var is _single _statement = self . statement _ids !== undefined && self . statement _ids . length === 1 ;
2014-05-30 16:47:50 +00:00
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 = [ ] ;
var time _taken ;
if ( sec _taken / 60 >= 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 !" ) + " <i class='fa fa-trophy congrats_icon'></i>" ;
else title = _t ( "Congrats, you're all done !" ) + " <i class='fa fa-thumbs-o-up congrats_icon'></i>" ;
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 . $ ( ".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 ,
2014-09-04 09:32:16 +00:00
has _statement _id : is _single _statement ,
2014-05-30 16:47:50 +00:00
} ) ) ;
// Animate it
var container = $ ( "<div style='overflow: hidden;' />" ) ;
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' } ) ;
self . $ ( ".button_back_to_statement" ) . click ( function ( ) {
self . do _action ( {
type : 'ir.actions.client' ,
tag : 'history_back' ,
} ) ;
} ) ;
2014-09-04 09:32:16 +00:00
if ( is _single _statement && self . $ ( ".button_close_statement" ) . length !== 0 ) {
2014-05-30 16:47:50 +00:00
self . $ ( ".button_close_statement" ) . hide ( ) ;
self . model _bank _statement
. query ( [ "balance_end_real" , "balance_end" ] )
2014-09-04 09:32:16 +00:00
. filter ( [ [ 'id' , '=' , self . statement _ids [ 0 ] ] ] )
2014-05-30 16:47:50 +00:00
. first ( )
. then ( function ( data ) {
if ( data . balance _end _real === data . balance _end ) {
self . $ ( ".button_close_statement" ) . show ( ) ;
self . $ ( ".button_close_statement" ) . click ( function ( ) {
self . $ ( ".button_close_statement" ) . attr ( "disabled" , "disabled" ) ;
self . model _bank _statement
2014-09-04 09:32:16 +00:00
. call ( "button_confirm_bank" , [ [ self . statement _ids [ 0 ] ] ] )
2014-05-30 16:47:50 +00:00
. then ( function ( ) {
self . do _action ( {
type : 'ir.actions.client' ,
tag : 'history_back' ,
} ) ;
} , function ( ) {
self . $ ( ".button_close_statement" ) . removeAttr ( "disabled" ) ;
} ) ;
} ) ;
}
} ) ;
}
} ,
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 .partner_name" : "partnerNameClickHandler" ,
"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 ) ;
2014-09-04 09:32:16 +00:00
this . formatCurrency = this . getParent ( ) . formatCurrency ;
2014-05-30 16:47:50 +00:00
if ( context . initial _data _provided ) {
// Process data
2014-09-04 09:32:16 +00:00
_ . each ( context . reconciliation _proposition , function ( line ) {
this . decorateMoveLine ( line , context . st _line . currency _id ) ;
} , this ) ;
2014-05-30 16:47:50 +00:00
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
var selected _line _ids = _ ( context . reconciliation _proposition ) . map ( function ( o ) { return o . id } ) ;
2014-09-04 09:32:16 +00:00
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 ) ;
2014-05-30 16:47:50 +00:00
} 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 _account _id _code = this . getParent ( ) . map _account _id _code ;
this . map _tax _id _amount = this . getParent ( ) . map _tax _id _amount ;
this . presets = this . getParent ( ) . presets ;
this . is _valid = true ;
this . is _consistent = true ; // Used to prevent bad server requests
2014-09-04 09:32:16 +00:00
this . total _move _lines _num = undefined ; // Used for pagers
2014-05-30 16:47:50 +00:00
this . filter = "" ;
2014-09-04 09:32:16 +00:00
// 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 ;
2014-05-30 16:47:50 +00:00
this . set ( "mode" , undefined ) ;
this . on ( "change:mode" , this , this . modeChanged ) ;
2014-09-04 09:32:16 +00:00
this . set ( "balance" , undefined ) ; // Debit is +, credit is -
this . on ( "change:balance" , this , this . balanceChanged ) ;
2014-05-30 16:47:50 +00:00
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 ) ;
2014-09-04 09:32:16 +00:00
this . mv _lines _deselected = [ ] ; // deselected lines are displayed on top of the match table
2014-05-30 16:47:50 +00:00
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 ;
2014-09-04 09:32:16 +00:00
if ( self . context . animate _entrance ) {
self . $el . fadeOut ( 0 ) ;
self . $el . slideUp ( 0 ) ;
2014-05-30 16:47:50 +00:00
}
2014-09-04 09:32:16 +00:00
return $ . when ( self . loadData ( ) ) . then ( function ( ) {
return $ . when ( self . render ( ) ) . then ( function ( ) {
2014-05-30 16:47:50 +00:00
self . is _consistent = true ;
// Make an entrance
self . animation _speed = self . getParent ( ) . animation _speed ;
self . aestetic _animation _speed = self . getParent ( ) . aestetic _animation _speed ;
2014-09-04 09:32:16 +00:00
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 ) ;
}
2014-05-30 16:47:50 +00:00
} ) ;
} ) ;
} ) ;
} ,
2014-09-04 09:32:16 +00:00
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 ( ) ;
} ) ;
} ,
2014-05-30 16:47:50 +00:00
restart : function ( mode ) {
var self = this ;
mode = ( mode === undefined ? 'inactive' : mode ) ;
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 ( ) {
2014-09-04 09:32:16 +00:00
self . getParent ( ) . unexcludeMoveLines ( self , self . partner _id , _ . map ( self . get ( "mv_lines_selected" ) , function ( o ) { return o . id } ) ) ;
2014-05-30 16:47:50 +00:00
$ . 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 } ) ;
2014-09-04 09:32:16 +00:00
self . mv _lines _deselected = [ ] ;
2014-05-30 16:47:50 +00:00
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 < create _form _fields _arr . length ; i ++ ) {
field _manager . fields _view . fields [ create _form _fields _arr [ i ] . id ] = _ . extend ( new Default _field ( ) , create _form _fields _arr [ i ] . field _properties ) ;
}
field _manager . fields _view . fields [ "change_partner" ] = _ . extend ( new Default _field ( ) , {
relation : "res.partner" ,
string : _t ( "Partner" ) ,
type : "many2one" ,
domain : [ [ 'parent_id' , '=' , false ] , '|' , [ 'customer' , '=' , true ] , [ 'supplier' , '=' , true ] ] ,
} ) ;
// Returns a function that serves as a xhr response handler
var hideGroupResponseClosureFactory = function ( field _widget , $container , obj _key ) {
return function ( has _group ) {
if ( has _group ) $container . show ( ) ;
else {
field _widget . destroy ( ) ;
$container . remove ( ) ;
delete self [ obj _key ] ;
}
} ;
} ;
// generate the create "form"
self . create _form = [ ] ;
for ( var i = 0 ; i < create _form _fields _arr . length ; i ++ ) {
var field _data = create _form _fields _arr [ i ] ;
// create widgets
var node = new Default _node ( field _data . id ) ;
if ( ! field _data . required ) node . attrs . modifiers = "" ;
var field = new field _data . constructor ( field _manager , node ) ;
self [ field _data . id + "_field" ] = field ;
self . create _form . push ( field ) ;
// on update : change the last created line
field . corresponding _property = field _data . corresponding _property ;
field . on ( "change:value" , self , self . formCreateInputChanged ) ;
// append to DOM
var $field _container = $ ( QWeb . render ( "form_create_field" , { id : field _data . id , label : field _data . label } ) ) ;
field . appendTo ( $field _container . find ( "td" ) ) ;
self . $ ( ".create_form" ) . prepend ( $field _container ) ;
// now that widget's dom has been created (appendTo does that), bind events and adds tabindex
if ( field _data . field _properties . type != "many2one" ) {
// Triggers change:value TODO : moche bind ?
field . $el . find ( "input" ) . keyup ( function ( e , field ) { field . commit _value ( ) ; } . bind ( null , null , field ) ) ;
}
field . $el . find ( "input" ) . attr ( "tabindex" , field _data . tabindex ) ;
// Hide the field if group not OK
if ( field _data . group !== undefined ) {
var target = $field _container ;
target . hide ( ) ;
self . model _res _users
. call ( "has_group" , [ field _data . group ] )
. then ( hideGroupResponseClosureFactory ( field , target , ( field _data . id + "_field" ) ) ) ;
}
}
// generate the change partner "form"
var change _partner _node = new Default _node ( "change_partner" ) ; change _partner _node . attrs . modifiers = "" ;
self . change _partner _field = new instance . web . form . FieldMany2One ( field _manager , change _partner _node ) ;
self . change _partner _field . appendTo ( self . $ ( ".change_partner_container" ) ) ;
self . change _partner _field . on ( "change:value" , self . change _partner _field , function ( ) {
self . changePartner ( this . get _value ( ) ) ;
} ) ;
2014-09-04 09:32:16 +00:00
self . change _partner _field . $el . find ( "input" ) . attr ( "placeholder" , _t ( "Select Partner" ) ) ;
2014-05-30 16:47:50 +00:00
field _manager . do _show ( ) ;
} ,
/** Utils */
/* TODO : if t-call for attr, all in qweb */
decorateStatementLine : function ( line ) {
line . q _popover = QWeb . render ( "bank_statement_reconciliation_line_details" , { line : line } ) ;
} ,
// adds fields, prefixed with q_, to the move line for qweb rendering
2014-09-04 09:32:16 +00:00
decorateMoveLine : function ( line , currency _id ) {
2014-05-30 16:47:50 +00:00
line . partial _reconcile = false ;
line . propose _partial _reconcile = false ;
2014-09-04 09:32:16 +00:00
line [ 'credit' ] = [ line [ 'debit' ] , line [ 'debit' ] = line [ 'credit' ] ] [ 0 ] ;
2014-05-30 16:47:50 +00:00
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 _label = line . name ;
2014-09-04 09:32:16 +00:00
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 )
2014-05-30 16:47:50 +00:00
line . q _label += " : " + line . ref ;
} ,
bindPopoverTo : function ( el ) {
var self = this ;
$ ( el ) . addClass ( "bootstrap_popover" ) ;
el . popover ( {
'placement' : 'left' ,
'container' : self . el ,
'html' : true ,
'trigger' : 'hover' ,
'animation' : false ,
'toggle' : 'popover'
} ) ;
} ,
islineCreatedBeingEditedValid : function ( ) {
var line = this . get ( "line_created_being_edited" ) [ 0 ] ;
return line . amount // must be defined and not 0
&& line . account _id // must be defined (and will never be 0)
&& line . label ; // must be defined and not empty
} ,
/* returns the created lines, plus the ones being edited if valid */
getCreatedLines : function ( ) {
var self = this ;
var created _lines = self . get ( "lines_created" ) . slice ( ) ;
if ( self . islineCreatedBeingEditedValid ( ) )
return created _lines . concat ( self . get ( "line_created_being_edited" ) ) ;
else
return created _lines ;
} ,
/** Matching */
moveLineClickHandler : function ( e ) {
var self = this ;
if ( e . currentTarget . dataset . selected === "true" ) self . deselectMoveLine ( e . currentTarget ) ;
else self . selectMoveLine ( e . currentTarget ) ;
} ,
2014-06-20 13:35:46 +00:00
2014-05-30 16:47:50 +00:00
selectMoveLine : function ( mv _line ) {
var self = this ;
var line _id = mv _line . dataset . lineid ;
2014-09-04 09:32:16 +00:00
// 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' ,
} , $ ( "<div />" ) . 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]);
}
2014-05-30 16:47:50 +00:00
} ,
2014-06-20 13:35:46 +00:00
2014-05-30 16:47:50 +00:00
deselectMoveLine : function ( mv _line ) {
var self = this ;
var line _id = mv _line . dataset . lineid ;
2014-09-04 09:32:16 +00:00
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]);
2014-05-30 16:47:50 +00:00
} ,
/** Matches pagination */
pagerControlLeftHandler : function ( ) {
var self = this ;
2014-09-04 09:32:16 +00:00
if ( self . $ ( ".pager_control_left" ) . hasClass ( "disabled" ) ) { return ; /* shouldn't happen, anyway*/ }
if ( self . total _move _lines _num < 0 ) { return ; }
2014-05-30 16:47:50 +00:00
self . set ( "pager_index" , self . get ( "pager_index" ) - 1 ) ;
} ,
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
pagerControlRightHandler : function ( ) {
var self = this ;
var new _index = self . get ( "pager_index" ) + 1 ;
2014-09-04 09:32:16 +00:00
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 ; }
2014-05-30 16:47:50 +00:00
self . set ( "pager_index" , new _index ) ;
} ,
filterHandler : function ( ) {
var self = this ;
self . set ( "pager_index" , 0 ) ;
self . filter = self . $ ( ".filter" ) . val ( ) ;
2014-09-04 09:32:16 +00:00
window . clearTimeout ( self . apply _filter _timeout ) ;
self . apply _filter _timeout = window . setTimeout ( self . proxy ( 'updateMatches' ) , 200 ) ;
2014-05-30 16:47:50 +00:00
} ,
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
/** Creating */
initializeCreateForm : function ( ) {
var self = this ;
_ . each ( self . create _form , function ( field ) {
field . set ( "value" , false ) ;
} ) ;
2014-09-04 09:32:16 +00:00
self . label _field . set ( "value" , self . st _line . name ) ;
2014-05-30 16:47:50 +00:00
self . amount _field . set ( "value" , - 1 * self . get ( "balance" ) ) ;
self . account _id _field . focus ( ) ;
} ,
addLineBeingEdited : function ( ) {
var self = this ;
if ( ! self . islineCreatedBeingEditedValid ( ) ) return ;
self . set ( "lines_created" , self . get ( "lines_created" ) . concat ( self . get ( "line_created_being_edited" ) ) ) ;
// Add empty created line
var new _id = self . get ( "line_created_being_edited" ) [ 0 ] . id + 1 ;
self . set ( "line_created_being_edited" , [ { 'id' : new _id } ] ) ;
self . initializeCreateForm ( ) ;
} ,
removeLine : function ( $line ) {
var self = this ;
var line _id = $line . data ( "lineid" ) ;
// if deleting the created line that is being edited, validate it before
if ( line _id === self . get ( "line_created_being_edited" ) [ 0 ] . id ) {
self . addLineBeingEdited ( ) ;
}
self . set ( "lines_created" , _ . filter ( self . get ( "lines_created" ) , function ( o ) { return o . id != line _id } ) ) ;
self . amount _field . set ( "value" , - 1 * self . get ( "balance" ) ) ;
} ,
presetClickHandler : function ( e ) {
var self = this ;
self . initializeCreateForm ( ) ;
var preset = self . presets [ e . currentTarget . dataset . presetid ] ;
2014-09-04 09:32:16 +00:00
// 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 ) ;
2014-05-30 16:47:50 +00:00
for ( var key in preset ) {
if ( ! preset . hasOwnProperty ( key ) || key === "amount" ) continue ;
2014-09-04 09:32:16 +00:00
if ( preset [ key ] && self . hasOwnProperty ( key + "_field" ) )
2014-05-30 16:47:50 +00:00
self [ key + "_field" ] . set _value ( preset [ key ] ) ;
}
if ( preset . amount && self . amount _field ) {
if ( preset . amount _type === "fixed" )
2014-09-04 09:32:16 +00:00
self . amount _field . set _value ( preset . amount ) ;
2014-05-30 16:47:50 +00:00
else if ( preset . amount _type === "percentage_of_total" )
2014-09-04 09:32:16 +00:00
self . amount _field . set _value ( self . st _line . amount * preset . amount / 100 ) ;
2014-05-30 16:47:50 +00:00
else if ( preset . amount _type === "percentage_of_balance" ) {
self . amount _field . set _value ( 0 ) ;
self . updateBalance ( ) ;
2014-09-04 09:32:16 +00:00
self . amount _field . set _value ( - 1 * self . get ( "balance" ) * preset . amount / 100 ) ;
2014-05-30 16:47:50 +00:00
}
}
} ,
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
/** Display */
initialLineClickHandler : function ( ) {
var self = this ;
if ( self . get ( "mode" ) === "match" ) {
self . set ( "mode" , "inactive" ) ;
} else {
self . set ( "mode" , "match" ) ;
}
} ,
lineOpenBalanceClickHandler : function ( ) {
var self = this ;
if ( self . get ( "mode" ) === "create" ) {
2014-06-17 09:29:27 +00:00
self . addLineBeingEdited ( ) ;
2014-05-30 16:47:50 +00:00
self . set ( "mode" , "match" ) ;
} else {
self . set ( "mode" , "create" ) ;
}
} ,
partnerNameClickHandler : function ( ) {
var self = this ;
2014-09-04 09:32:16 +00:00
// Delete statement line's partner
return self . changePartner ( '' , function ( ) {
self . $ ( ".partner_name" ) . hide ( ) ;
self . $ ( ".change_partner_container" ) . show ( ) ;
} ) ;
2014-05-30 16:47:50 +00:00
} ,
/** Views updating */
updateAccountingViewMatchedLines : function ( ) {
var self = this ;
$ . each ( self . $ ( ".tbody_matched_lines .bootstrap_popover" ) , function ( ) { $ ( this ) . popover ( 'destroy' ) } ) ;
self . $ ( ".tbody_matched_lines" ) . empty ( ) ;
_ ( self . get ( "mv_lines_selected" ) ) . each ( function ( line ) {
var $line = $ ( QWeb . render ( "bank_statement_reconciliation_move_line" , { line : line , selected : true } ) ) ;
self . bindPopoverTo ( $line . find ( ".line_info_button" ) ) ;
if ( line . propose _partial _reconcile ) self . bindPopoverTo ( $line . find ( ".do_partial_reconcile_button" ) ) ;
if ( line . partial _reconcile ) self . bindPopoverTo ( $line . find ( ".undo_partial_reconcile_button" ) ) ;
self . $ ( ".tbody_matched_lines" ) . append ( $line ) ;
} ) ;
} ,
updateAccountingViewCreatedLines : function ( ) {
var self = this ;
$ . each ( self . $ ( ".tbody_created_lines .bootstrap_popover" ) , function ( ) { $ ( this ) . popover ( 'destroy' ) } ) ;
self . $ ( ".tbody_created_lines" ) . empty ( ) ;
_ ( self . getCreatedLines ( ) ) . each ( function ( line ) {
var $line = $ ( QWeb . render ( "bank_statement_reconciliation_created_line" , { line : line } ) ) ;
$line . find ( ".line_remove_button" ) . click ( function ( ) { self . removeLine ( $ ( this ) . closest ( ".created_line" ) ) } ) ;
self . $ ( ".tbody_created_lines" ) . append ( $line ) ;
if ( line . no _remove _action ) {
// Then the previous line's remove button deletes this line too
$line . hover ( function ( ) { $ ( this ) . prev ( ) . addClass ( "active" ) } , function ( ) { $ ( this ) . prev ( ) . removeClass ( "active" ) } ) ;
}
} ) ;
} ,
updateMatchView : function ( ) {
var self = this ;
var table = self . $ ( ".match table" ) ;
var nothing _displayed = true ;
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
// 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 ;
2014-09-04 09:32:16 +00:00
_ ( _ . 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 ;
} ) ;
2014-05-30 16:47:50 +00:00
_ ( self . get ( "mv_lines" ) ) . each ( function ( line ) {
2014-09-04 09:32:16 +00:00
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 ;
2014-05-30 16:47:50 +00:00
} ) ;
2014-09-04 09:32:16 +00:00
if ( nothing _displayed && this . filter !== "" )
2014-05-30 16:47:50 +00:00
table . append ( QWeb . render ( "filter_no_match" , { filter _str : self . filter } ) ) ;
} ,
updatePagerControls : function ( ) {
var self = this ;
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
if ( self . get ( "pager_index" ) === 0 )
self . $ ( ".pager_control_left" ) . addClass ( "disabled" ) ;
else
self . $ ( ".pager_control_left" ) . removeClass ( "disabled" ) ;
2014-09-04 09:32:16 +00:00
if ( self . total _move _lines _num <= ( ( self . get ( "pager_index" ) + 1 ) * self . max _move _lines _displayed ) )
2014-05-30 16:47:50 +00:00
self . $ ( ".pager_control_right" ) . addClass ( "disabled" ) ;
else
self . $ ( ".pager_control_right" ) . removeClass ( "disabled" ) ;
} ,
/** Properties changed */
// Updates the validation button and the "open balance" line
balanceChanged : function ( ) {
var self = this ;
var balance = self . get ( "balance" ) ;
2014-06-17 09:29:27 +00:00
self . $ ( ".tbody_open_balance" ) . empty ( ) ;
2014-05-30 16:47:50 +00:00
// Special case hack : no identified partner
if ( self . st _line . has _no _partner ) {
if ( Math . abs ( balance ) . toFixed ( 3 ) === "0.000" ) {
self . $ ( ".button_ok" ) . addClass ( "oe_highlight" ) ;
self . $ ( ".button_ok" ) . removeAttr ( "disabled" ) ;
self . $ ( ".button_ok" ) . text ( "OK" ) ;
self . is _valid = true ;
} else {
self . $ ( ".button_ok" ) . removeClass ( "oe_highlight" ) ;
self . $ ( ".button_ok" ) . attr ( "disabled" , "disabled" ) ;
self . $ ( ".button_ok" ) . text ( "OK" ) ;
self . is _valid = false ;
2014-06-17 09:29:27 +00:00
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 ) : "" ) ;
2014-09-04 09:32:16 +00:00
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" ) ;
2014-06-17 09:29:27 +00:00
self . $ ( ".tbody_open_balance" ) . append ( $line ) ;
2014-05-30 16:47:50 +00:00
}
return ;
}
if ( Math . abs ( balance ) . toFixed ( 3 ) === "0.000" ) {
self . $ ( ".button_ok" ) . addClass ( "oe_highlight" ) ;
self . $ ( ".button_ok" ) . text ( "OK" ) ;
} else {
self . $ ( ".button_ok" ) . removeClass ( "oe_highlight" ) ;
self . $ ( ".button_ok" ) . text ( "Keep open" ) ;
2014-06-02 00:28:15 +00:00
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 ) : "" ) ;
2014-09-04 09:32:16 +00:00
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 ]
} ) ) ;
2014-05-30 16:47:50 +00:00
self . $ ( ".tbody_open_balance" ) . append ( $line ) ;
}
} ,
modeChanged : function ( ) {
var self = this ;
self . $ ( ".action_pane.active" ) . removeClass ( "active" ) ;
2014-09-04 09:32:16 +00:00
// Special case hack : if no_partner, either inactive or create
2014-05-30 16:47:50 +00:00
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" ;
2014-09-04 09:32:16 +00:00
} 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 ;
2014-05-30 16:47:50 +00:00
}
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" ) {
2014-09-04 09:32:16 +00:00
return $ . when ( self . updateMatches ( ) ) . then ( function ( ) {
2014-05-30 16:47:50 +00:00
if ( self . $el . hasClass ( "no_match" ) ) {
self . set ( "mode" , "inactive" ) ;
return ;
}
self . $ ( ".match" ) . slideDown ( self . animation _speed ) ;
self . $ ( ".create" ) . slideUp ( self . animation _speed ) ;
self . el . dataset . mode = "match" ;
} ) ;
} 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 ( ) {
2014-09-04 09:32:16 +00:00
this . updateMatches ( ) ;
2014-05-30 16:47:50 +00:00
} ,
mvLinesChanged : function ( ) {
var self = this ;
2014-09-04 09:32:16 +00:00
// 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 ) ;
}
2014-05-30 16:47:50 +00:00
// If there is no match to display, disable match view and pass in mode inactive
2014-09-04 09:32:16 +00:00
if ( self . total _move _lines _num + self . mv _lines _deselected . length === 0 && self . filter === "" ) {
2014-05-30 16:47:50 +00:00
self . $el . addClass ( "no_match" ) ;
if ( self . get ( "mode" ) === "match" ) {
self . set ( "mode" , "inactive" ) ;
}
} else {
self . $el . removeClass ( "no_match" ) ;
}
self . updateMatchView ( ) ;
self . updatePagerControls ( ) ;
} ,
mvLinesSelectedChanged : function ( elt , val ) {
var self = this ;
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
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 } ) ;
2014-09-04 09:32:16 +00:00
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 ( ) ;
} ) ;
2014-05-30 16:47:50 +00:00
} ,
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
// 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 ;
2014-06-02 00:28:15 +00:00
line _created _being _edited [ 0 ] . currency _id = self . st _line . currency _id ;
2014-05-30 16:47:50 +00:00
// 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 = self . map _tax _id _amount [ self . tax _id _field . get ( "value" ) ] ;
if ( amount && tax ) {
deferred _tax = $ . when ( self . model _tax
. call ( "compute_for_bank_reconciliation" , [ self . tax _id _field . get ( "value" ) , amount ] ) )
. then ( function ( data ) {
2014-07-11 15:15:34 +00:00
line _created _being _edited [ 0 ] . amount _with _tax = line _created _being _edited [ 0 ] . amount ;
2014-05-30 16:47:50 +00:00
line _created _being _edited [ 0 ] . amount = ( data . total . toFixed ( 3 ) === amount . toFixed ( 3 ) ? amount : data . total ) ;
2014-07-11 15:15:34 +00:00
var current _line _cursor = 1 ;
$ . each ( data . taxes , function ( index , tax ) {
if ( tax . amount !== 0.0 ) {
2014-09-04 09:32:16 +00:00
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
} ;
2014-07-11 15:15:34 +00:00
current _line _cursor = current _line _cursor + 1 ;
2014-09-04 09:32:16 +00:00
}
2014-07-11 15:15:34 +00:00
} ) ;
2014-05-30 16:47:50 +00:00
}
) ;
} else {
line _created _being _edited [ 0 ] . amount = amount ;
2014-09-04 09:32:16 +00:00
line _created _being _edited . length = 1 ;
2014-05-30 16:47:50 +00:00
deferred _tax . resolve ( ) ;
}
} else { deferred _tax . resolve ( ) ; }
$ . when ( deferred _tax ) . then ( function ( ) {
// Format amounts
2014-07-11 15:15:34 +00:00
$ . each ( line _created _being _edited , function ( index , val ) {
if ( val . amount )
line _created _being _edited [ index ] . amount _str = self . formatCurrency ( Math . abs ( val . amount ) , val . currency _id ) ;
} ) ;
2014-05-30 16:47:50 +00:00
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 ) {
2014-09-04 09:32:16 +00:00
line . debit += balance ;
2014-06-02 00:28:15 +00:00
line . debit _str = self . formatCurrency ( line . debit , self . st _line . currency _id ) ;
2014-05-30 16:47:50 +00:00
} else {
line . credit -= balance ;
2014-06-02 00:28:15 +00:00
line . credit _str = self . formatCurrency ( line . credit , self . st _line . currency _id ) ;
2014-05-30 16:47:50 +00:00
}
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 ) {
2014-06-02 00:28:15 +00:00
var self = this ;
2014-05-30 16:47:50 +00:00
if ( line . initial _amount > 0 ) {
line . debit = line . initial _amount ;
2014-06-02 00:28:15 +00:00
line . debit _str = self . formatCurrency ( line . debit , self . st _line . currency _id ) ;
2014-05-30 16:47:50 +00:00
} else {
line . credit = - 1 * line . initial _amount ;
2014-06-02 00:28:15 +00:00
line . credit _str = self . formatCurrency ( line . credit , self . st _line . currency _id ) ;
2014-05-30 16:47:50 +00:00
}
line . propose _partial _reconcile = true ;
line . partial _reconcile = false ;
} ,
updateBalance : function ( ) {
var self = this ;
var mv _lines _selected = self . get ( "mv_lines_selected" ) ;
2014-09-04 09:32:16 +00:00
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
2014-05-30 16:47:50 +00:00
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 ;
} ) ;
2014-09-04 09:32:16 +00:00
// 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 ;
2014-05-30 16:47:50 +00:00
self . set ( "balance" , balance ) ;
// Propose partial reconciliation if necessary
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 ( ) ;
}
} ,
2014-06-20 13:35:46 +00:00
2014-09-04 09:32:16 +00:00
// Loads move lines according to the widget's state
updateMatches : function ( ) {
if ( this . st _line . has _no _partner ) return ;
2014-05-30 16:47:50 +00:00
var self = this ;
2014-09-04 09:32:16 +00:00
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 ) ;
2014-05-30 16:47:50 +00:00
} ) ;
} ,
2014-09-04 09:32:16 +00:00
2014-05-30 16:47:50 +00:00
// Changes the partner_id of the statement_line in the DB and reloads the widget
2014-09-04 09:32:16 +00:00
changePartner : function ( partner _id , callback ) {
2014-05-30 16:47:50 +00:00
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 ( ) {
2014-09-04 09:32:16 +00:00
self . do _load _reconciliation _proposition = false ; // of the server might set the statement line's partner
2014-05-30 16:47:50 +00:00
return $ . when ( self . restart ( self . get ( "mode" ) ) ) . then ( function ( ) {
2014-09-04 09:32:16 +00:00
self . do _load _reconciliation _proposition = true ;
2014-05-30 16:47:50 +00:00
self . is _consistent = true ;
2014-09-04 09:32:16 +00:00
self . set ( "mode" , "match" ) ;
if ( callback ) callback ( ) ;
2014-05-30 16:47:50 +00:00
} ) ;
} ) ;
} ,
// 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 ,
} ;
} ,
// idem
prepareCreatedMoveLineForPersisting : function ( line ) {
var dict = { } ;
if ( dict [ 'account_id' ] === undefined )
dict [ 'account_id' ] = line . account _id ;
dict [ 'name' ] = line . label ;
2014-07-11 15:15:34 +00:00
var amount = line . tax _id ? line . amount _with _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 . is _tax _line ) dict [ 'is_tax_line' ] = line . is _tax _line ;
2014-05-30 16:47:50 +00:00
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' ] = _t ( "Open balance" ) ;
if ( balance > 0 ) dict [ 'debit' ] = balance ;
if ( balance < 0 ) dict [ 'credit' ] = - 1 * balance ;
return dict ;
} ,
// Persist data, notify parent view and terminate widget
2014-09-04 09:32:16 +00:00
persistAndDestroy : function ( speed ) {
2014-05-30 16:47:50 +00:00
var self = this ;
2014-09-04 09:32:16 +00:00
speed = ( isNaN ( speed ) ? self . animation _speed : speed ) ;
2014-05-30 16:47:50 +00:00
if ( ! self . is _consistent ) return ;
2014-09-04 09:32:16 +00:00
self . getParent ( ) . unexcludeMoveLines ( self , self . partner _id , _ . map ( self . get ( "mv_lines_selected" ) , function ( o ) { return o . id } ) ) ;
2014-05-30 16:47:50 +00:00
// Sliding animation
var height = self . $el . outerHeight ( ) ;
var container = $ ( "<div />" ) ;
container . css ( "height" , height )
. css ( "marginTop" , self . $el . css ( "marginTop" ) )
. css ( "marginBottom" , self . $el . css ( "marginBottom" ) ) ;
self . $el . wrap ( container ) ;
2014-09-04 09:32:16 +00:00
var deferred _animation = self . $el . parent ( ) . slideUp ( speed * height / 150 ) ;
2014-05-30 16:47:50 +00:00
// RPC
2014-09-04 09:32:16 +00:00
return $ . when ( self . makeRPCForPersisting ( ) )
2014-05-30 16:47:50 +00:00
. then ( function ( ) {
$ . 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 ) ;
} ) ;
} ) ;
} , function ( ) {
2014-09-04 09:32:16 +00:00
self . $el . parent ( ) . slideDown ( speed * height / 150 , function ( ) {
2014-05-30 16:47:50 +00:00
self . $el . unwrap ( ) ;
} ) ;
} ) ;
2014-09-04 09:32:16 +00:00
} ,
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 ] ) ;
2014-05-30 16:47:50 +00:00
} ,
} ) ;
2012-10-15 15:33:39 +00:00
instance . web . views . add ( 'tree_account_reconciliation' , 'instance.web.account.ReconciliationListView' ) ;
2012-09-13 16:09:48 +00:00
instance . web . account . ReconciliationListView = instance . web . ListView . extend ( {
init : function ( ) {
this . _super . apply ( this , arguments ) ;
2012-09-21 10:11:22 +00:00
var self = this ;
2012-09-17 09:55:18 +00:00
this . current _partner = null ;
2012-10-22 10:39:15 +00:00
this . on ( 'record_selected' , this , function ( ) {
2012-09-21 10:11:22 +00:00
if ( self . get _selected _ids ( ) . length === 0 ) {
self . $ ( ".oe_account_recon_reconcile" ) . attr ( "disabled" , "" ) ;
} else {
self . $ ( ".oe_account_recon_reconcile" ) . removeAttr ( "disabled" ) ;
}
} ) ;
2012-09-14 13:06:30 +00:00
} ,
2012-10-19 10:53:04 +00:00
load _list : function ( ) {
2012-09-17 12:53:09 +00:00
var self = this ;
2012-09-14 13:06:30 +00:00
var tmp = this . _super . apply ( this , arguments ) ;
2012-09-17 12:53:09 +00:00
if ( this . partners ) {
2012-09-17 12:04:42 +00:00
this . $el . prepend ( QWeb . render ( "AccountReconciliation" , { widget : this } ) ) ;
2012-09-17 13:49:57 +00:00
this . $ ( ".oe_account_recon_previous" ) . click ( function ( ) {
2013-04-18 15:13:53 +00:00
self . current _partner = ( ( ( self . current _partner - 1 ) % self . partners . length ) + self . partners . length ) % self . partners . length ;
2012-09-17 12:53:09 +00:00
self . search _by _partner ( ) ;
} ) ;
2012-09-17 13:49:57 +00:00
this . $ ( ".oe_account_recon_next" ) . click ( function ( ) {
2012-09-17 12:53:09 +00:00
self . current _partner = ( self . current _partner + 1 ) % self . partners . length ;
self . search _by _partner ( ) ;
} ) ;
2012-09-17 13:49:57 +00:00
this . $ ( ".oe_account_recon_reconcile" ) . click ( function ( ) {
2012-09-17 12:53:09 +00:00
self . reconcile ( ) ;
} ) ;
2012-09-20 16:22:57 +00:00
this . $ ( ".oe_account_recom_mark_as_reconciled" ) . click ( function ( ) {
self . mark _as _reconciled ( ) ;
} ) ;
2012-09-17 12:53:09 +00:00
}
2012-09-14 13:06:30 +00:00
return tmp ;
} ,
2012-09-14 15:31:33 +00:00
do _search : function ( domain , context , group _by ) {
2012-09-17 08:41:30 +00:00
var self = this ;
this . last _domain = domain ;
this . last _context = context ;
this . last _group _by = group _by ;
this . old _search = _ . bind ( this . _super , this ) ;
2012-09-20 12:20:53 +00:00
var mod = new instance . web . Model ( "account.move.line" , context , domain ) ;
2012-11-12 09:36:09 +00:00
return mod . call ( "list_partners_to_reconcile" , [ ] ) . then ( function ( result ) {
2012-09-17 13:49:57 +00:00
var current = self . current _partner !== null ? self . partners [ self . current _partner ] [ 0 ] : null ;
2012-09-20 12:20:53 +00:00
self . partners = result ;
2012-09-17 12:53:09 +00:00
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 ;
2012-09-17 09:55:18 +00:00
self . search _by _partner ( ) ;
2012-09-14 15:31:33 +00:00
} ) ;
} ,
2012-09-17 09:55:18 +00:00
search _by _partner : function ( ) {
2012-09-21 12:59:17 +00:00
var self = this ;
2012-09-21 13:08:16 +00:00
var fct = function ( ) {
2012-09-21 12:59:17 +00:00
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 ) ;
2012-09-21 13:08:16 +00:00
} ;
if ( self . current _partner === null ) {
self . last _reconciliation _date = _t ( "Never" ) ;
return fct ( ) ;
} else {
return new instance . web . Model ( "res.partner" ) . call ( "read" ,
2012-11-12 09:36:09 +00:00
[ self . partners [ self . current _partner ] [ 0 ] , [ "last_reconciliation_date" ] ] ) . then ( function ( res ) {
2012-09-21 13:08:16 +00:00
self . last _reconciliation _date =
instance . web . format _value ( res . last _reconciliation _date , { "type" : "datetime" } , _t ( "Never" ) ) ;
return fct ( ) ;
} ) ;
}
2012-09-17 09:55:18 +00:00
} ,
2012-09-17 12:53:09 +00:00
reconcile : function ( ) {
var self = this ;
var ids = this . get _selected _ids ( ) ;
2012-09-17 13:49:57 +00:00
if ( ids . length === 0 ) {
2014-04-10 15:50:22 +00:00
new instance . web . Dialog ( this , {
2012-09-17 13:49:57 +00:00
title : _t ( "Warning" ) ,
2014-04-11 12:28:40 +00:00
size : 'medium' ,
2014-04-10 15:50:22 +00:00
} , $ ( "<div />" ) . text ( _t ( "You must choose at least one record." ) ) ) . open ( ) ;
2012-09-17 13:49:57 +00:00
return false ;
}
2012-11-12 09:36:09 +00:00
new instance . web . Model ( "ir.model.data" ) . call ( "get_object_reference" , [ "account" , "action_view_account_move_line_reconcile" ] ) . then ( function ( result ) {
2012-09-17 13:49:57 +00:00
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
2012-11-12 09:36:09 +00:00
} ) . done ( function ( result ) {
2012-12-19 17:40:09 +00:00
result . context = instance . web . pyeval . eval ( 'contexts' , [ result . context , additional _context ] ) ;
2012-09-17 13:49:57 +00:00
result . flags = result . flags || { } ;
result . flags . new _window = true ;
2012-10-17 14:56:39 +00:00
return self . do _action ( result , {
on _close : function ( ) {
self . do _search ( self . last _domain , self . last _context , self . last _group _by ) ;
}
2012-09-17 13:49:57 +00:00
} ) ;
} ) ;
} ) ;
2012-09-17 12:53:09 +00:00
} ,
2012-09-20 16:22:57 +00:00
mark _as _reconciled : function ( ) {
var self = this ;
var id = self . partners [ self . current _partner ] [ 0 ] ;
2012-11-12 09:36:09 +00:00
new instance . web . Model ( "res.partner" ) . call ( "mark_as_reconciled" , [ [ id ] ] ) . then ( function ( ) {
2012-09-20 16:22:57 +00:00
self . do _search ( self . last _domain , self . last _context , self . last _group _by ) ;
} ) ;
} ,
2012-10-19 10:53:04 +00:00
do _select : function ( ids , records ) {
2012-10-22 10:39:15 +00:00
this . trigger ( 'record_selected' )
2012-10-19 10:53:04 +00:00
this . _super . apply ( this , arguments ) ;
} ,
2012-09-13 16:09:48 +00:00
} ) ;
2012-11-12 09:36:09 +00:00
2012-09-20 13:00:28 +00:00
} ;