Merge pull request #30499 from ruthra-kumar/payment_terms_status_filter

feat: 'customer' column and more filter to Payment terms status report
This commit is contained in:
Deepesh Garg 2022-04-07 13:01:16 +05:30 committed by GitHub
commit f3e6a26d34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 281 additions and 30 deletions

View File

@ -27,28 +27,55 @@ function get_filters() {
"default": frappe.datetime.get_today()
},
{
"fieldname":"sales_order",
"label": __("Sales Order"),
"fieldtype": "MultiSelectList",
"fieldname":"customer_group",
"label": __("Customer Group"),
"fieldtype": "Link",
"width": 100,
"options": "Sales Order",
"get_data": function(txt) {
return frappe.db.get_link_options("Sales Order", txt, this.filters());
"options": "Customer Group",
},
"filters": () => {
return {
docstatus: 1,
payment_terms_template: ['not in', ['']],
company: frappe.query_report.get_filter_value("company"),
transaction_date: ['between', [frappe.query_report.get_filter_value("period_start_date"), frappe.query_report.get_filter_value("period_end_date")]]
{
"fieldname":"customer",
"label": __("Customer"),
"fieldtype": "Link",
"width": 100,
"options": "Customer",
"get_query": () => {
var customer_group = frappe.query_report.get_filter_value('customer_group');
return{
"query": "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items",
"filters": [
['Customer', 'disabled', '=', '0'],
['Customer Group','name', '=', customer_group]
]
}
}
},
on_change: function(){
frappe.query_report.refresh();
{
"fieldname":"item_group",
"label": __("Item Group"),
"fieldtype": "Link",
"width": 100,
"options": "Item Group",
},
{
"fieldname":"item",
"label": __("Item"),
"fieldtype": "Link",
"width": 100,
"options": "Item",
"get_query": () => {
var item_group = frappe.query_report.get_filter_value('item_group');
return{
"query": "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items",
"filters": [
['Item', 'disabled', '=', '0'],
['Item Group','name', '=', item_group]
]
}
}
}
]
return filters;
}

View File

@ -3,7 +3,7 @@
import frappe
from frappe import _, qb, query_builder
from frappe.query_builder import functions
from frappe.query_builder import Criterion, functions
def get_columns():
@ -14,6 +14,12 @@ def get_columns():
"fieldtype": "Link",
"options": "Sales Order",
},
{
"label": _("Customer"),
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer",
},
{
"label": _("Posting Date"),
"fieldname": "submitted",
@ -67,6 +73,55 @@ def get_columns():
return columns
def get_descendants_of(doctype, group_name):
group_doc = qb.DocType(doctype)
# get lft and rgt of group node
lft, rgt = (
qb.from_(group_doc).select(group_doc.lft, group_doc.rgt).where(group_doc.name == group_name)
).run()[0]
# get all children of group node
query = (
qb.from_(group_doc).select(group_doc.name).where((group_doc.lft >= lft) & (group_doc.rgt <= rgt))
)
child_nodes = []
for x in query.run():
child_nodes.append(x[0])
return child_nodes
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_customers_or_items(doctype, txt, searchfield, start, page_len, filters):
filter_list = []
if isinstance(filters, list):
for item in filters:
if item[0] == doctype:
filter_list.append(item)
elif item[0] == "Customer Group":
if item[3] != "":
filter_list.append(
[doctype, "customer_group", "in", get_descendants_of("Customer Group", item[3])]
)
elif item[0] == "Item Group":
if item[3] != "":
filter_list.append([doctype, "item_group", "in", get_descendants_of("Item Group", item[3])])
if searchfield and txt:
filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt])
return frappe.desk.reportview.execute(
doctype,
filters=filter_list,
fields=["name", "customer_group"] if doctype == "Customer" else ["name", "item_group"],
limit_start=start,
limit_page_length=page_len,
as_list=True,
)
def get_conditions(filters):
"""
Convert filter options to conditions used in query
@ -79,11 +134,37 @@ def get_conditions(filters):
conditions.start_date = filters.period_start_date or frappe.utils.add_months(
conditions.end_date, -1
)
conditions.sales_order = filters.sales_order or []
return conditions
def build_filter_criterions(filters):
filters = frappe._dict(filters) if filters else frappe._dict({})
qb_criterions = []
if filters.customer_group:
qb_criterions.append(
qb.DocType("Sales Order").customer_group.isin(
get_descendants_of("Customer Group", filters.customer_group)
)
)
if filters.customer:
qb_criterions.append(qb.DocType("Sales Order").customer == filters.customer)
if filters.item_group:
qb_criterions.append(
qb.DocType("Sales Order Item").item_group.isin(
get_descendants_of("Item Group", filters.item_group)
)
)
if filters.item:
qb_criterions.append(qb.DocType("Sales Order Item").item_code == filters.item)
return qb_criterions
def get_so_with_invoices(filters):
"""
Get Sales Order with payment terms template with their associated Invoices
@ -92,16 +173,23 @@ def get_so_with_invoices(filters):
so = qb.DocType("Sales Order")
ps = qb.DocType("Payment Schedule")
soi = qb.DocType("Sales Order Item")
conditions = get_conditions(filters)
filter_criterions = build_filter_criterions(filters)
datediff = query_builder.CustomFunction("DATEDIFF", ["cur_date", "due_date"])
ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"])
conditions = get_conditions(filters)
query_so = (
qb.from_(so)
.join(soi)
.on(soi.parent == so.name)
.join(ps)
.on(ps.parent == so.name)
.select(
so.name,
so.customer,
so.transaction_date.as_("submitted"),
ifelse(datediff(ps.due_date, functions.CurDate()) < 0, "Overdue", "Unpaid").as_("status"),
ps.payment_term,
@ -117,12 +205,10 @@ def get_so_with_invoices(filters):
& (so.company == conditions.company)
& (so.transaction_date[conditions.start_date : conditions.end_date])
)
.where(Criterion.all(filter_criterions))
.orderby(so.name, so.transaction_date, ps.due_date)
)
if conditions.sales_order != []:
query_so = query_so.where(so.name.isin(conditions.sales_order))
sorders = query_so.run(as_dict=True)
invoices = []

