mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-01 04:28:27 +00:00
Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com> fix: filter overdue purchase order items by company (#54099)
This commit is contained in:
@@ -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():
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
{% endif %}
|
||||
|
||||
<!-- Purchase Order Items Overdue -->
|
||||
{% if purchase_orders_items_overdue_list %}
|
||||
{% if purchase_orders_items_overdue_map %}
|
||||
<h4 style="{{ section_head }}" class="text-center">{{ _("Purchase Order Items not received on time") }}</h4>
|
||||
<div>
|
||||
<div style="background-color: #fafbfc;">
|
||||
@@ -206,43 +206,41 @@
|
||||
<hr>
|
||||
</div>
|
||||
<div>
|
||||
{% for po in purchase_order_list %}
|
||||
{% for po, po_items in purchase_orders_items_overdue_map.items() %}
|
||||
<div style="{{ line_item }}">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th>
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold;">{{ po.po }}</span>
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold;">{{ po | e }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{% for t in purchase_orders_items_overdue_list %}
|
||||
{% if t.parent == po.po %}
|
||||
<div >
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="padding-left: 7px;">
|
||||
<a style="width: 40%; {{ link_css }}" href="{{ t.link }}">{{ _(t.item_code) }}</a>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ t.missing_qty }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ t.rate }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ t.amount }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for row in po_items %}
|
||||
<div >
|
||||
<table style="width: 100%; table-layout: fixed;">
|
||||
<tr>
|
||||
<td style="width: 40%; padding-left: 7px; vertical-align: top;">
|
||||
<a style="{{ link_css }}" href="{{ row.link | e }}">{{ _(row.item_code) | e }}</a>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right; white-space: nowrap; vertical-align: top;">
|
||||
<span style="{{ label_css }}">
|
||||
{{ row.missing_qty | e }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right; white-space: nowrap; vertical-align: top;">
|
||||
<span style="{{ label_css }}">
|
||||
{{ row.rate | e }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right; white-space: nowrap; vertical-align: top;">
|
||||
<span style="{{ label_css }}">
|
||||
{{ row.amount | e }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -2,8 +2,79 @@
|
||||
# See license.txt
|
||||
import unittest
|
||||
|
||||
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):
|
||||
pass
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user