diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py index b10e6434223..ce8f4f35a3f 100644 --- a/erpnext/manufacturing/report/process_loss_report/process_loss_report.py +++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py @@ -5,6 +5,7 @@ from typing import Dict, List, Tuple import frappe from frappe import _ +from frappe.query_builder.functions import Sum Filters = frappe._dict Row = frappe._dict @@ -14,15 +15,50 @@ QueryArgs = Dict[str, str] def execute(filters: Filters) -> Tuple[Columns, Data]: + filters = frappe._dict(filters or {}) columns = get_columns() data = get_data(filters) return columns, data def get_data(filters: Filters) -> Data: - query_args = get_query_args(filters) - data = run_query(query_args) + wo = frappe.qb.DocType("Work Order") + se = frappe.qb.DocType("Stock Entry") + + query = ( + frappe.qb.from_(wo) + .inner_join(se) + .on(wo.name == se.work_order) + .select( + wo.name, + wo.status, + wo.production_item, + wo.qty, + wo.produced_qty, + wo.process_loss_qty, + (wo.produced_qty - wo.process_loss_qty).as_("actual_produced_qty"), + Sum(se.total_incoming_value).as_("total_fg_value"), + Sum(se.total_outgoing_value).as_("total_rm_value"), + ) + .where( + (wo.process_loss_qty > 0) + & (wo.company == filters.company) + & (se.docstatus == 1) + & (se.posting_date.between(filters.from_date, filters.to_date)) + ) + .groupby(se.work_order) + ) + + if "item" in filters: + query.where(wo.production_item == filters.item) + + if "work_order" in filters: + query.where(wo.name == filters.work_order) + + data = query.run(as_dict=True) + update_data_with_total_pl_value(data) + return data @@ -67,54 +103,7 @@ def get_columns() -> Columns: ] -def get_query_args(filters: Filters) -> QueryArgs: - query_args = {} - query_args.update(filters) - query_args.update(get_filter_conditions(filters)) - return query_args - - -def run_query(query_args: QueryArgs) -> Data: - return frappe.db.sql( - """ - SELECT - wo.name, wo.status, wo.production_item, wo.qty, - wo.produced_qty, wo.process_loss_qty, - (wo.produced_qty - wo.process_loss_qty) as actual_produced_qty, - sum(se.total_incoming_value) as total_fg_value, - sum(se.total_outgoing_value) as total_rm_value - FROM - `tabWork Order` wo INNER JOIN `tabStock Entry` se - ON wo.name=se.work_order - WHERE - process_loss_qty > 0 - AND wo.company = %(company)s - AND se.docstatus = 1 - AND se.posting_date BETWEEN %(from_date)s AND %(to_date)s - {item_filter} - {work_order_filter} - GROUP BY - se.work_order - """.format( - **query_args - ), - query_args, - as_dict=1, - ) - - def update_data_with_total_pl_value(data: Data) -> None: for row in data: value_per_unit_fg = row["total_fg_value"] / row["actual_produced_qty"] row["total_pl_value"] = row["process_loss_qty"] * value_per_unit_fg - - -def get_filter_conditions(filters: Filters) -> QueryArgs: - filter_conditions = dict(item_filter="", work_order_filter="") - if "item" in filters: - production_item = filters.get("item") - filter_conditions.update({"item_filter": f"AND wo.production_item='{production_item}'"}) - if "work_order" in filters: - work_order_name = filters.get("work_order") - filter_conditions.update({"work_order_filter": f"AND wo.name='{work_order_name}'"}) - return filter_conditions diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py index c6b7e58d656..d2e3e4081c2 100644 --- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py +++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py @@ -3,6 +3,7 @@ import frappe +from frappe.query_builder.functions import IfNull from frappe.utils import cint @@ -16,70 +17,70 @@ def execute(filters=None): def get_item_list(wo_list, filters): out = [] - # Add a row for each item/qty - for wo_details in wo_list: - desc = frappe.db.get_value("BOM", wo_details.bom_no, "description") + if wo_list: + bin = frappe.qb.DocType("Bin") + bom = frappe.qb.DocType("BOM") + bom_item = frappe.qb.DocType("BOM Item") - for wo_item_details in frappe.db.get_values( - "Work Order Item", {"parent": wo_details.name}, ["item_code", "source_warehouse"], as_dict=1 - ): + # Add a row for each item/qty + for wo_details in wo_list: + desc = frappe.db.get_value("BOM", wo_details.bom_no, "description") - item_list = frappe.db.sql( - """SELECT - bom_item.item_code as item_code, - ifnull(ledger.actual_qty*bom.quantity/bom_item.stock_qty,0) as build_qty - FROM - `tabBOM` as bom, `tabBOM Item` AS bom_item - LEFT JOIN `tabBin` AS ledger - ON bom_item.item_code = ledger.item_code - AND ledger.warehouse = ifnull(%(warehouse)s,%(filterhouse)s) - WHERE - bom.name = bom_item.parent - and bom_item.item_code = %(item_code)s - and bom.name = %(bom)s - GROUP BY - bom_item.item_code""", - { - "bom": wo_details.bom_no, - "warehouse": wo_item_details.source_warehouse, - "filterhouse": filters.warehouse, - "item_code": wo_item_details.item_code, - }, - as_dict=1, - ) + for wo_item_details in frappe.db.get_values( + "Work Order Item", {"parent": wo_details.name}, ["item_code", "source_warehouse"], as_dict=1 + ): + item_list = ( + frappe.qb.from_(bom) + .from_(bom_item) + .left_join(bin) + .on( + (bom_item.item_code == bin.item_code) + & (bin.warehouse == IfNull(wo_item_details.source_warehouse, filters.warehouse)) + ) + .select( + bom_item.item_code.as_("item_code"), + IfNull(bin.actual_qty * bom.quantity / bom_item.stock_qty, 0).as_("build_qty"), + ) + .where( + (bom.name == bom_item.parent) + & (bom_item.item_code == wo_item_details.item_code) + & (bom.name == wo_details.bom_no) + ) + .groupby(bom_item.item_code) + ).run(as_dict=1) - stock_qty = 0 - count = 0 - buildable_qty = wo_details.qty - for item in item_list: - count = count + 1 - if item.build_qty >= (wo_details.qty - wo_details.produced_qty): - stock_qty = stock_qty + 1 - elif buildable_qty >= item.build_qty: - buildable_qty = item.build_qty + stock_qty = 0 + count = 0 + buildable_qty = wo_details.qty + for item in item_list: + count = count + 1 + if item.build_qty >= (wo_details.qty - wo_details.produced_qty): + stock_qty = stock_qty + 1 + elif buildable_qty >= item.build_qty: + buildable_qty = item.build_qty - if count == stock_qty: - build = "Y" - else: - build = "N" + if count == stock_qty: + build = "Y" + else: + build = "N" - row = frappe._dict( - { - "work_order": wo_details.name, - "status": wo_details.status, - "req_items": cint(count), - "instock": stock_qty, - "description": desc, - "source_warehouse": wo_item_details.source_warehouse, - "item_code": wo_item_details.item_code, - "bom_no": wo_details.bom_no, - "qty": wo_details.qty, - "buildable_qty": buildable_qty, - "ready_to_build": build, - } - ) + row = frappe._dict( + { + "work_order": wo_details.name, + "status": wo_details.status, + "req_items": cint(count), + "instock": stock_qty, + "description": desc, + "source_warehouse": wo_item_details.source_warehouse, + "item_code": wo_item_details.item_code, + "bom_no": wo_details.bom_no, + "qty": wo_details.qty, + "buildable_qty": buildable_qty, + "ready_to_build": build, + } + ) - out.append(row) + out.append(row) return out