@ -26,15 +26,22 @@ import logging
from openerp.osv import osv
from openerp.tools.translate import _
from openerp.addons.account.report.common_report_header import common_report_header
from openerp.addons.account.report.common_report_header \
import common_report_header
_logger = logging . getLogger ( ' financial.reports.webkit ' )
MAX_MONSTER_SLICE = 50000
class CommonReportHeaderWebkit ( common_report_header ) :
""" Define common helper for financial report """
####################From getter helper #####################################
######################################################################
# From getter helper #
######################################################################
def get_start_period_br ( self , data ) :
return self . _get_info ( data , ' period_from ' , ' account.period ' )
@ -112,20 +119,26 @@ class CommonReportHeaderWebkit(common_report_header):
def _get_form_param ( self , param , data , default = False ) :
return data . get ( ' form ' , { } ) . get ( param , default )
####################Account and account line filter helper #################
#############################################
# Account and account line filter helper #
#############################################
def sort_accounts_with_structure ( self , root_account_ids , account_ids , context = None ) :
def sort_accounts_with_structure ( self , root_account_ids , account_ids ,
context = None ) :
""" Sort accounts by code respecting their structure """
def recursive_sort_by_code ( accounts , parent ) :
sorted_accounts = [ ]
# add all accounts with same parent
level_accounts = [ account for account in accounts
if account [ ' parent_id ' ] and account [ ' parent_id ' ] [ 0 ] == parent [ ' id ' ] ]
# add consolidation children of parent, as they are logically on the same level
if account [ ' parent_id ' ]
and account [ ' parent_id ' ] [ 0 ] == parent [ ' id ' ] ]
# add consolidation children of parent, as they are logically on
# the same level
if parent . get ( ' child_consol_ids ' ) :
level_accounts . extend ( [ account for account in accounts
if account [ ' id ' ] in parent [ ' child_consol_ids ' ] ] )
if account [ ' id ' ]
in parent [ ' child_consol_ids ' ] ] )
# stop recursion if no children found
if not level_accounts :
return [ ]
@ -134,16 +147,17 @@ class CommonReportHeaderWebkit(common_report_header):
for level_account in level_accounts :
sorted_accounts . append ( level_account [ ' id ' ] )
sorted_accounts . extend ( recursive_sort_by_code ( accounts , parent = level_account ) )
sorted_accounts . extend (
recursive_sort_by_code ( accounts , parent = level_account ) )
return sorted_accounts
if not account_ids :
return [ ]
accounts_data = self . pool . get ( ' account.account ' ) . read ( self . cr , self . uid ,
account_ids ,
[ ' id ' , ' parent_id ' , ' level ' , ' code ' , ' child_consol_ids ' ] ,
context = context )
accounts_data = self . pool . get ( ' account.account ' ) . read (
self . cr , self . uid , account_ids ,
[ ' id ' , ' parent_id ' , ' level ' , ' code ' , ' child_consol_ids ' ] ,
context = context )
sorted_accounts = [ ]
@ -151,7 +165,8 @@ class CommonReportHeaderWebkit(common_report_header):
if account_data [ ' id ' ] in root_account_ids ]
for root_account_data in root_accounts_data :
sorted_accounts . append ( root_account_data [ ' id ' ] )
sorted_accounts . extend ( recursive_sort_by_code ( accounts_data , root_account_data ) )
sorted_accounts . extend (
recursive_sort_by_code ( accounts_data , root_account_data ) )
# fallback to unsorted accounts when sort failed
# sort fails when the levels are miscalculated by account.account
@ -162,11 +177,14 @@ class CommonReportHeaderWebkit(common_report_header):
return sorted_accounts
def get_all_accounts ( self , account_ids , exclude_type = None , only_type = None , filter_report_type = None , context = None ) :
def get_all_accounts ( self , account_ids , exclude_type = None , only_type = None ,
filter_report_type = None , context = None ) :
""" Get all account passed in params with their childrens
@param exclude_type : list of types to exclude ( view , receivable , payable , consolidation , other )
@param only_type : list of types to filter on ( view , receivable , payable , consolidation , other )
@param exclude_type : list of types to exclude ( view , receivable ,
payable , consolidation , other )
@param only_type : list of types to filter on ( view , receivable ,
payable , consolidation , other )
@param filter_report_type : list of report type to filter on
"""
context = context or { }
@ -176,9 +194,11 @@ class CommonReportHeaderWebkit(common_report_header):
acc_obj = self . pool . get ( ' account.account ' )
for account_id in account_ids :
accounts . append ( account_id )
accounts + = acc_obj . _get_children_and_consol ( self . cursor , self . uid , account_id , context = context )
accounts + = acc_obj . _get_children_and_consol (
self . cursor , self . uid , account_id , context = context )
res_ids = list ( set ( accounts ) )
res_ids = self . sort_accounts_with_structure ( account_ids , res_ids , context = context )
res_ids = self . sort_accounts_with_structure (
account_ids , res_ids , context = context )
if exclude_type or only_type or filter_report_type :
sql_filters = { ' ids ' : tuple ( res_ids ) }
@ -207,17 +227,22 @@ class CommonReportHeaderWebkit(common_report_header):
res_ids = [ res_id for res_id in res_ids if res_id in only_ids ]
return res_ids
####################Periods and fiscal years helper #######################
##########################################
# Periods and fiscal years helper #
##########################################
def _get_opening_periods ( self ) :
""" Return the list of all journal that can be use to create opening entries
We actually filter on this instead of opening period as older version of OpenERP
did not have this notion """
return self . pool . get ( ' account.period ' ) . search ( self . cursor , self . uid , [ ( ' special ' , ' = ' , True ) ] )
""" Return the list of all journal that can be use to create opening
entries .
We actually filter on this instead of opening period as older version
of OpenERP did not have this notion """
return self . pool . get ( ' account.period ' ) . search ( self . cursor , self . uid ,
[ ( ' special ' , ' = ' , True ) ] )
def exclude_opening_periods ( self , period_ids ) :
period_obj = self . pool . get ( ' account.period ' )
return period_obj . search ( self . cr , self . uid , [ [ ' special ' , ' = ' , False ] , [ ' id ' , ' in ' , period_ids ] ] )
return period_obj . search ( self . cr , self . uid , [ [ ' special ' , ' = ' , False ] ,
[ ' id ' , ' in ' , period_ids ] ] )
def get_included_opening_period ( self , period ) :
""" Return the opening included in normal period we use the assumption
@ -228,7 +253,7 @@ class CommonReportHeaderWebkit(common_report_header):
( ' date_start ' , ' >= ' , period . date_start ) ,
( ' date_stop ' , ' <= ' , period . date_stop ) ,
( ' company_id ' , ' = ' , period . company_id . id ) ] ,
limit = 1 )
limit = 1 )
def periods_contains_move_lines ( self , period_ids ) :
if not period_ids :
@ -236,12 +261,16 @@ class CommonReportHeaderWebkit(common_report_header):
mv_line_obj = self . pool . get ( ' account.move.line ' )
if isinstance ( period_ids , ( int , long ) ) :
period_ids = [ period_ids ]
return mv_line_obj . search ( self . cursor , self . uid , [ ( ' period_id ' , ' in ' , period_ids ) ] , limit = 1 ) and True or False
return mv_line_obj . search ( self . cursor , self . uid ,
[ ( ' period_id ' , ' in ' , period_ids ) ] , limit = 1 ) \
and True or False
def _get_period_range_from_periods ( self , start_period , stop_period , mode = None ) :
def _get_period_range_from_periods ( self , start_period , stop_period ,
mode = None ) :
"""
Deprecated . We have to use now the build_ctx_periods of period_obj otherwise we ' ll have
inconsistencies , because build_ctx_periods does never filter on the the special
Deprecated . We have to use now the build_ctx_periods of period_obj
otherwise we ' ll have inconsistencies, because build_ctx_periods does
never filter on the the special
"""
period_obj = self . pool . get ( ' account.period ' )
search_period = [ ( ' date_start ' , ' >= ' , start_period . date_start ) ,
@ -252,8 +281,10 @@ class CommonReportHeaderWebkit(common_report_header):
res = period_obj . search ( self . cursor , self . uid , search_period )
return res
def _get_period_range_from_start_period ( self , start_period , include_opening = False ,
fiscalyear = False , stop_at_previous_opening = False ) :
def _get_period_range_from_start_period ( self , start_period ,
include_opening = False ,
fiscalyear = False ,
stop_at_previous_opening = False ) :
""" We retrieve all periods before start period """
opening_period_id = False
past_limit = [ ]
@ -262,24 +293,28 @@ class CommonReportHeaderWebkit(common_report_header):
# We look for previous opening period
if stop_at_previous_opening :
opening_search = [ ( ' special ' , ' = ' , True ) ,
( ' date_stop ' , ' < ' , start_period . date_start ) ]
( ' date_stop ' , ' < ' , start_period . date_start ) ]
if fiscalyear :
opening_search . append ( ( ' fiscalyear_id ' , ' = ' , fiscalyear . id ) )
opening_periods = period_obj . search ( self . cursor , self . uid , opening_search ,
opening_periods = period_obj . search ( self . cursor , self . uid ,
opening_search ,
order = ' date_stop desc ' )
for opening_period in opening_periods :
validation_res = mv_line_obj . search ( self . cursor ,
self . uid ,
[ ( ' period_id ' , ' = ' , opening_period ) ] ,
[ ( ' period_id ' , ' = ' ,
opening_period ) ] ,
limit = 1 )
if validation_res :
opening_period_id = opening_period
break
if opening_period_id :
#we also look for overlapping periods
opening_period_br = period_obj . browse ( self . cursor , self . uid , opening_period_id )
past_limit = [ ( ' date_start ' , ' >= ' , opening_period_br . date_stop ) ]
# we also look for overlapping periods
opening_period_br = period_obj . browse (
self . cursor , self . uid , opening_period_id )
past_limit = [
( ' date_start ' , ' >= ' , opening_period_br . date_stop ) ]
periods_search = [ ( ' date_stop ' , ' <= ' , start_period . date_stop ) ]
periods_search + = past_limit
@ -303,7 +338,8 @@ class CommonReportHeaderWebkit(common_report_header):
def get_last_fiscalyear_period ( self , fiscalyear ) :
return self . _get_st_fiscalyear_period ( fiscalyear , order = ' DESC ' )
def _get_st_fiscalyear_period ( self , fiscalyear , special = False , order = ' ASC ' ) :
def _get_st_fiscalyear_period ( self , fiscalyear , special = False ,
order = ' ASC ' ) :
period_obj = self . pool . get ( ' account.period ' )
p_id = period_obj . search ( self . cursor ,
self . uid ,
@ -315,9 +351,12 @@ class CommonReportHeaderWebkit(common_report_header):
raise osv . except_osv ( _ ( ' No period found ' ) , ' ' )
return period_obj . browse ( self . cursor , self . uid , p_id [ 0 ] )
####################Initial Balance helper #################################
###############################
# Initial Balance helper #
###############################
def _compute_init_balance ( self , account_id = None , period_ids = None , mode = ' computed ' , default_values = False ) :
def _compute_init_balance ( self , account_id = None , period_ids = None ,
mode = ' computed ' , default_values = False ) :
if not isinstance ( period_ids , list ) :
period_ids = [ period_ids ]
res = { }
@ -332,7 +371,8 @@ class CommonReportHeaderWebkit(common_report_header):
" sum(amount_currency) AS curr_balance "
" FROM account_move_line "
" WHERE period_id in %s "
" AND account_id = %s " , ( tuple ( period_ids ) , account_id ) )
" AND account_id = %s " ,
( tuple ( period_ids ) , account_id ) )
res = self . cursor . dictfetchone ( )
except Exception , exc :
@ -348,67 +388,81 @@ class CommonReportHeaderWebkit(common_report_header):
def _read_opening_balance ( self , account_ids , start_period ) :
""" Read opening balances from the opening balance
"""
opening_period_selected = self . get_included_opening_period ( start_period )
opening_period_selected = self . get_included_opening_period (
start_period )
if not opening_period_selected :
raise osv . except_osv (
_ ( ' Error ' ) ,
_ ( ' No opening period found to compute the opening balances. \n '
' You have to configure a period on the first of January '
' with the special flag. ' ) )
_ ( ' Error ' ) ,
_ ( ' No opening period found to compute the opening balances. \n '
' You have to configure a period on the first of January '
' with the special flag. ' ) )
res = { }
for account_id in account_ids :
res [ account_id ] = self . _compute_init_balance ( account_id , opening_period_selected , mode = ' read ' )
res [ account_id ] = self . _compute_init_balance (
account_id , opening_period_selected , mode = ' read ' )
return res
def _compute_initial_balances ( self , account_ids , start_period , fiscalyear ) :
""" We compute initial balance.
If form is filtered by date all initial balance are equal to 0
This function will sum pear and apple in currency amount if account as no secondary currency """
# if opening period is included in start period we do not need to compute init balance
# we just read it from opening entries
This function will sum pear and apple in currency amount if account as
no secondary currency """
# if opening period is included in start period we do not need to
# compute init balance we just read it from opening entries
res = { }
# PNL and Balance accounts are not computed the same way look for attached doc
# We include opening period in pnl account in order to see if opening entries
# were created by error on this account
pnl_periods_ids = self . _get_period_range_from_start_period ( start_period , fiscalyear = fiscalyear ,
include_opening = True )
bs_period_ids = self . _get_period_range_from_start_period ( start_period , include_opening = True ,
stop_at_previous_opening = True )
opening_period_selected = self . get_included_opening_period ( start_period )
for acc in self . pool . get ( ' account.account ' ) . browse ( self . cursor , self . uid , account_ids ) :
# PNL and Balance accounts are not computed the same way look for
# attached doc We include opening period in pnl account in order to see
# if opening entries were created by error on this account
pnl_periods_ids = self . _get_period_range_from_start_period (
start_period , fiscalyear = fiscalyear , include_opening = True )
bs_period_ids = self . _get_period_range_from_start_period (
start_period , include_opening = True , stop_at_previous_opening = True )
opening_period_selected = self . get_included_opening_period (
start_period )
for acc in self . pool . get ( ' account.account ' ) . browse ( self . cursor ,
self . uid ,
account_ids ) :
res [ acc . id ] = self . _compute_init_balance ( default_values = True )
if acc . user_type . close_method == ' none ' :
# we compute the initial balance for close_method == none only when we print a GL
# during the year, when the opening period is not included in the period selection!
# we compute the initial balance for close_method == none only
# when we print a GL during the year, when the opening period
# is not included in the period selection!
if pnl_periods_ids and not opening_period_selected :
res [ acc . id ] = self . _compute_init_balance ( acc . id , pnl_periods_ids )
res [ acc . id ] = self . _compute_init_balance (
acc . id , pnl_periods_ids )
else :
res [ acc . id ] = self . _compute_init_balance ( acc . id , bs_period_ids )
return res
####################Account move retrieval helper ##########################
def _get_move_ids_from_periods ( self , account_id , period_start , period_stop , target_move ) :
################################################
# Account move retrieval helper #
################################################
def _get_move_ids_from_periods ( self , account_id , period_start , period_stop ,
target_move ) :
move_line_obj = self . pool . get ( ' account.move.line ' )
period_obj = self . pool . get ( ' account.period ' )
periods = period_obj . build_ctx_periods ( self . cursor , self . uid , period_start . id , period_stop . id )
periods = period_obj . build_ctx_periods (
self . cursor , self . uid , period_start . id , period_stop . id )
if not periods :
return [ ]
search = [ ( ' period_id ' , ' in ' , periods ) , ( ' account_id ' , ' = ' , account_id ) ]
search = [
( ' period_id ' , ' in ' , periods ) , ( ' account_id ' , ' = ' , account_id ) ]
if target_move == ' posted ' :
search + = [ ( ' move_id.state ' , ' = ' , ' posted ' ) ]
return move_line_obj . search ( self . cursor , self . uid , search )
def _get_move_ids_from_dates ( self , account_id , date_start , date_stop , target_move , mode = ' include_opening ' ) :
def _get_move_ids_from_dates ( self , account_id , date_start , date_stop ,
target_move , mode = ' include_opening ' ) :
# TODO imporve perfomance by setting opening period as a property
move_line_obj = self . pool . get ( ' account.move.line ' )
search_period = [ ( ' date ' , ' >= ' , date_start ) ,
( ' date ' , ' <= ' , date_stop ) ,
( ' account_id ' , ' = ' , account_id ) ]
# actually not used because OpenERP itself always include the opening when we
# get the periods from january to december
# actually not used because OpenERP itself always include the opening
# when we get the periods from january to december
if mode == ' exclude_opening ' :
opening = self . _get_opening_periods ( )
if opening :
@ -419,20 +473,28 @@ class CommonReportHeaderWebkit(common_report_header):
return move_line_obj . search ( self . cursor , self . uid , search_period )
def get_move_lines_ids ( self , account_id , main_filter , start , stop , target_move , mode = ' include_opening ' ) :
def get_move_lines_ids ( self , account_id , main_filter , start , stop ,
target_move , mode = ' include_opening ' ) :
""" Get account move lines base on form data """
if mode not in ( ' include_opening ' , ' exclude_opening ' ) :
raise osv . except_osv ( _ ( ' Invalid query mode ' ) , _ ( ' Must be in include_opening, exclude_opening ' ) )
raise osv . except_osv (
_ ( ' Invalid query mode ' ) ,
_ ( ' Must be in include_opening, exclude_opening ' ) )
if main_filter in ( ' filter_period ' , ' filter_no ' ) :
return self . _get_move_ids_from_periods ( account_id , start , stop , target_move )
return self . _get_move_ids_from_periods ( account_id , start , stop ,
target_move )
elif main_filter == ' filter_date ' :
return self . _get_move_ids_from_dates ( account_id , start , stop , target_move )
return self . _get_move_ids_from_dates ( account_id , start , stop ,
target_move )
else :
raise osv . except_osv ( _ ( ' No valid filter ' ) , _ ( ' Please set a valid time filter ' ) )
raise osv . except_osv (
_ ( ' No valid filter ' ) , _ ( ' Please set a valid time filter ' ) )
def _get_move_line_datas ( self , move_line_ids , order = ' per.special DESC, l.date ASC, per.date_start ASC, m.name ASC ' ) :
def _get_move_line_datas ( self , move_line_ids ,
order = ' per.special DESC, l.date ASC, \
per . date_start ASC , m . name ASC ' ):
# Possible bang if move_line_ids is too long
# We can not slice here as we have to do the sort.
# If slice has to be done it means that we have to reorder in python
@ -475,7 +537,8 @@ SELECT l.id AS id,
FROM account_move_line l
JOIN account_move m on ( l . move_id = m . id )
LEFT JOIN res_currency c on ( l . currency_id = c . id )
LEFT JOIN account_move_reconcile partialrec on ( l . reconcile_partial_id = partialrec . id )
LEFT JOIN account_move_reconcile partialrec
on ( l . reconcile_partial_id = partialrec . id )
LEFT JOIN account_move_reconcile fullrec on ( l . reconcile_id = fullrec . id )
LEFT JOIN res_partner p on ( l . partner_id = p . id )
LEFT JOIN account_invoice i on ( m . id = i . move_id )
@ -506,8 +569,10 @@ SELECT account_move.id,
AND m2 . account_id < > % s limit % s ) , ' , ' )
FROM account_move
JOIN account_move_line on ( account_move_line . move_id = account_move . id )
JOIN account_account on ( account_move_line . account_id = account_account . id )
JOIN account_move_line
on ( account_move_line . move_id = account_move . id )
JOIN account_account
on ( account_move_line . account_id = account_account . id )
WHERE move_id in % s """
try :
@ -524,8 +589,10 @@ WHERE move_id in %s"""
return True
def _get_initial_balance_mode ( self , start_period ) :
opening_period_selected = self . get_included_opening_period ( start_period )
opening_move_lines = self . periods_contains_move_lines ( opening_period_selected )
opening_period_selected = self . get_included_opening_period (
start_period )
opening_move_lines = self . periods_contains_move_lines (
opening_period_selected )
if opening_move_lines :
return ' opening_balance '
else :