@ -41,6 +41,34 @@ def execute(filters=None):
columns = get_columns ( group_wise_columns , filters )
if filters . group_by == ' Invoice ' :
get_data_when_grouped_by_invoice ( columns , gross_profit_data , filters , group_wise_columns , data )
else :
get_data_when_not_grouped_by_invoice ( gross_profit_data , filters , group_wise_columns , data )
return columns , data
def get_data_when_grouped_by_invoice ( columns , gross_profit_data , filters , group_wise_columns , data ) :
column_names = get_column_names ( )
# to display item as Item Code: Item Name
columns [ 0 ] = ' Sales Invoice:Link/Item:300 '
# removing Item Code and Item Name columns
del columns [ 4 : 6 ]
for src in gross_profit_data . si_list :
row = frappe . _dict ( )
row . indent = src . indent
row . parent_invoice = src . parent_invoice
row . currency = filters . currency
for col in group_wise_columns . get ( scrub ( filters . group_by ) ) :
row [ column_names [ col ] ] = src . get ( col )
data . append ( row )
def get_data_when_not_grouped_by_invoice ( gross_profit_data , filters , group_wise_columns , data ) :
for idx , src in enumerate ( gross_profit_data . grouped_data ) :
row = [ ]
for col in group_wise_columns . get ( scrub ( filters . group_by ) ) :
@ -51,8 +79,6 @@ def execute(filters=None):
row [ 0 ] = frappe . bold ( " Total " )
data . append ( row )
return columns , data
def get_columns ( group_wise_columns , filters ) :
columns = [ ]
column_map = frappe . _dict ( {
@ -93,12 +119,38 @@ def get_columns(group_wise_columns, filters):
return columns
def get_column_names ( ) :
return frappe . _dict ( {
' parent ' : ' sales_invoice ' ,
' customer ' : ' customer ' ,
' customer_group ' : ' customer_group ' ,
' posting_date ' : ' posting_date ' ,
' item_code ' : ' item_code ' ,
' item_name ' : ' item_name ' ,
' item_group ' : ' item_group ' ,
' brand ' : ' brand ' ,
' description ' : ' description ' ,
' warehouse ' : ' warehouse ' ,
' qty ' : ' qty ' ,
' base_rate ' : ' avg._selling_rate ' ,
' buying_rate ' : ' valuation_rate ' ,
' base_amount ' : ' selling_amount ' ,
' buying_amount ' : ' buying_amount ' ,
' gross_profit ' : ' gross_profit ' ,
' gross_profit_percent ' : ' gross_profit_ % ' ,
' project ' : ' project '
} )
class GrossProfitGenerator ( object ) :
def __init__ ( self , filters = None ) :
self . data = [ ]
self . average_buying_rate = { }
self . filters = frappe . _dict ( filters )
self . load_invoice_items ( )
if filters . group_by == ' Invoice ' :
self . group_items_by_invoice ( )
self . load_stock_ledger_entries ( )
self . load_product_bundle ( )
self . load_non_stock_items ( )
@ -112,7 +164,12 @@ class GrossProfitGenerator(object):
self . currency_precision = cint ( frappe . db . get_default ( " currency_precision " ) ) or 3
self . float_precision = cint ( frappe . db . get_default ( " float_precision " ) ) or 2
for row in self . si_list :
grouped_by_invoice = True if self . filters . get ( " group_by " ) == " Invoice " else False
if grouped_by_invoice :
buying_amount = 0
for row in reversed ( self . si_list ) :
if self . skip_row ( row , self . product_bundles ) :
continue
@ -134,12 +191,20 @@ class GrossProfitGenerator(object):
row . buying_amount = flt ( self . get_buying_amount ( row , row . item_code ) ,
self . currency_precision )
if grouped_by_invoice :
if row . indent == 1.0 :
buying_amount + = row . buying_amount
elif row . indent == 0.0 :
row . buying_amount = buying_amount
buying_amount = 0
# get buying rate
if row . qty :
row . buying_rate = flt ( row . buying_amount / row . qty , self . float_precision )
row . base_rate = flt ( row . base_amount / row . qty , self . float_precision )
if flt ( row . qty ) :
row . buying_rate = flt ( row . buying_amount / flt ( row . qty ) , self . float_precision )
row . base_rate = flt ( row . base_amount / flt ( row . qty ) , self . float_precision )
else :
row . buying_rate , row . base_rate = 0.0 , 0.0
if self . is_not_invoice_row ( row ) :
row . buying_rate , row . base_rate = 0.0 , 0.0
# calculate gross profit
row . gross_profit = flt ( row . base_amount - row . buying_amount , self . currency_precision )
@ -171,7 +236,7 @@ class GrossProfitGenerator(object):
if i == 0 :
new_row = row
else :
new_row . qty + = row . qty
new_row . qty + = flt ( row . qty )
new_row . buying_amount + = flt ( row . buying_amount , self . currency_precision )
new_row . base_amount + = flt ( row . base_amount , self . currency_precision )
new_row = self . set_average_rate ( new_row )
@ -183,16 +248,19 @@ class GrossProfitGenerator(object):
and row . item_code in self . returned_invoices [ row . parent ] :
returned_item_rows = self . returned_invoices [ row . parent ] [ row . item_code ]
for returned_item_row in returned_item_rows :
row . qty + = returned_item_row . qty
row . qty + = flt ( returned_item_row . qty )
row . base_amount + = flt ( returned_item_row . base_amount , self . currency_precision )
row . buying_amount = flt ( row . qty * row . buying_rate , self . currency_precision )
if row . qty or row . base_amount :
row . buying_amount = flt ( flt ( row . qty ) * flt ( row . buying_rate ) , self . currency_precision )
if ( flt ( row . qty ) or row . base_amount ) and self . is_not_invoice_row ( row ) :
row = self . set_average_rate ( row )
self . grouped_data . append ( row )
self . add_to_totals ( row )
self . set_average_gross_profit ( self . totals )
self . grouped_data . append ( self . totals )
def is_not_invoice_row ( self , row ) :
return ( self . filters . get ( " group_by " ) == " Invoice " and row . indent != 0.0 ) or self . filters . get ( " group_by " ) != " Invoice "
def set_average_rate ( self , new_row ) :
self . set_average_gross_profit ( new_row )
new_row . buying_rate = flt ( new_row . buying_amount / new_row . qty , self . float_precision ) if new_row . qty else 0
@ -354,6 +422,109 @@ class GrossProfitGenerator(object):
. format ( conditions = conditions , sales_person_cols = sales_person_cols ,
sales_team_table = sales_team_table , match_cond = get_match_cond ( ' Sales Invoice ' ) ) , self . filters , as_dict = 1 )
def group_items_by_invoice ( self ) :
"""
Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children .
"""
parents = [ ]
for row in self . si_list :
if row . parent not in parents :
parents . append ( row . parent )
parents_index = 0
for index , row in enumerate ( self . si_list ) :
if parents_index < len ( parents ) and row . parent == parents [ parents_index ] :
invoice = self . get_invoice_row ( row )
self . si_list . insert ( index , invoice )
parents_index + = 1
else :
# skipping the bundle items rows
if not row . indent :
row . indent = 1.0
row . parent_invoice = row . parent
row . parent = row . item_code
if frappe . db . exists ( ' Product Bundle ' , row . item_code ) :
self . add_bundle_items ( row , index )
def get_invoice_row ( self , row ) :
return frappe . _dict ( {
' parent_invoice ' : " " ,
' indent ' : 0.0 ,
' parent ' : row . parent ,
' posting_date ' : row . posting_date ,
' posting_time ' : row . posting_time ,
' project ' : row . project ,
' update_stock ' : row . update_stock ,
' customer ' : row . customer ,
' customer_group ' : row . customer_group ,
' item_code ' : None ,
' item_name ' : None ,
' description ' : None ,
' warehouse ' : None ,
' item_group ' : None ,
' brand ' : None ,
' dn_detail ' : None ,
' delivery_note ' : None ,
' qty ' : None ,
' item_row ' : None ,
' is_return ' : row . is_return ,
' cost_center ' : row . cost_center ,
' base_net_amount ' : frappe . db . get_value ( ' Sales Invoice ' , row . parent , ' base_net_total ' )
} )
def add_bundle_items ( self , product_bundle , index ) :
bundle_items = self . get_bundle_items ( product_bundle )
for i , item in enumerate ( bundle_items ) :
bundle_item = self . get_bundle_item_row ( product_bundle , item )
self . si_list . insert ( ( index + i + 1 ) , bundle_item )
def get_bundle_items ( self , product_bundle ) :
return frappe . get_all (
' Product Bundle Item ' ,
filters = {
' parent ' : product_bundle . item_code
} ,
fields = [ ' item_code ' , ' qty ' ]
)
def get_bundle_item_row ( self , product_bundle , item ) :
item_name , description , item_group , brand = self . get_bundle_item_details ( item . item_code )
return frappe . _dict ( {
' parent_invoice ' : product_bundle . item_code ,
' indent ' : product_bundle . indent + 1 ,
' parent ' : item . item_code ,
' posting_date ' : product_bundle . posting_date ,
' posting_time ' : product_bundle . posting_time ,
' project ' : product_bundle . project ,
' customer ' : product_bundle . customer ,
' customer_group ' : product_bundle . customer_group ,
' item_code ' : item . item_code ,
' item_name ' : item_name ,
' description ' : description ,
' warehouse ' : product_bundle . warehouse ,
' item_group ' : item_group ,
' brand ' : brand ,
' dn_detail ' : product_bundle . dn_detail ,
' delivery_note ' : product_bundle . delivery_note ,
' qty ' : ( flt ( product_bundle . qty ) * flt ( item . qty ) ) ,
' item_row ' : None ,
' is_return ' : product_bundle . is_return ,
' cost_center ' : product_bundle . cost_center
} )
def get_bundle_item_details ( self , item_code ) :
return frappe . db . get_value (
' Item ' ,
item_code ,
[ ' item_name ' , ' description ' , ' item_group ' , ' brand ' ]
)
def load_stock_ledger_entries ( self ) :
res = frappe . db . sql ( """ select item_code, voucher_type, voucher_no,
voucher_detail_no , stock_value , warehouse , actual_qty as qty