refactor: Accounts Receivable / Payable report (#18906)

This commit is contained in:
Nabin Hait 2019-09-03 16:07:46 +05:30 committed by GitHub
parent cb44f3a7c3
commit 1ab75db0ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 748 additions and 905 deletions

View File

@ -40,7 +40,7 @@
</div>
</div>
{% if(filters.show_pdc_in_print) { %}
{% if(filters.show_future_payments) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
var range2 = report.columns[12].label;
@ -122,22 +122,22 @@
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_pdc_in_print) { %}
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
{% } %}
@ -162,18 +162,18 @@
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
<td style="text-align: right">{%= data[i][__("Age (Days)")] %}</td>
<td>
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
{%= data[i]["voucher_type"] %}
<br>
{% } %}
{%= data[i]["voucher_no"] %}
</td>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td>{%= data[i]["sales_person"] %}</td>
{% } %}
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
@ -195,7 +195,7 @@
<td style="text-align: right">
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %}</td>
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
@ -204,7 +204,7 @@
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
@ -215,10 +215,10 @@
{% } %}
{% } else { %}
<td></td>
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
<td></td>
{% } %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td></td>
{% } %}
<td></td>
@ -226,7 +226,7 @@
<td style="text-align: right">
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %}</td>
{% if(!filters.show_pdc_in_print) { %}
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
@ -234,7 +234,7 @@
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i][__("Customer LPO")] %}</td>

View File

@ -130,13 +130,18 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Check",
},
{
"fieldname":"show_pdc_in_print",
"label": __("Show PDC in Print"),
"fieldname":"show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
},
{
"fieldname":"show_sales_person_in_print",
"label": __("Show Sales Person in Print"),
"fieldname":"show_delivery_notes",
"label": __("Show Delivery Notes"),
"fieldtype": "Check",
},
{
"fieldname":"show_sales_person",
"label": __("Show Sales Person"),
"fieldtype": "Check",
},
{

View File

@ -14,33 +14,44 @@ class TestAccountsReceivable(unittest.TestCase):
filters = {
'company': '_Test Company 2',
'based_on_payment_terms': 1
'based_on_payment_terms': 1,
'report_date': today(),
'range1': 30,
'range2': 60,
'range3': 90,
'range4': 120
}
# check invoice grand total and invoiced column's value for 3 payment terms
name = make_sales_invoice()
report = execute(filters)
expected_data = [[100,30], [100,50], [100,20]]
expected_data = [[100, 30], [100, 50], [100, 20]]
self.assertEqual(expected_data[0], report[1][0][7:9])
self.assertEqual(expected_data[1], report[1][1][7:9])
self.assertEqual(expected_data[2], report[1][2][7:9])
for i in range(3):
row = report[1][i-1]
self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced])
# check invoice grand total, invoiced, paid and outstanding column's value after payment
make_payment(name)
report = execute(filters)
expected_data_after_payment = [[100,50], [100,20]]
expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]]
self.assertEqual(expected_data_after_payment[0], report[1][0][7:9])
self.assertEqual(expected_data_after_payment[1], report[1][1][7:9])
for i in range(2):
row = report[1][i-1]
self.assertEqual(expected_data_after_payment[i-1],
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding])
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
make_credit_note(name)
report = execute(filters)
expected_data_after_credit_note = [[100,100,30,100,-30]]
self.assertEqual(expected_data_after_credit_note[0], report[1][0][7:12])
expected_data_after_credit_note = [100, 0, 0, 40, -40]
row = report[1][0]
self.assertEqual(expected_data_after_credit_note,
[row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding])
def make_sales_invoice():
frappe.set_user("Administrator")
@ -64,7 +75,7 @@ def make_sales_invoice():
return si.name
def make_payment(docname):
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40)
pe.paid_from = "Debtors - _TC2"
pe.insert()
pe.submit()

View File

