diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index ea25bd033b9..46d688bf8f4 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -158,10 +158,7 @@ class EmailDigest(Document): context.quote = {"text": quote[0], "author": quote[1]} if self.get("purchase_orders_items_overdue"): - ( - context.purchase_order_list, - context.purchase_orders_items_overdue_list, - ) = self.get_purchase_orders_items_overdue_list() + context.purchase_orders_items_overdue_map = self.get_purchase_orders_items_overdue_list() if not context: return None @@ -860,30 +857,42 @@ class EmailDigest(Document): return fmt_money(value, currency=self.currency) def get_purchase_orders_items_overdue_list(self): - fields_po = "distinct `tabPurchase Order Item`.parent as po" - fields_poi = ( - "`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code," - "received_qty, qty - received_qty as missing_qty, rate, amount" + po = frappe.qb.DocType("Purchase Order") + poi = frappe.qb.DocType("Purchase Order Item") + + query = ( + frappe.qb.from_(poi) + .select( + poi.parent, + poi.schedule_date, + poi.item_code, + poi.received_qty, + (poi.qty - poi.received_qty).as_("missing_qty"), + poi.rate, + poi.amount, + po.currency, + ) + .inner_join(po) + .on(po.name == poi.parent) + .where(po.status != "Closed") + .where(poi.docstatus == 1) + .where(poi.schedule_date < today()) + .where(poi.received_qty < poi.qty) + .where(po.company == self.company) + .orderby(poi.parent, order=frappe.qb.desc) + .orderby(poi.idx) ) - sql_po = f"""select {fields_po} from `tabPurchase Order Item` - left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent - where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and CURRENT_DATE > `tabPurchase Order Item`.schedule_date - and received_qty < qty order by `tabPurchase Order Item`.parent DESC, - `tabPurchase Order Item`.schedule_date DESC""" + items_by_parent = frappe._dict() - sql_poi = f"""select {fields_poi} from `tabPurchase Order Item` - left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent - where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and CURRENT_DATE > `tabPurchase Order Item`.schedule_date - and received_qty < qty order by `tabPurchase Order Item`.idx""" - purchase_order_list = frappe.db.sql(sql_po, as_dict=True) - purchase_order_items_overdue_list = frappe.db.sql(sql_poi, as_dict=True) + for row in query.run(as_dict=True): + row.link = get_url_to_form("Purchase Order", row.parent) + row.rate = fmt_money(row.rate, 2, row.currency) + row.amount = fmt_money(row.amount, 2, row.currency) - for t in purchase_order_items_overdue_list: - t.link = get_url_to_form("Purchase Order", t.parent) - t.rate = fmt_money(t.rate, 2, t.currency) - t.amount = fmt_money(t.amount, 2, t.currency) - return purchase_order_list, purchase_order_items_overdue_list + items_by_parent.setdefault(row.parent, []).append(row) + + return items_by_parent def send(): diff --git a/erpnext/setup/doctype/email_digest/templates/default.html b/erpnext/setup/doctype/email_digest/templates/default.html index 666301a643e..722a2470772 100644 --- a/erpnext/setup/doctype/email_digest/templates/default.html +++ b/erpnext/setup/doctype/email_digest/templates/default.html @@ -182,7 +182,7 @@ {% endif %} -{% if purchase_orders_items_overdue_list %} +{% if purchase_orders_items_overdue_map %}

{{ _("Purchase Order Items not received on time") }}

@@ -206,43 +206,41 @@
- {% for po in purchase_order_list %} + {% for po, po_items in purchase_orders_items_overdue_map.items() %}
diff --git a/erpnext/setup/doctype/email_digest/test_email_digest.py b/erpnext/setup/doctype/email_digest/test_email_digest.py index dae28b81b5e..2f0f8862381 100644 --- a/erpnext/setup/doctype/email_digest/test_email_digest.py +++ b/erpnext/setup/doctype/email_digest/test_email_digest.py @@ -1,6 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt +<<<<<<< HEAD import unittest # test_records = frappe.get_test_records('Email Digest') @@ -8,3 +9,81 @@ import unittest class TestEmailDigest(unittest.TestCase): pass +======= +import frappe +from frappe.utils import add_days, today + +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order +from erpnext.tests.utils import ERPNextTestSuite + + +class TestEmailDigest(ERPNextTestSuite): + def test_purchase_orders_items_overdue_list_is_filtered_by_company(self): + digest = create_email_digest( + company="_Test Company", + frequency="Daily", + purchase_orders_items_overdue=1, + name="Test Email Digest PO Company Filter", + ) + backdate = add_days(today(), -1) + + po1 = create_purchase_order(transaction_date=backdate, do_not_save=True) + po1.schedule_date = backdate + po1.items[0].schedule_date = backdate + po1.insert() + po1.submit() + + po2 = create_purchase_order( + company="_Test Company 1", + warehouse="Stores - _TC1", + transaction_date=backdate, + do_not_save=True, + ) + po2.schedule_date = backdate + po2.items[0].schedule_date = backdate + po2.insert() + po2.submit() + + overdue_items = digest.get_purchase_orders_items_overdue_list() + + self.assertIn(po1.name, overdue_items) + self.assertNotIn(po2.name, overdue_items) + + +def create_email_digest(**args): + args = frappe._dict(args) + doc = frappe.new_doc("Email Digest") + doc.name = args.name or "Test Email Digest" + doc.company = args.company or "_Test Company" + doc.frequency = args.frequency or "Daily" + doc.enabled = args.enabled or 0 + doc.bank_balance = args.bank_balance or 0 + doc.credit_balance = args.credit_balance or 0 + doc.invoiced_amount = args.invoiced_amount or 0 + doc.payables = args.payables or 0 + doc.sales_orders_to_bill = args.sales_orders_to_bill or 0 + doc.purchase_orders_to_bill = args.purchase_orders_to_bill or 0 + doc.sales_order = args.sales_order or 0 + doc.purchase_order = args.purchase_order or 0 + doc.sales_orders_to_deliver = args.sales_orders_to_deliver or 0 + doc.purchase_orders_to_receive = args.purchase_orders_to_receive or 0 + doc.sales_invoice = args.sales_invoice or 0 + doc.purchase_invoice = args.purchase_invoice or 0 + doc.new_quotations = args.new_quotations or 0 + doc.pending_quotations = args.pending_quotations or 0 + doc.issue = args.issue or 0 + doc.project = args.project or 0 + doc.purchase_orders_items_overdue = args.purchase_orders_items_overdue or 0 + doc.calendar_events = args.calendar_events or 0 + doc.todo_list = args.todo_list or 0 + doc.notifications = args.notifications or 0 + doc.add_quote = args.add_quote or 0 + + for recipient in args.recipients or ["Administrator"]: + doc.append("recipients", {"recipient": recipient}) + + if not args.do_not_save: + doc.insert() + + return doc +>>>>>>> a8030c9713 (fix: filter overdue purchase order items by company (#54099))
- {{ po.po }} + {{ po | e }}
- {% for t in purchase_orders_items_overdue_list %} - {% if t.parent == po.po %} -
- - - - - - - -
- {{ _(t.item_code) }} - - - {{ t.missing_qty }} - - - - {{ t.rate }} - - - - {{ t.amount }} - -
-
- {% endif %} + {% for row in po_items %} +
+ + + + + + + +
+ {{ _(row.item_code) | e }} + + + {{ row.missing_qty | e }} + + + + {{ row.rate | e }} + + + + {{ row.amount | e }} + +
+
{% endfor %}