View File

@ -11,10 +11,13 @@ from erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_s
)
from erpnext.stock.doctype.item.test_item import create_item
test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template"]
test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Payment Terms Template", "Customer"]
class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
def tearDown(self):
frappe.db.rollback()
def create_payment_terms_template(self):
# create template for 50-50 payments
template = None
@ -48,9 +51,9 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
template.insert()
self.template = template
def test_payment_terms_status(self):
def test_01_payment_terms_status(self):
self.create_payment_terms_template()
item = create_item(item_code="_Test Excavator", is_stock_item=0)
item = create_item(item_code="_Test Excavator 1", is_stock_item=0)
so = make_sales_order(
transaction_date="2021-06-15",
delivery_date=add_days("2021-06-15", -30),
@ -78,13 +81,14 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
"company": "_Test Company",
"period_start_date": "2021-06-01",
"period_end_date": "2021-06-30",
"sales_order": [so.name],
"item": item.item_code,
}
)
expected_value = [
{
"name": so.name,
"customer": so.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Completed",
"payment_term": None,
@ -98,6 +102,7 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
},
{
"name": so.name,
"customer": so.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Partly Paid",
"payment_term": None,
@ -132,11 +137,11 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
)
doc.insert()
def test_alternate_currency(self):
def test_02_alternate_currency(self):
transaction_date = "2021-06-15"
self.create_payment_terms_template()
self.create_exchange_rate(transaction_date)
item = create_item(item_code="_Test Excavator", is_stock_item=0)
item = create_item(item_code="_Test Excavator 2", is_stock_item=0)
so = make_sales_order(
transaction_date=transaction_date,
currency="USD",
@ -166,7 +171,7 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
"company": "_Test Company",
"period_start_date": "2021-06-01",
"period_end_date": "2021-06-30",
"sales_order": [so.name],
"item": item.item_code,
}
)
@ -174,6 +179,7 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
expected_value = [
{
"name": so.name,
"customer": so.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Completed",
"payment_term": None,
@ -187,6 +193,7 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
},
{
"name": so.name,
"customer": so.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Partly Paid",
"payment_term": None,
@ -200,3 +207,134 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase):
},
]
self.assertEqual(data, expected_value)
def test_03_group_filters(self):
transaction_date = "2021-06-15"
self.create_payment_terms_template()
item1 = create_item(item_code="_Test Excavator 1", is_stock_item=0)
item1.item_group = "Products"
item1.save()
so1 = make_sales_order(
transaction_date=transaction_date,
delivery_date=add_days(transaction_date, -30),
item=item1.item_code,
qty=1,
rate=1000000,
do_not_save=True,
)
so1.po_no = ""
so1.taxes_and_charges = ""
so1.taxes = ""
so1.payment_terms_template = self.template.name
so1.save()
so1.submit()
item2 = create_item(item_code="_Test Steel", is_stock_item=0)
item2.item_group = "Raw Material"
item2.save()
so2 = make_sales_order(
customer="_Test Customer 1",
transaction_date=transaction_date,
delivery_date=add_days(transaction_date, -30),
item=item2.item_code,
qty=100,
rate=1000,
do_not_save=True,
)
so2.po_no = ""
so2.taxes_and_charges = ""
so2.taxes = ""
so2.payment_terms_template = self.template.name
so2.save()
so2.submit()
base_filters = {
"company": "_Test Company",
"period_start_date": "2021-06-01",
"period_end_date": "2021-06-30",
}
expected_value_so1 = [
{
"name": so1.name,
"customer": so1.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Overdue",
"payment_term": None,
"description": "_Test 50-50",
"due_date": datetime.date(2021, 6, 30),
"invoice_portion": 50.0,
"currency": "INR",
"base_payment_amount": 500000.0,
"paid_amount": 0.0,
"invoices": "",
},
{
"name": so1.name,
"customer": so1.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Overdue",
"payment_term": None,
"description": "_Test 50-50",
"due_date": datetime.date(2021, 7, 15),
"invoice_portion": 50.0,
"currency": "INR",
"base_payment_amount": 500000.0,
"paid_amount": 0.0,
"invoices": "",
},
]
expected_value_so2 = [
{
"name": so2.name,
"customer": so2.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Overdue",
"payment_term": None,
"description": "_Test 50-50",
"due_date": datetime.date(2021, 6, 30),
"invoice_portion": 50.0,
"currency": "INR",
"base_payment_amount": 50000.0,
"paid_amount": 0.0,
"invoices": "",
},
{
"name": so2.name,
"customer": so2.customer,
"submitted": datetime.date(2021, 6, 15),
"status": "Overdue",
"payment_term": None,
"description": "_Test 50-50",
"due_date": datetime.date(2021, 7, 15),
"invoice_portion": 50.0,
"currency": "INR",
"base_payment_amount": 50000.0,
"paid_amount": 0.0,
"invoices": "",
},
]
group_filters = [
{"customer_group": "All Customer Groups"},
{"item_group": "All Item Groups"},
{"item_group": "Products"},
{"item_group": "Raw Material"},
]
expected_values_for_group_filters = [
expected_value_so1 + expected_value_so2,
expected_value_so1 + expected_value_so2,
expected_value_so1,
expected_value_so2,
]
for idx, g in enumerate(group_filters, 0):
# build filter
filters = frappe._dict({}).update(base_filters).update(g)
with self.subTest(filters=filters):
columns, data, message, chart = execute(filters)
self.assertEqual(data, expected_values_for_group_filters[idx])