feat: Group by AR/AP report (#20573)
* feat: Group by AR/AP report * fix: Do not consider total row in charts * fix: Subtotal row for last party
This commit is contained in:
parent
ecba5c40bb
commit
0ce9e0cc1f
@ -100,6 +100,11 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier Group"
|
"options": "Supplier Group"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "group_by_party",
|
||||||
|
"label": __("Group By Supplier"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"based_on_payment_terms",
|
"fieldname":"based_on_payment_terms",
|
||||||
"label": __("Based On Payment Terms"),
|
"label": __("Based On Payment Terms"),
|
||||||
@ -112,6 +117,16 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
"hidden": 1
|
"hidden": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
if (data && data.bold) {
|
||||||
|
value = value.bold();
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
onload: function(report) {
|
onload: function(report) {
|
||||||
report.page.add_inner_button(__("Accounts Payable Summary"), function() {
|
report.page.add_inner_button(__("Accounts Payable Summary"), function() {
|
||||||
var filters = report.get_values();
|
var filters = report.get_values();
|
||||||
|
@ -131,6 +131,11 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Sales Person"
|
"options": "Sales Person"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "group_by_party",
|
||||||
|
"label": __("Group By Customer"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"based_on_payment_terms",
|
"fieldname":"based_on_payment_terms",
|
||||||
"label": __("Based On Payment Terms"),
|
"label": __("Based On Payment Terms"),
|
||||||
@ -177,6 +182,15 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
if (data && data.bold) {
|
||||||
|
value = value.bold();
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
onload: function(report) {
|
onload: function(report) {
|
||||||
report.page.add_inner_button(__("Accounts Receivable Summary"), function() {
|
report.page.add_inner_button(__("Accounts Receivable Summary"), function() {
|
||||||
var filters = report.get_values();
|
var filters = report.get_values();
|
||||||
|
@ -46,7 +46,7 @@ class ReceivablePayableReport(object):
|
|||||||
self.get_columns()
|
self.get_columns()
|
||||||
self.get_data()
|
self.get_data()
|
||||||
self.get_chart_data()
|
self.get_chart_data()
|
||||||
return self.columns, self.data, None, self.chart
|
return self.columns, self.data, None, self.chart, None, self.skip_total_row
|
||||||
|
|
||||||
def set_defaults(self):
|
def set_defaults(self):
|
||||||
if not self.filters.get("company"):
|
if not self.filters.get("company"):
|
||||||
@ -57,6 +57,12 @@ class ReceivablePayableReport(object):
|
|||||||
self.party_type = self.filters.party_type
|
self.party_type = self.filters.party_type
|
||||||
self.party_details = {}
|
self.party_details = {}
|
||||||
self.invoices = set()
|
self.invoices = set()
|
||||||
|
self.skip_total_row = 0
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.previous_party=''
|
||||||
|
self.total_row_map = {}
|
||||||
|
self.skip_total_row = 1
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
self.get_gl_entries()
|
self.get_gl_entries()
|
||||||
@ -102,6 +108,12 @@ class ReceivablePayableReport(object):
|
|||||||
)
|
)
|
||||||
self.get_invoices(gle)
|
self.get_invoices(gle)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.init_subtotal_row(gle.party)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.init_subtotal_row('Total')
|
||||||
|
|
||||||
def get_invoices(self, gle):
|
def get_invoices(self, gle):
|
||||||
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'):
|
||||||
if self.filters.get("sales_person"):
|
if self.filters.get("sales_person"):
|
||||||
@ -111,6 +123,20 @@ class ReceivablePayableReport(object):
|
|||||||
else:
|
else:
|
||||||
self.invoices.add(gle.voucher_no)
|
self.invoices.add(gle.voucher_no)
|
||||||
|
|
||||||
|
def init_subtotal_row(self, party):
|
||||||
|
if not self.total_row_map.get(party):
|
||||||
|
self.total_row_map.setdefault(party, {
|
||||||
|
'party': party,
|
||||||
|
'bold': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
for field in self.get_currency_fields():
|
||||||
|
self.total_row_map[party][field] = 0.0
|
||||||
|
|
||||||
|
def get_currency_fields(self):
|
||||||
|
return ['invoiced', 'paid', 'credit_note', 'outstanding', 'range1',
|
||||||
|
'range2', 'range3', 'range4', 'range5']
|
||||||
|
|
||||||
def update_voucher_balance(self, gle):
|
def update_voucher_balance(self, gle):
|
||||||
# get the row where this balance needs to be updated
|
# get the row where this balance needs to be updated
|
||||||
# if its a payment, it will return the linked invoice or will be considered as advance
|
# if its a payment, it will return the linked invoice or will be considered as advance
|
||||||
@ -135,6 +161,18 @@ class ReceivablePayableReport(object):
|
|||||||
# advance / unlinked payment or other adjustment
|
# advance / unlinked payment or other adjustment
|
||||||
row.paid -= gle_balance
|
row.paid -= gle_balance
|
||||||
|
|
||||||
|
def update_sub_total_row(self, row, party):
|
||||||
|
total_row = self.total_row_map.get(party)
|
||||||
|
|
||||||
|
for field in self.get_currency_fields():
|
||||||
|
total_row[field] += row.get(field, 0.0)
|
||||||
|
|
||||||
|
def append_subtotal_row(self, party):
|
||||||
|
sub_total_row = self.total_row_map.get(party)
|
||||||
|
self.data.append(sub_total_row)
|
||||||
|
self.data.append({})
|
||||||
|
self.update_sub_total_row(sub_total_row, 'Total')
|
||||||
|
|
||||||
def get_voucher_balance(self, gle):
|
def get_voucher_balance(self, gle):
|
||||||
if self.filters.get("sales_person"):
|
if self.filters.get("sales_person"):
|
||||||
against_voucher = gle.against_voucher or gle.voucher_no
|
against_voucher = gle.against_voucher or gle.voucher_no
|
||||||
@ -192,11 +230,22 @@ class ReceivablePayableReport(object):
|
|||||||
else:
|
else:
|
||||||
self.append_row(row)
|
self.append_row(row)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.append_subtotal_row(self.previous_party)
|
||||||
|
self.data.append(self.total_row_map.get('Total'))
|
||||||
|
|
||||||
def append_row(self, row):
|
def append_row(self, row):
|
||||||
self.allocate_future_payments(row)
|
self.allocate_future_payments(row)
|
||||||
self.set_invoice_details(row)
|
self.set_invoice_details(row)
|
||||||
self.set_party_details(row)
|
self.set_party_details(row)
|
||||||
self.set_ageing(row)
|
self.set_ageing(row)
|
||||||
|
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
self.update_sub_total_row(row, row.party)
|
||||||
|
if self.previous_party and (self.previous_party != row.party):
|
||||||
|
self.append_subtotal_row(self.previous_party)
|
||||||
|
self.previous_party = row.party
|
||||||
|
|
||||||
self.data.append(row)
|
self.data.append(row)
|
||||||
|
|
||||||
def set_invoice_details(self, row):
|
def set_invoice_details(self, row):
|
||||||
@ -503,6 +552,7 @@ class ReceivablePayableReport(object):
|
|||||||
# get all the GL entries filtered by the given filters
|
# get all the GL entries filtered by the given filters
|
||||||
|
|
||||||
conditions, values = self.prepare_conditions()
|
conditions, values = self.prepare_conditions()
|
||||||
|
order_by = self.get_order_by_condition()
|
||||||
|
|
||||||
if self.filters.get(scrub(self.party_type)):
|
if self.filters.get(scrub(self.party_type)):
|
||||||
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
|
||||||
@ -520,9 +570,8 @@ class ReceivablePayableReport(object):
|
|||||||
and party_type=%s
|
and party_type=%s
|
||||||
and (party is not null and party != '')
|
and (party is not null and party != '')
|
||||||
and posting_date <= %s
|
and posting_date <= %s
|
||||||
{1}
|
{1} {2}"""
|
||||||
order by posting_date, party"""
|
.format(select_fields, conditions, order_by), values, as_dict=True)
|
||||||
.format(select_fields, conditions), values, as_dict=True)
|
|
||||||
|
|
||||||
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
def get_sales_invoices_or_customers_based_on_sales_person(self):
|
||||||
if self.filters.get("sales_person"):
|
if self.filters.get("sales_person"):
|
||||||
@ -557,6 +606,12 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
|
def get_order_by_condition(self):
|
||||||
|
if self.filters.get('group_by_party'):
|
||||||
|
return "order by party, posting_date"
|
||||||
|
else:
|
||||||
|
return "order by posting_date, party"
|
||||||
|
|
||||||
def add_common_filters(self, conditions, values, party_type_field):
|
def add_common_filters(self, conditions, values, party_type_field):
|
||||||
if self.filters.company:
|
if self.filters.company:
|
||||||
conditions.append("company=%s")
|
conditions.append("company=%s")
|
||||||
@ -736,11 +791,13 @@ class ReceivablePayableReport(object):
|
|||||||
def get_chart_data(self):
|
def get_chart_data(self):
|
||||||
rows = []
|
rows = []
|
||||||
for row in self.data:
|
for row in self.data:
|
||||||
values = [row.range1, row.range2, row.range3, row.range4, row.range5]
|
row = frappe._dict(row)
|
||||||
precision = cint(frappe.db.get_default("float_precision")) or 2
|
if not cint(row.bold):
|
||||||
rows.append({
|
values = [row.range1, row.range2, row.range3, row.range4, row.range5]
|
||||||
'values': [flt(val, precision) for val in values]
|
precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||||
})
|
rows.append({
|
||||||
|
'values': [flt(val, precision) for val in values]
|
||||||
|
})
|
||||||
|
|
||||||
self.chart = {
|
self.chart = {
|
||||||
"data": {
|
"data": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user