@ -3,236 +3,11 @@
from __future__ import unicode_literals
import frappe
from frappe import _, scrub
from frappe.utils import flt
from frappe import _
from frappe.utils import flt, cint
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
from six import iteritems
from six.moves import zip
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, args), self.get_data(party_naming_by, args)
def get_columns(self, party_naming_by, args):
columns = [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
if party_naming_by == "Naming Series":
columns += [ args.get("party_type") + " Name::140"]
credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
columns += [{
"label": _("Advance Amount"),
"fieldname": "advance_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 100
},{
"label": _("Total Invoiced Amt"),
"fieldname": "total_invoiced_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 100
},
{
"label": _("Total Paid Amt"),
"fieldname": "total_paid_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 100
}]
columns += [
{
"label": _(credit_debit_label),
"fieldname": scrub(credit_debit_label),
"fieldtype": "Currency",
"options": "currency",
"width": 140
},
{
"label": _("Total Outstanding Amt"),
"fieldname": "total_outstanding_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _("0-" + str(self.filters.range1)),
"fieldname": scrub("0-" + str(self.filters.range1)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range1) + "-" + str(self.filters.range2)),
"fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range2) + "-" + str(self.filters.range3)),
"fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
"fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range4) + _("-Above")),
"fieldname": scrub(str(self.filters.range4) + _("-Above")),
"fieldtype": "Currency",
"options": "currency",
"width": 160
}
]
if args.get("party_type") == "Customer":
columns += [{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80
},
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"width": 80
},
{
"label": _("Sales Person"),
"fieldtype": "Data",
"fieldname": "sales_person",
"width": 120,
}]
if args.get("party_type") == "Supplier":
columns += [{
"label": _("Supplier Group"),
"fieldname": "supplier_group",
"fieldtype": "Link",
"options": "Supplier Group",
"width": 80
}]
columns.append({
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"width": 80
})
return columns
def get_data(self, party_naming_by, args):
data = []
partywise_total = self.get_partywise_total(party_naming_by, args)
partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"),
self.filters.get("report_date")) or {}
for party, party_dict in iteritems(partywise_total):
row = [party]
if party_naming_by == "Naming Series":
row += [self.get_party_name(args.get("party_type"), party)]
row += [partywise_advance_amount.get(party, 0)]
paid_amt = 0
if party_dict.paid_amt > 0:
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
row += [
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
]
if args.get("party_type") == "Customer":
row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))]
if args.get("party_type") == "Supplier":
row += [self.get_supplier_group(party)]
row.append(party_dict.currency)
data.append(row)
return data
def get_partywise_total(self, party_naming_by, args):
party_total = frappe._dict()
for d in self.get_voucherwise_data(party_naming_by, args):
party_total.setdefault(d.party,
frappe._dict({
"invoiced_amt": 0,
"paid_amt": 0,
"credit_amt": 0,
"outstanding_amt": 0,
"range1": 0,
"range2": 0,
"range3": 0,
"range4": 0,
"range5": 0,
"sales_person": []
})
)
for k in list(party_total[d.party]):
if k not in ["currency", "sales_person"]:
party_total[d.party][k] += flt(d.get(k, 0))
party_total[d.party].currency = d.currency
if d.sales_person:
party_total[d.party].sales_person.append(d.sales_person)
return party_total
def get_voucherwise_data(self, party_naming_by, args):
voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1]
cols = ["posting_date", "party"]
if party_naming_by == "Naming Series":
cols += ["party_name"]
if args.get("party_type") == 'Customer':
cols += ["contact"]
cols += ["voucher_type", "voucher_no", "due_date"]
if args.get("party_type") == "Supplier":
cols += ["bill_no", "bill_date"]
cols += ["invoiced_amt", "paid_amt", "credit_amt",
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
"pdc/lc_amount"]
if args.get("party_type") == "Supplier":
cols += ["supplier_group", "remarks"]
if args.get("party_type") == "Customer":
cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"]
return self.make_data_dict(cols, voucherwise_data)
def make_data_dict(self, cols, data):
data_dict = []
for d in data:
data_dict.append(frappe._dict(zip(cols, d)))
return data_dict
def execute(filters=None):
args = {
@ -241,3 +16,119 @@ def execute(filters=None):
}
return AccountsReceivableSummary(filters).run(args)
class AccountsReceivableSummary(ReceivablePayableReport):
def run(self, args):
self.party_type = args.get('party_type')
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
self.get_columns()
self.get_data(args)
return self.columns, self.data
def get_data(self, args):
self.data = []
self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
self.get_party_total(args)
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date) or {}
for party, party_dict in iteritems(self.party_total):
row = frappe._dict()
row.party = party
if self.party_naming_by == "Naming Series":
row.party_name = frappe.get_cached_value(self.party_type, party, [self.party_type + "_name"])
row.update(party_dict)
# Advance against party
row.advance = party_advance_amount.get(party, 0)
# In AR/AP, advance shown in paid columns,
# but in summary report advance shown in separate column
row.paid -= row.advance
self.data.append(row)
def get_party_total(self, args):
self.party_total = frappe._dict()
for d in self.receivables:
self.init_party_total(d)
# Add all amount columns
for k in list(self.party_total[d.party]):
if k not in ["currency", "sales_person"]:
self.party_total[d.party][k] += d.get(k, 0.0)
# set territory, customer_group, sales person etc
self.set_party_details(d)
def init_party_total(self, row):
self.party_total.setdefault(row.party, frappe._dict({
"invoiced": 0.0,
"paid": 0.0,
"credit_note": 0.0,
"outstanding": 0.0,
"range1": 0.0,
"range2": 0.0,
"range3": 0.0,
"range4": 0.0,
"range5": 0.0,
"sales_person": []
}))
def set_party_details(self, row):
self.party_total[row.party].currency = row.currency
for key in ('territory', 'customer_group', 'supplier_group'):
if row.get(key):
self.party_total[row.party][key] = row.get(key)
if row.sales_person:
self.party_total[row.party].sales_person.append(row.sales_person)
def get_columns(self):
self.columns = []
self.add_column(label=_(self.party_type), fieldname='party',
fieldtype='Link', options=self.party_type, width=180)
if self.party_naming_by == "Naming Series":
self.add_column(_('{0} Name').format(self.party_type),
fieldname = 'party_name', fieldtype='Data')
credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note"
self.add_column(_('Advance Amount'), fieldname='advance')
self.add_column(_('Invoiced Amount'), fieldname='invoiced')
self.add_column(_('Paid Amount'), fieldname='paid')
self.add_column(_(credit_debit_label), fieldname='credit_note')
self.add_column(_('Outstanding Amount'), fieldname='outstanding')
self.setup_ageing_columns()
if self.party_type == "Customer":
self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
options='Territory')
self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
options='Customer Group')
if self.filters.show_sales_person:
self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
else:
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
options='Supplier Group')
self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link',
options='Currency', width=80)
def setup_ageing_columns(self):
for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
self.add_column(label=label, fieldname='range' + str(i+1))

View File

@ -372,7 +372,7 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil
return frappe.db.sql("""
select `tabContact`.name from `tabContact`, `tabDynamic Link`
where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
and `tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.is_primary_contact = 1
and `tabDynamic Link`.link_doctype = 'Customer'
and `tabContact`.name like %(txt)s
""", {
'customer': customer,
@ -384,7 +384,7 @@ def get_customer_primary_address(doctype, txt, searchfield, start, page_len, fil
return frappe.db.sql("""
select `tabAddress`.name from `tabAddress`, `tabDynamic Link`
where `tabAddress`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
and `tabDynamic Link`.link_doctype = 'Customer' and `tabAddress`.is_primary_address = 1
and `tabDynamic Link`.link_doctype = 'Customer'
and `tabAddress`.name like %(txt)s
""", {
'customer': customer,