@ -3,39 +3,42 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from datetime import datetime , timedelta
from odoo import _ , api , fields , models
from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT
from odoo import api , fields , models , _
class ReportStatementCommon ( models . AbstractModel ) :
""" Abstract Report Statement for use in other models """
_name = ' statement.common '
_description = ' Statement Reports Common '
_name = " statement.common "
_description = " Statement Reports Common "
def _get_invoice_address ( self , part ) :
inv_addr_id = part . address_get ( [ ' invoice ' ] ) . get ( ' invoice ' , part . id )
inv_addr_id = part . address_get ( [ " invoice " ] ) . get ( " invoice " , part . id )
return self . env [ " res.partner " ] . browse ( inv_addr_id )
def _format_date_to_partner_lang (
self ,
date ,
date_format = DEFAULT_SERVER_DATE_FORMAT
self , date , date_format = DEFAULT_SERVER_DATE_FORMAT
) :
if isinstance ( date , str ) :
date = datetime . strptime ( date , DEFAULT_SERVER_DATE_FORMAT )
return date . strftime ( date_format ) if date else ' '
return date . strftime ( date_format ) if date else " "
def _get_account_display_lines ( self , company_id , partner_ids , date_start ,
date_end , account_type ) :
def _get_account_display_lines (
self , company_id , partner_ids , date_start , date_end , account_type
) :
raise NotImplementedError
def _get_account_initial_balance ( self , company_id , partner_ids ,
date_start , account_type ) :
def _get_account_initial_balance (
self , company_id , partner_ids , date_start , account_type
) :
return { }
def _show_buckets_sql_q1 ( self , partners , date_end , account_type ) :
return str ( self . _cr . mogrify ( """
return str (
self . _cr . mogrify (
"""
SELECT l . partner_id , l . currency_id , l . company_id , l . move_id ,
CASE WHEN l . balance > 0.0
THEN l . balance - sum ( coalesce ( pd . amount , 0.0 ) )
@ -76,11 +79,16 @@ class ReportStatementCommon(models.AbstractModel):
GROUP BY l . partner_id , l . currency_id , l . date , l . date_maturity ,
l . amount_currency , l . balance , l . move_id ,
l . company_id , l . id
""" , locals()), " utf-8 " )
""" ,
locals ( ) ,
) ,
" utf-8 " ,
)
def _show_buckets_sql_q2 ( self , date_end , minus_30 , minus_60 , minus_90 ,
minus_120 ) :
return str ( self . _cr . mogrify ( """
def _show_buckets_sql_q2 ( self , date_end , minus_30 , minus_60 , minus_90 , minus_120 ) :
return str (
self . _cr . mogrify (
"""
SELECT partner_id , currency_id , date_maturity , open_due ,
open_due_currency , move_id , company_id ,
CASE
@ -146,17 +154,27 @@ class ReportStatementCommon(models.AbstractModel):
FROM Q1
GROUP BY partner_id , currency_id , date_maturity , open_due ,
open_due_currency , move_id , company_id
""" , locals()), " utf-8 " )
""" ,
locals ( ) ,
) ,
" utf-8 " ,
)
def _show_buckets_sql_q3 ( self , company_id ) :
return str ( self . _cr . mogrify ( """
return str (
self . _cr . mogrify (
"""
SELECT Q2 . partner_id , current , b_1_30 , b_30_60 , b_60_90 , b_90_120 ,
b_over_120 ,
COALESCE ( Q2 . currency_id , c . currency_id ) AS currency_id
FROM Q2
JOIN res_company c ON ( c . id = Q2 . company_id )
WHERE c . id = % ( company_id ) s
""" , locals()), " utf-8 " )
""" ,
locals ( ) ,
) ,
" utf-8 " ,
)
def _show_buckets_sql_q4 ( self ) :
return """
@ -172,41 +190,36 @@ class ReportStatementCommon(models.AbstractModel):
def _get_bucket_dates ( self , date_end , aging_type ) :
return getattr (
self , ' _get_bucket_dates_ %s ' % aging_type ,
self . _get_bucket_dates_days
self , " _get_bucket_dates_ %s " % aging_type , self . _get_bucket_dates_days
) ( date_end )
def _get_bucket_dates_days ( self , date_end ) :
return {
' date_end ' : date_end ,
' minus_30 ' : date_end - timedelta ( days = 30 ) ,
' minus_60 ' : date_end - timedelta ( days = 60 ) ,
' minus_90 ' : date_end - timedelta ( days = 90 ) ,
' minus_120 ' : date_end - timedelta ( days = 120 ) ,
" date_end " : date_end ,
" minus_30 " : date_end - timedelta ( days = 30 ) ,
" minus_60 " : date_end - timedelta ( days = 60 ) ,
" minus_90 " : date_end - timedelta ( days = 90 ) ,
" minus_120 " : date_end - timedelta ( days = 120 ) ,
}
def _get_bucket_dates_months ( self , date_end ) :
res = { }
d = date_end
for k in (
" date_end " ,
" minus_30 " ,
" minus_60 " ,
" minus_90 " ,
" minus_120 " ,
) :
for k in ( " date_end " , " minus_30 " , " minus_60 " , " minus_90 " , " minus_120 " ) :
res [ k ] = d
d = d . replace ( day = 1 ) - timedelta ( days = 1 )
return res
def _get_account_show_buckets ( self , company_id , partner_ids , date_end ,
account_type , aging_type ) :
def _get_account_show_buckets (
self , company_id , partner_ids , date_end , account_type , aging_type
) :
buckets = dict ( map ( lambda x : ( x , [ ] ) , partner_ids ) )
partners = tuple ( partner_ids )
full_dates = self . _get_bucket_dates ( date_end , aging_type )
# pylint: disable=E8103
# All input queries are properly escaped - false positive
self . env . cr . execute ( """
self . env . cr . execute (
"""
WITH Q1 AS ( % s ) ,
Q2 AS ( % s ) ,
Q3 AS ( % s ) ,
@ -217,63 +230,63 @@ class ReportStatementCommon(models.AbstractModel):
AS balance
FROM Q4
GROUP BY partner_id , currency_id , current , b_1_30 , b_30_60 ,
b_60_90 , b_90_120 , b_over_120 """ % (
b_60_90 , b_90_120 , b_over_120 """
% (
self . _show_buckets_sql_q1 ( partners , date_end , account_type ) ,
self . _show_buckets_sql_q2 (
full_dates [ ' date_end ' ] ,
full_dates [ ' minus_30 ' ] ,
full_dates [ ' minus_60 ' ] ,
full_dates [ ' minus_90 ' ] ,
full_dates [ ' minus_120 ' ] ) ,
full_dates [ " date_end " ] ,
full_dates [ " minus_30 " ] ,
full_dates [ " minus_60 " ] ,
full_dates [ " minus_90 " ] ,
full_dates [ " minus_120 " ] ,
) ,
self . _show_buckets_sql_q3 ( company_id ) ,
self . _show_buckets_sql_q4 ( ) ) )
self . _show_buckets_sql_q4 ( ) ,
)
)
for row in self . env . cr . dictfetchall ( ) :
buckets [ row . pop ( ' partner_id ' ) ] . append ( row )
buckets [ row . pop ( " partner_id " ) ] . append ( row )
return buckets
def _get_bucket_labels ( self , date_end , aging_type ) :
return getattr (
self , ' _get_bucket_labels_ %s ' % aging_type ,
self . _get_bucket_dates_days
self , " _get_bucket_labels_ %s " % aging_type , self . _get_bucket_dates_days
) ( date_end )
def _get_bucket_labels_days ( self , date_end ) :
return [
_ ( ' Current ' ) ,
_ ( ' 1 - 30 Days ' ) ,
_ ( ' 31 - 60 Days ' ) ,
_ ( ' 61 - 90 Days ' ) ,
_ ( ' 91 - 120 Days ' ) ,
_ ( ' 121 Days + ' ) ,
_ ( ' Total ' ) ,
_ ( " Current " ) ,
_ ( " 1 - 30 Days " ) ,
_ ( " 31 - 60 Days " ) ,
_ ( " 61 - 90 Days " ) ,
_ ( " 91 - 120 Days " ) ,
_ ( " 121 Days + " ) ,
_ ( " Total " ) ,
]
def _get_bucket_labels_months ( self , date_end ) :
return [
_ ( ' Current ' ) ,
_ ( ' 1 Month ' ) ,
_ ( ' 2 Months ' ) ,
_ ( ' 3 Months ' ) ,
_ ( ' 4 Months ' ) ,
_ ( ' Older ' ) ,
_ ( ' Total ' ) ,
_ ( " Current " ) ,
_ ( " 1 Month " ) ,
_ ( " 2 Months " ) ,
_ ( " 3 Months " ) ,
_ ( " 4 Months " ) ,
_ ( " Older " ) ,
_ ( " Total " ) ,
]
def _get_line_currency_defaults ( self , currency_id , currencies ,
balance_forward ) :
def _get_line_currency_defaults ( self , currency_id , currencies , balance_forward ) :
if currency_id not in currencies :
# This will only happen if currency is inactive
currencies [ currency_id ] = (
self . env [ ' res.currency ' ] . browse ( currency_id )
)
currencies [ currency_id ] = self . env [ " res.currency " ] . browse ( currency_id )
return (
{
' lines ' : [ ] ,
' buckets ' : [ ] ,
' balance_forward ' : balance_forward ,
' amount_due ' : balance_forward ,
" lines " : [ ] ,
" buckets " : [ ] ,
" balance_forward " : balance_forward ,
" amount_due " : balance_forward ,
} ,
currencies
currencies ,
)
@api.multi
@ -298,45 +311,48 @@ class ReportStatementCommon(models.AbstractModel):
}
}
"""
company_id = data [ ' company_id ' ]
partner_ids = data [ ' partner_ids ' ]
date_start = data . get ( ' date_start ' )
company_id = data [ " company_id " ]
partner_ids = data [ " partner_ids " ]
date_start = data . get ( " date_start " )
if date_start and isinstance ( date_start , str ) :
date_start = datetime . strptime (
date_start , DEFAULT_SERVER_DATE_FORMAT
) . date ( )
date_end = data [ ' date_end ' ]
date_end = data [ " date_end " ]
if isinstance ( date_end , str ) :
date_end = datetime . strptime (
date_end , DEFAULT_SERVER_DATE_FORMAT
) . date ( )
account_type = data [ ' account_type ' ]
aging_type = data [ ' aging_type ' ]
date_end = datetime . strptime ( date_end , DEFAULT_SERVER_DATE_FORMAT ) . date ( )
account_type = data [ " account_type " ]
aging_type = data [ " aging_type " ]
today = fields . Date . today ( )
amount_field = data . get ( ' amount_field ' , ' amount ' )
amount_field = data . get ( " amount_field " , " amount " )
# There should be relatively few of these, so to speed performance
# we cache them - default needed if partner lang not set
self . _cr . execute ( """
self . _cr . execute (
"""
SELECT p . id , l . date_format
FROM res_partner p LEFT JOIN res_lang l ON p . lang = l . code
WHERE p . id IN % ( partner_ids ) s
""" , { " partner_ids " : tuple(partner_ids)})
""" ,
{ " partner_ids " : tuple ( partner_ids ) } ,
)
date_formats = { r [ 0 ] : r [ 1 ] for r in self . _cr . fetchall ( ) }
default_fmt = self . env [ " res.lang " ] . _lang_get (
self . env . user . lang ) . date_format
currencies = { x . id : x for x in self . env [ ' res.currency ' ] . search ( [ ] ) }
default_fmt = self . env [ " res.lang " ] . _lang_get ( self . env . user . lang ) . date_format
currencies = { x . id : x for x in self . env [ " res.currency " ] . search ( [ ] ) }
res = { }
# get base data
lines = self . _get_account_display_lines (
company_id , partner_ids , date_start , date_end , account_type )
company_id , partner_ids , date_start , date_end , account_type
)
balances_forward = self . _get_account_initial_balance (
company_id , partner_ids , date_start , account_type )
company_id , partner_ids , date_start , account_type
)
if data [ ' show_aging_buckets ' ] :
if data [ " show_aging_buckets " ] :
buckets = self . _get_account_show_buckets (
company_id , partner_ids , date_end , account_type , aging_type )
company_id , partner_ids , date_end , account_type , aging_type
)
bucket_labels = self . _get_bucket_labels ( date_end , aging_type )
else :
bucket_labels = { }
@ -346,69 +362,65 @@ class ReportStatementCommon(models.AbstractModel):
partners_to_remove = set ( )
for partner_id in partner_ids :
res [ partner_id ] = {
' today ' : format_date (
today ,
date_formats . get ( partner_id , default_fmt )
" today " : format_date ( today , date_formats . get ( partner_id , default_fmt ) ) ,
" start " : format_date (
date_start , date_ formats . get ( partner_id , default_fmt )
) ,
' start ' : format_date (
date_start ,
date_formats . get ( partner_id , default_fmt )
) ,
' end ' : format_date (
date_end ,
date_formats . get ( partner_id , default_fmt )
) ,
' currencies ' : { } ,
" end " : format_date ( date_end , date_formats . get ( partner_id , default_fmt ) ) ,
" currencies " : { } ,
}
currency_dict = res [ partner_id ] [ ' currencies ' ]
currency_dict = res [ partner_id ] [ " currencies " ]
for line in balances_forward . get ( partner_id , [ ] ) :
currency_dict [ line [ ' currency_id ' ] ] , currencies = (
self . _get_line_currency_defaults (
line [ ' currency_id ' ] , currencies , line [ ' balance ' ] )
(
currency_dict [ line [ " currency_id " ] ] ,
currencies ,
) = self . _get_line_currency_defaults (
line [ " currency_id " ] , currencies , line [ " balance " ]
)
for line in lines [ partner_id ] :
if line [ ' currency_id ' ] not in currency_dict :
currency_dict [ line [ ' currency_id ' ] ] , currencies = (
self . _get_line_currency_defaults (
line [ ' currency_id ' ] , currencies , 0.0 )
if line [ " currency_id " ] not in currency_dict :
(
currency_dict [ line [ " currency_id " ] ] ,
currencies ,
) = self . _get_line_currency_defaults (
line [ " currency_id " ] , currencies , 0.0
)
line_currency = currency_dict [ line [ ' currency_id ' ] ]
if not line [ ' blocked ' ] :
line_currency [ ' amount_due ' ] + = line [ amount_field ]
line [ ' balance ' ] = line_currency [ ' amount_due ' ]
line [ ' date ' ] = format_date (
line [ ' date ' ] , date_formats . get ( partner_id , default_fmt )
line_currency = currency_dict [ line [ " currency_id " ] ]
if not line [ " blocked " ] :
line_currency [ " amount_due " ] + = line [ amount_field ]
line [ " balance " ] = line_currency [ " amount_due " ]
line [ " date " ] = format_date (
line [ " date " ] , date_formats . get ( partner_id , default_fmt )
)
line [ ' date_maturity ' ] = format_date (
line [ ' date_maturity ' ] ,
date_formats . get ( partner_id , default_fmt )
line [ " date_maturity " ] = format_date (
line [ " date_maturity " ] , date_formats . get ( partner_id , default_fmt )
)
line_currency [ ' lines ' ] . append ( line )
line_currency [ " lines " ] . append ( line )
if data [ ' show_aging_buckets ' ] :
if data [ " show_aging_buckets " ] :
for line in buckets [ partner_id ] :
if line [ ' currency_id ' ] not in currency_dict :
currency_dict [ line [ ' currency_id ' ] ] , currencies = (
self . _get_line_currency_defaults (
line [ ' currency_id ' ] , currencies , 0.0 )
if line [ " currency_id " ] not in currency_dict :
(
currency_dict [ line [ " currency_id " ] ] ,
currencies ,
) = self . _get_line_currency_defaults (
line [ " currency_id " ] , currencies , 0.0
)
line_currency = currency_dict [ line [ ' currency_id ' ] ]
line_currency [ ' buckets ' ] = line
line_currency = currency_dict [ line [ " currency_id " ] ]
line_currency [ " buckets " ] = line
if len ( partner_ids ) > 1 :
values = currency_dict . values ( )
if not any (
[ v [ ' lines ' ] or v [ ' balance_forward ' ] for v in values ]
) :
if not any ( [ v [ " lines " ] or v [ " balance_forward " ] for v in values ] ) :
if data [ " filter_non_due_partners " ] :
partners_to_remove . add ( partner_id )
continue
else :
res [ partner_id ] [ ' no_entries ' ] = True
res [ partner_id ] [ " no_entries " ] = True
if data [ " filter_negative_balances " ] :
if not all ( [ v [ ' amount_due ' ] > = 0.0 for v in values ] ) :
if not all ( [ v [ " amount_due " ] > = 0.0 for v in values ] ) :
partners_to_remove . add ( partner_id )
for partner in partners_to_remove :
@ -416,13 +428,13 @@ class ReportStatementCommon(models.AbstractModel):
partner_ids . remove ( partner )
return {
' doc_ids ' : partner_ids ,
' doc_model ' : ' res.partner ' ,
' docs ' : self . env [ ' res.partner ' ] . browse ( partner_ids ) ,
' data ' : res ,
' company ' : self . env [ ' res.company ' ] . browse ( company_id ) ,
' Currencies ' : currencies ,
' account_type ' : account_type ,
' bucket_labels ' : bucket_labels ,
' get_inv_addr ' : self . _get_invoice_address ,
" doc_ids " : partner_ids ,
" doc_model " : " res.partner " ,
" docs " : self . env [ " res.partner " ] . browse ( partner_ids ) ,
" data " : res ,
" company " : self . env [ " res.company " ] . browse ( company_id ) ,
" Currencies " : currencies ,
" account_type " : account_type ,
" bucket_labels " : bucket_labels ,
" get_inv_addr " : self . _get_invoice_address ,
}