[#1379] Add Accounts Receivable Summary Report
This commit is contained in:
parent
e63432db63
commit
574e55788f
@ -50,14 +50,15 @@ class ReceivablePayableReport(object):
|
|||||||
from erpnext.accounts.utils import get_currency_precision
|
from erpnext.accounts.utils import get_currency_precision
|
||||||
currency_precision = get_currency_precision() or 2
|
currency_precision = get_currency_precision() or 2
|
||||||
data = []
|
data = []
|
||||||
dr_or_cr = args.get("dr_or_cr")
|
dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
|
||||||
|
voucher_details = self.get_voucher_details()
|
||||||
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
|
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
|
||||||
|
|
||||||
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
|
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
|
||||||
if self.is_receivable_or_payable(gle, args.get("dr_or_cr"), future_vouchers):
|
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
|
||||||
outstanding_amount = self.get_outstanding_amount(gle, self.filters.report_date, args.get("dr_or_cr"))
|
outstanding_amount = self.get_outstanding_amount(gle, self.filters.report_date, dr_or_cr)
|
||||||
if abs(outstanding_amount) > 0.1/10**currency_precision:
|
if abs(outstanding_amount) > 0.1/10**currency_precision:
|
||||||
due_date = self.get_due_date(args.get("party_type"), gle)
|
due_date = voucher_details.get("voucher_no", {}).get("due_date", "")
|
||||||
invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0
|
invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0
|
||||||
paid_amt = invoiced_amount - outstanding_amount
|
paid_amt = invoiced_amount - outstanding_amount
|
||||||
entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date
|
entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date
|
||||||
@ -69,7 +70,10 @@ class ReceivablePayableReport(object):
|
|||||||
row += [gle.voucher_type, gle.voucher_no, due_date]
|
row += [gle.voucher_type, gle.voucher_no, due_date]
|
||||||
|
|
||||||
if args.get("party_type") == "Supplier":
|
if args.get("party_type") == "Supplier":
|
||||||
row += self.get_supplier_bill_data(gle)
|
row += [
|
||||||
|
voucher_details.get("voucher_no", {}).get("bill_no", ""),
|
||||||
|
voucher_details.get("voucher_no", {}).get("bill_date", "")
|
||||||
|
]
|
||||||
|
|
||||||
row += [invoiced_amount, paid_amt, outstanding_amount] + \
|
row += [invoiced_amount, paid_amt, outstanding_amount] + \
|
||||||
get_ageing_data(cint(self.filters.range1), cint(self.filters.range2), \
|
get_ageing_data(cint(self.filters.range1), cint(self.filters.range2), \
|
||||||
@ -113,9 +117,9 @@ class ReceivablePayableReport(object):
|
|||||||
payment_amount = 0.0
|
payment_amount = 0.0
|
||||||
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
|
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
|
||||||
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
|
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
|
||||||
payment_amount += (flt(e.credit if dr_or_cr == "debit" else e.debit) - flt(e.get(dr_or_cr)))
|
payment_amount += (flt(e.credit if gle.party_type == "Customer" else e.debit) - flt(e.get(dr_or_cr)))
|
||||||
|
|
||||||
return flt(gle.get(dr_or_cr)) - flt(gle.credit if dr_or_cr == "debit" else gle.debit) - payment_amount
|
return flt(gle.get(dr_or_cr)) - flt(gle.credit if gle.party_type == "Customer" else gle.debit) - payment_amount
|
||||||
|
|
||||||
def get_party_name(self, party_type, party_name):
|
def get_party_name(self, party_type, party_name):
|
||||||
return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or ""
|
return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or ""
|
||||||
@ -138,35 +142,18 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
return self.party_map
|
return self.party_map
|
||||||
|
|
||||||
def get_due_date(self, party_type, gle):
|
def get_voucher_details(self):
|
||||||
self.get_voucher_details(gle)
|
voucher_details = frappe._dict()
|
||||||
if party_type == "Customer":
|
|
||||||
return self.voucher_detail_map.get(gle.voucher_no) if gle.voucher_type == "Sales Invoice" else ""
|
|
||||||
elif party_type == "Supplier":
|
|
||||||
return self.voucher_detail_map.get(gle.voucher_no).get("due_date") \
|
|
||||||
if gle.voucher_type in ["Purchase Invoice", "Journal Voucher"] else ""
|
|
||||||
|
|
||||||
def get_supplier_bill_data(self, gle):
|
for si in frappe.db.sql("""select name, due_date
|
||||||
self.get_voucher_details(gle)
|
from `tabSales Invoice` where docstatus=1""", as_dict=1):
|
||||||
return [self.voucher_detail_map.get(gle.voucher_no).get("bill_no"), \
|
voucher_details.setdefault(si.name, si)
|
||||||
self.voucher_detail_map.get(gle.voucher_no).get("bill_date")] \
|
|
||||||
if gle.voucher_type in ["Purchase Invoice", "Journal Voucher"] else ""
|
|
||||||
|
|
||||||
def get_voucher_details(self, gle):
|
for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date
|
||||||
# TODO can be restricted to posting date
|
from `tabPurchase Invoice` where docstatus=1""", as_dict=1):
|
||||||
if not hasattr(self, "voucher_detail_map"):
|
voucher_details.setdefault(pi.name, pi)
|
||||||
self.voucher_detail_map = dict(frappe.db.sql("""select name, due_date
|
|
||||||
from `tabSales Invoice` where docstatus=1"""))
|
|
||||||
|
|
||||||
voucher_details = {}
|
return voucher_details
|
||||||
get_voucher_details = frappe.db.sql("""select name, due_date, bill_no, bill_date
|
|
||||||
from `tabPurchase Invoice` where docstatus=1""")
|
|
||||||
|
|
||||||
for voucher_name, due_date, bill_no, bill_date in get_voucher_details:
|
|
||||||
voucher_details.setdefault(voucher_name, {}).update({"due_date": due_date,
|
|
||||||
"bill_no": bill_no, "bill_date": bill_date})
|
|
||||||
|
|
||||||
self.voucher_detail_map.update(voucher_details)
|
|
||||||
|
|
||||||
def get_gl_entries(self, party_type):
|
def get_gl_entries(self, party_type):
|
||||||
if not hasattr(self, "gl_entries"):
|
if not hasattr(self, "gl_entries"):
|
||||||
@ -212,7 +199,7 @@ class ReceivablePayableReport(object):
|
|||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
args = {
|
args = {
|
||||||
"party_type": "Customer",
|
"party_type": "Customer",
|
||||||
"dr_or_cr": "debit",
|
# "dr_or_cr": "debit",
|
||||||
"naming_by": ["Selling Settings", "cust_master_name"],
|
"naming_by": ["Selling Settings", "cust_master_name"],
|
||||||
}
|
}
|
||||||
return ReceivablePayableReport(filters).run(args)
|
return ReceivablePayableReport(filters).run(args)
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||||
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
frappe.query_reports["Accounts Receivable Summary"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname":"company",
|
||||||
|
"label": __("Company"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"default": frappe.defaults.get_user_default("company")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"customer",
|
||||||
|
"label": __("Customer"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"report_date",
|
||||||
|
"label": __("Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": get_today()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"ageing_based_on",
|
||||||
|
"label": __("Ageing Based On"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": 'Posting Date' + NEWLINE + 'Due Date',
|
||||||
|
"default": "Posting Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldtype": "Break",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"range1",
|
||||||
|
"label": __("Ageing Range 1"),
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"default": "30",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"range2",
|
||||||
|
"label": __("Ageing Range 2"),
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"default": "60",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"range3",
|
||||||
|
"label": __("Ageing Range 3"),
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"default": "90",
|
||||||
|
"reqd": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 1,
|
||||||
|
"apply_user_permissions": 1,
|
||||||
|
"creation": "2014-10-17 15:45:00.694265",
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"modified": "2014-10-17 15:45:00.694265",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Accounts Receivable Summary",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"ref_doctype": "Sales Invoice",
|
||||||
|
"report_name": "Accounts Receivable Summary",
|
||||||
|
"report_type": "Script Report"
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
|
||||||
|
|
||||||
|
class AccountsReceivableSummary(ReceivablePayableReport):
|
||||||
|
def run(self, args):
|
||||||
|
party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
||||||
|
return self.get_columns(party_naming_by), self.get_data(party_naming_by, args)
|
||||||
|
|
||||||
|
def get_columns(self, party_naming_by):
|
||||||
|
columns = [_("Customer") + ":Link/Customer:200"]
|
||||||
|
|
||||||
|
if party_naming_by == "Naming Series":
|
||||||
|
columns += ["Customer Name::110"]
|
||||||
|
|
||||||
|
columns += [_("Total Invoiced Amount") + ":Currency:100",
|
||||||
|
_("Total Paid Amount") + ":Currency:100", _("Total Outstanding Amount") + ":Currency:100",
|
||||||
|
"0-" + self.filters.range1 + ":Currency:100",
|
||||||
|
self.filters.range1 + "-" + self.filters.range2 + ":Currency:100",
|
||||||
|
self.filters.range2 + "-" + self.filters.range3 + ":Currency:100",
|
||||||
|
self.filters.range3 + _("-Above") + ":Currency:100",
|
||||||
|
_("Territory") + ":Link/Territory:80"
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
|
|
||||||
|
def get_data(self, party_naming_by, args):
|
||||||
|
data = []
|
||||||
|
prev_columns, prev_data = ReceivablePayableReport(self.filters).run(args)
|
||||||
|
total_amount_dict = frappe._dict()
|
||||||
|
|
||||||
|
key_list = ["posting_date", "customer"]
|
||||||
|
|
||||||
|
if party_naming_by == "Naming Series":
|
||||||
|
key_list += ["customer_name"]
|
||||||
|
|
||||||
|
key_list += ["voucher_type", "voucher_no", "due_date", "invoiced_amt", "paid_amt",
|
||||||
|
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "territory", "remarks"]
|
||||||
|
|
||||||
|
data_dict = self.make_data_dict(key_list, prev_data)
|
||||||
|
|
||||||
|
for d in data_dict:
|
||||||
|
if d["customer"] in total_amount_dict:
|
||||||
|
customer_key = total_amount_dict[d.customer]
|
||||||
|
customer_key["total_invoiced_amt"] += d.get("invoiced_amt")
|
||||||
|
customer_key["total_paid_amt"] += d.get("paid_amt")
|
||||||
|
customer_key["total_outstanding_amt"]+= d.get("outstanding_amt")
|
||||||
|
customer_key["total_range1"] += d.get("range1")
|
||||||
|
customer_key["total_range2"] += d.get("range2")
|
||||||
|
customer_key["total_range3"] += d.get("range3")
|
||||||
|
customer_key["total_range4"] += d.get("range4")
|
||||||
|
else:
|
||||||
|
total_amount_dict.setdefault(d.get("customer"), {}).update({
|
||||||
|
"total_invoiced_amt": d.get("invoiced_amt"),
|
||||||
|
"total_paid_amt": d.get("paid_amt"),
|
||||||
|
"total_outstanding_amt": d.get("outstanding_amt"),
|
||||||
|
"total_range1": d.get("range1"),
|
||||||
|
"total_range2": d.get("range2"),
|
||||||
|
"total_range3": d.get("range3"),
|
||||||
|
"total_range4": d.get("range4")
|
||||||
|
})
|
||||||
|
|
||||||
|
for i in total_amount_dict:
|
||||||
|
row = [i]
|
||||||
|
|
||||||
|
if party_naming_by == "Naming Series":
|
||||||
|
row += [self.get_party_name("Customer", i)]
|
||||||
|
|
||||||
|
row += [total_amount_dict[i]["total_invoiced_amt"], total_amount_dict[i]["total_paid_amt"],
|
||||||
|
total_amount_dict[i]["total_outstanding_amt"], total_amount_dict[i]["total_range1"],
|
||||||
|
total_amount_dict[i]["total_range2"], total_amount_dict[i]["total_range3"],
|
||||||
|
total_amount_dict[i]["total_range4"], self.get_territory(i)]
|
||||||
|
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def make_data_dict(self, key_list, data):
|
||||||
|
make_data_dict = []
|
||||||
|
for d in data:
|
||||||
|
make_data_dict.append(frappe._dict(zip(key_list, d)))
|
||||||
|
|
||||||
|
return make_data_dict
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
args = {
|
||||||
|
"party_type": "Customer",
|
||||||
|
"dr_or_cr": "debit",
|
||||||
|
"naming_by": ["Selling Settings", "cust_master_name"],
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccountsReceivableSummary(filters).run(args)
|
||||||
Loading…
x
Reference in New Issue
Block a user