fix: cost of poor quality report time filters not working (#28958)
* fix: cost of poor quality report time filters not working
* chore:update cost of poor quality report to use query builder
* fix: linter warnings
* chore: updated report query
* chore: added test filters
* fix : cleared linter warnings
* chore: formatting
* refactor: query generation
- optionally apply date filters
- join instead of expensive sub-query
- return as dictionary
* test: simplify test
Co-authored-by: Ankush Menat <ankush@frappe.io>
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
index 97e7e0a..72eed5e 100644
--- a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js
@@ -17,14 +17,12 @@
fieldname:"from_date",
fieldtype: "Datetime",
default: frappe.datetime.convert_to_system_tz(frappe.datetime.add_months(frappe.datetime.now_datetime(), -1)),
- reqd: 1
},
{
label: __("To Date"),
fieldname:"to_date",
fieldtype: "Datetime",
default: frappe.datetime.now_datetime(),
- reqd: 1,
},
{
label: __("Job Card"),
diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
index 7741823..88b2117 100644
--- a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
+++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py
@@ -3,46 +3,65 @@
import frappe
from frappe import _
-from frappe.utils import flt
def execute(filters=None):
- columns, data = [], []
+ return get_columns(filters), get_data(filters)
- columns = get_columns(filters)
- data = get_data(filters)
-
- return columns, data
def get_data(report_filters):
data = []
operations = frappe.get_all("Operation", filters = {"is_corrective_operation": 1})
if operations:
- operations = [d.name for d in operations]
- fields = ["production_item as item_code", "item_name", "work_order", "operation",
- "workstation", "total_time_in_mins", "name", "hour_rate", "serial_no", "batch_no"]
+ if report_filters.get('operation'):
+ operations = [report_filters.get('operation')]
+ else:
+ operations = [d.name for d in operations]
- filters = get_filters(report_filters, operations)
+ job_card = frappe.qb.DocType("Job Card")
- job_cards = frappe.get_all("Job Card", fields = fields,
- filters = filters)
+ operating_cost = ((job_card.hour_rate) * (job_card.total_time_in_mins) / 60.0).as_('operating_cost')
+ item_code = (job_card.production_item).as_('item_code')
- for row in job_cards:
- row.operating_cost = flt(row.hour_rate) * (flt(row.total_time_in_mins) / 60.0)
- data.append(row)
+ query = (frappe.qb
+ .from_(job_card)
+ .select(job_card.name, job_card.work_order, item_code, job_card.item_name,
+ job_card.operation, job_card.serial_no, job_card.batch_no,
+ job_card.workstation, job_card.total_time_in_mins, job_card.hour_rate,
+ operating_cost)
+ .where(
+ (job_card.docstatus == 1)
+ & (job_card.is_corrective_job_card == 1))
+ .groupby(job_card.name)
+ )
+ query = append_filters(query, report_filters, operations, job_card)
+ data = query.run(as_dict=True)
return data
-def get_filters(report_filters, operations):
- filters = {"docstatus": 1, "operation": ("in", operations), "is_corrective_job_card": 1}
- for field in ["name", "work_order", "operation", "workstation", "company", "serial_no", "batch_no", "production_item"]:
- if report_filters.get(field):
- if field != 'serial_no':
- filters[field] = report_filters.get(field)
- else:
- filters[field] = ('like', '% {} %'.format(report_filters.get(field)))
+def append_filters(query, report_filters, operations, job_card):
+ """Append optional filters to query builder. """
- return filters
+ for field in ("name", "work_order", "operation", "workstation",
+ "company", "serial_no", "batch_no", "production_item"):
+ if report_filters.get(field):
+ if field == 'serial_no':
+ query = query.where(job_card[field].like('%{}%'.format(report_filters.get(field))))
+ elif field == 'operation':
+ query = query.where(job_card[field].isin(operations))
+ else:
+ query = query.where(job_card[field] == report_filters.get(field))
+
+ if report_filters.get('from_date') or report_filters.get('to_date'):
+ job_card_time_log = frappe.qb.DocType("Job Card Time Log")
+
+ query = query.join(job_card_time_log).on(job_card.name == job_card_time_log.parent)
+ if report_filters.get('from_date'):
+ query = query.where(job_card_time_log.from_time >= report_filters.get('from_date'))
+ if report_filters.get('to_date'):
+ query = query.where(job_card_time_log.to_time <= report_filters.get('to_date'))
+
+ return query
def get_columns(filters):
return [
diff --git a/erpnext/manufacturing/report/test_reports.py b/erpnext/manufacturing/report/test_reports.py
index 1de4726..9f51ded 100644
--- a/erpnext/manufacturing/report/test_reports.py
+++ b/erpnext/manufacturing/report/test_reports.py
@@ -18,7 +18,7 @@
("BOM Operations Time", {}),
("BOM Stock Calculated", {"bom": frappe.get_last_doc("BOM").name, "qty_to_make": 2}),
("BOM Stock Report", {"bom": frappe.get_last_doc("BOM").name, "qty_to_produce": 2}),
- ("Cost of Poor Quality Report", {}),
+ ("Cost of Poor Quality Report", {"item": "_Test Item", "serial_no": "00"}),
("Downtime Analysis", {}),
(
"Exponential Smoothing Forecasting",