Merge pull request #32635 from ruthra-kumar/fix_tax_or_charges_acc_updating_voucher_outstanding
fix: Integrity Error on PLE while submitting sales invoice
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 9af3188..826d71b 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -268,6 +268,18 @@
return item_details
+def update_pricing_rule_uom(pricing_rule, args):
+ child_doc = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}.get(
+ pricing_rule.apply_on
+ )
+
+ apply_on_field = frappe.scrub(pricing_rule.apply_on)
+
+ for row in pricing_rule.get(child_doc):
+ if row.get(apply_on_field) == args.get(apply_on_field):
+ pricing_rule.uom = row.uom
+
+
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
from erpnext.accounts.doctype.pricing_rule.utils import (
get_applied_pricing_rules,
@@ -324,6 +336,7 @@
if isinstance(pricing_rule, str):
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
+ update_pricing_rule_uom(pricing_rule, args)
pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or []
if pricing_rule.get("suggestion"):
@@ -440,12 +453,15 @@
if pricing_rule.currency == args.currency:
pricing_rule_rate = pricing_rule.rate
+ # TODO https://github.com/frappe/erpnext/pull/23636 solve this in some other way.
if pricing_rule_rate:
+ is_blank_uom = pricing_rule.get("uom") != args.get("uom")
# Override already set price list rate (from item price)
# if pricing_rule_rate > 0
item_details.update(
{
- "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
+ "price_list_rate": pricing_rule_rate
+ * (args.get("conversion_factor", 1) if is_blank_uom else 1),
}
)
item_details.update({"discount_percentage": 0.0})
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 0a9db6b..fbe5678 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -595,6 +595,121 @@
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
item.delete()
+ def test_item_price_with_blank_uom_pricing_rule(self):
+ properties = {
+ "item_code": "Item Blank UOM",
+ "stock_uom": "Nos",
+ "sales_uom": "Box",
+ "uoms": [dict(uom="Box", conversion_factor=10)],
+ }
+ item = make_item(properties=properties)
+
+ make_item_price("Item Blank UOM", "_Test Price List", 100)
+
+ pricing_rule_record = {
+ "doctype": "Pricing Rule",
+ "title": "_Test Item Blank UOM Rule",
+ "apply_on": "Item Code",
+ "items": [
+ {
+ "item_code": "Item Blank UOM",
+ }
+ ],
+ "selling": 1,
+ "currency": "INR",
+ "rate_or_discount": "Rate",
+ "rate": 101,
+ "company": "_Test Company",
+ }
+ rule = frappe.get_doc(pricing_rule_record)
+ rule.insert()
+
+ si = create_sales_invoice(
+ do_not_save=True, item_code="Item Blank UOM", uom="Box", conversion_factor=10
+ )
+ si.selling_price_list = "_Test Price List"
+ si.save()
+
+ # If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM.
+ # rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010
+ self.assertEqual(si.items[0].price_list_rate, 1010)
+ self.assertEqual(si.items[0].rate, 1010)
+
+ si.delete()
+
+ si = create_sales_invoice(do_not_save=True, item_code="Item Blank UOM", uom="Nos")
+ si.selling_price_list = "_Test Price List"
+ si.save()
+
+ # UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM.
+ # rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101
+ self.assertEqual(si.items[0].price_list_rate, 101)
+ self.assertEqual(si.items[0].rate, 101)
+
+ si.delete()
+ rule.delete()
+ frappe.get_doc("Item Price", {"item_code": "Item Blank UOM"}).delete()
+
+ item.delete()
+
+ def test_item_price_with_selling_uom_pricing_rule(self):
+ properties = {
+ "item_code": "Item UOM other than Stock",
+ "stock_uom": "Nos",
+ "sales_uom": "Box",
+ "uoms": [dict(uom="Box", conversion_factor=10)],
+ }
+ item = make_item(properties=properties)
+
+ make_item_price("Item UOM other than Stock", "_Test Price List", 100)
+
+ pricing_rule_record = {
+ "doctype": "Pricing Rule",
+ "title": "_Test Item UOM other than Stock Rule",
+ "apply_on": "Item Code",
+ "items": [
+ {
+ "item_code": "Item UOM other than Stock",
+ "uom": "Box",
+ }
+ ],
+ "selling": 1,
+ "currency": "INR",
+ "rate_or_discount": "Rate",
+ "rate": 101,
+ "company": "_Test Company",
+ }
+ rule = frappe.get_doc(pricing_rule_record)
+ rule.insert()
+
+ si = create_sales_invoice(
+ do_not_save=True, item_code="Item UOM other than Stock", uom="Box", conversion_factor=10
+ )
+ si.selling_price_list = "_Test Price List"
+ si.save()
+
+ # UOM is Box so apply pricing_rule only on Box UOM.
+ # Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor.
+ self.assertEqual(si.items[0].price_list_rate, 101)
+ self.assertEqual(si.items[0].rate, 101)
+
+ si.delete()
+
+ si = create_sales_invoice(do_not_save=True, item_code="Item UOM other than Stock", uom="Nos")
+ si.selling_price_list = "_Test Price List"
+ si.save()
+
+ # UOM is Box so pricing_rule won't apply as selling_uom is Nos.
+ # As Pricing Rule is not applied price of 100 will be fetched from Item Price List.
+ self.assertEqual(si.items[0].price_list_rate, 100)
+ self.assertEqual(si.items[0].rate, 100)
+
+ si.delete()
+ rule.delete()
+ frappe.get_doc("Item Price", {"item_code": "Item UOM other than Stock"}).delete()
+
+ item.delete()
+
def test_pricing_rule_for_different_currency(self):
make_item("Test Sanitizer Item")
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 1f29d73..4c78d72 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -111,6 +111,12 @@
)
if apply_on_field == "item_code":
+ if args.get("uom", None):
+ item_conditions += (
+ " and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format(
+ child_doc=child_doc, item_uom=args.get("uom")
+ )
+ )
if "variant_of" not in args:
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index ec861a2..c3a9855 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -31,7 +31,7 @@
super.onload();
// Ignore linked advances
- this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry'];
+ this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice'];
if(!this.frm.doc.__islocal) {
// show credit_to in print format
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 5dbe7eb..3d74b8f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -1415,6 +1415,7 @@
"Stock Ledger Entry",
"Repost Item Valuation",
"Payment Ledger Entry",
+ "Purchase Invoice",
)
self.update_advance_tax_references(cancel=1)
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 46c777d..1ba7824 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -3336,7 +3336,7 @@
"asset": args.asset or None,
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no,
- "conversion_factor": 1,
+ "conversion_factor": args.get("conversion_factor", 1),
"incoming_rate": args.incoming_rate or 0,
"batch_no": args.batch_no or None,
},
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index ae4ab64..d7bf991 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -1368,9 +1368,8 @@
frappe.delete_doc("Desktop Icon", icon)
-def create_payment_ledger_entry(
- gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
-):
+def get_payment_ledger_entries(gl_entries, cancel=0):
+ ple_map = []
if gl_entries:
ple = None
@@ -1410,44 +1409,57 @@
dr_or_cr *= -1
dr_or_cr_account_currency *= -1
- ple = frappe.get_doc(
- {
- "doctype": "Payment Ledger Entry",
- "posting_date": gle.posting_date,
- "company": gle.company,
- "account_type": account_type,
- "account": gle.account,
- "party_type": gle.party_type,
- "party": gle.party,
- "cost_center": gle.cost_center,
- "finance_book": gle.finance_book,
- "due_date": gle.due_date,
- "voucher_type": gle.voucher_type,
- "voucher_no": gle.voucher_no,
- "against_voucher_type": gle.against_voucher_type
- if gle.against_voucher_type
- else gle.voucher_type,
- "against_voucher_no": gle.against_voucher if gle.against_voucher else gle.voucher_no,
- "account_currency": gle.account_currency,
- "amount": dr_or_cr,
- "amount_in_account_currency": dr_or_cr_account_currency,
- "delinked": True if cancel else False,
- "remarks": gle.remarks,
- }
+ ple = frappe._dict(
+ doctype="Payment Ledger Entry",
+ posting_date=gle.posting_date,
+ company=gle.company,
+ account_type=account_type,
+ account=gle.account,
+ party_type=gle.party_type,
+ party=gle.party,
+ cost_center=gle.cost_center,
+ finance_book=gle.finance_book,
+ due_date=gle.due_date,
+ voucher_type=gle.voucher_type,
+ voucher_no=gle.voucher_no,
+ against_voucher_type=gle.against_voucher_type
+ if gle.against_voucher_type
+ else gle.voucher_type,
+ against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no,
+ account_currency=gle.account_currency,
+ amount=dr_or_cr,
+ amount_in_account_currency=dr_or_cr_account_currency,
+ delinked=True if cancel else False,
+ remarks=gle.remarks,
)
dimensions_and_defaults = get_dimensions()
if dimensions_and_defaults:
for dimension in dimensions_and_defaults[0]:
- ple.set(dimension.fieldname, gle.get(dimension.fieldname))
+ ple[dimension.fieldname] = gle.get(dimension.fieldname)
- if cancel:
- delink_original_entry(ple)
- ple.flags.ignore_permissions = 1
- ple.flags.adv_adj = adv_adj
- ple.flags.from_repost = from_repost
- ple.flags.update_outstanding = update_outstanding
- ple.submit()
+ ple_map.append(ple)
+ return ple_map
+
+
+def create_payment_ledger_entry(
+ gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
+):
+ if gl_entries:
+ ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel)
+
+ for entry in ple_map:
+
+ ple = frappe.get_doc(entry)
+
+ if cancel:
+ delink_original_entry(ple)
+
+ ple.flags.ignore_permissions = 1
+ ple.flags.adv_adj = adv_adj
+ ple.flags.from_repost = from_repost
+ ple.flags.update_outstanding = update_outstanding
+ ple.submit()
def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party):
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
index d70ac46..71019e8 100644
--- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -127,32 +127,27 @@
return columns
-def get_conditions(filters):
- conditions = ""
-
+def apply_filters_on_query(filters, parent, child, query):
if filters.get("company"):
- conditions += " AND parent.company=%s" % frappe.db.escape(filters.get("company"))
+ query = query.where(parent.company == filters.get("company"))
if filters.get("cost_center") or filters.get("project"):
- conditions += """
- AND (child.`cost_center`=%s OR child.`project`=%s)
- """ % (
- frappe.db.escape(filters.get("cost_center")),
- frappe.db.escape(filters.get("project")),
+ query = query.where(
+ (child.cost_center == filters.get("cost_center")) | (child.project == filters.get("project"))
)
if filters.get("from_date"):
- conditions += " AND parent.transaction_date>='%s'" % filters.get("from_date")
+ query = query.where(parent.transaction_date >= filters.get("from_date"))
if filters.get("to_date"):
- conditions += " AND parent.transaction_date<='%s'" % filters.get("to_date")
- return conditions
+ query = query.where(parent.transaction_date <= filters.get("to_date"))
+
+ return query
def get_data(filters):
- conditions = get_conditions(filters)
- purchase_order_entry = get_po_entries(conditions)
- mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
+ purchase_order_entry = get_po_entries(filters)
+ mr_records, procurement_record_against_mr = get_mapped_mr_details(filters)
pr_records = get_mapped_pr_records()
pi_records = get_mapped_pi_records()
@@ -187,11 +182,15 @@
return procurement_record
-def get_mapped_mr_details(conditions):
+def get_mapped_mr_details(filters):
mr_records = {}
- mr_details = frappe.db.sql(
- """
- SELECT
+ parent = frappe.qb.DocType("Material Request")
+ child = frappe.qb.DocType("Material Request Item")
+
+ query = (
+ frappe.qb.from_(parent)
+ .from_(child)
+ .select(
parent.transaction_date,
parent.per_ordered,
parent.owner,
@@ -203,18 +202,13 @@
child.uom,
parent.status,
child.project,
- child.cost_center
- FROM `tabMaterial Request` parent, `tabMaterial Request Item` child
- WHERE
- parent.per_ordered>=0
- AND parent.name=child.parent
- AND parent.docstatus=1
- {conditions}
- """.format(
- conditions=conditions
- ),
- as_dict=1,
- ) # nosec
+ child.cost_center,
+ )
+ .where((parent.per_ordered >= 0) & (parent.name == child.parent) & (parent.docstatus == 1))
+ )
+ query = apply_filters_on_query(filters, parent, child, query)
+
+ mr_details = query.run(as_dict=True)
procurement_record_against_mr = []
for record in mr_details:
@@ -241,46 +235,49 @@
def get_mapped_pi_records():
- return frappe._dict(
- frappe.db.sql(
- """
- SELECT
- pi_item.po_detail,
- pi_item.base_amount
- FROM `tabPurchase Invoice Item` as pi_item
- INNER JOIN `tabPurchase Order` as po
- ON pi_item.`purchase_order` = po.`name`
- WHERE
- pi_item.docstatus = 1
- AND po.status not in ('Closed','Completed','Cancelled')
- AND pi_item.po_detail IS NOT NULL
- """
+ po = frappe.qb.DocType("Purchase Order")
+ pi_item = frappe.qb.DocType("Purchase Invoice Item")
+ pi_records = (
+ frappe.qb.from_(pi_item)
+ .inner_join(po)
+ .on(pi_item.purchase_order == po.name)
+ .select(pi_item.po_detail, pi_item.base_amount)
+ .where(
+ (pi_item.docstatus == 1)
+ & (po.status.notin(("Closed", "Completed", "Cancelled")))
+ & (pi_item.po_detail.isnotnull())
)
- )
+ ).run()
+
+ return frappe._dict(pi_records)
def get_mapped_pr_records():
- return frappe._dict(
- frappe.db.sql(
- """
- SELECT
- pr_item.purchase_order_item,
- pr.posting_date
- FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
- WHERE
- pr.docstatus=1
- AND pr.name=pr_item.parent
- AND pr_item.purchase_order_item IS NOT NULL
- AND pr.status not in ('Closed','Completed','Cancelled')
- """
+ pr = frappe.qb.DocType("Purchase Receipt")
+ pr_item = frappe.qb.DocType("Purchase Receipt Item")
+ pr_records = (
+ frappe.qb.from_(pr)
+ .from_(pr_item)
+ .select(pr_item.purchase_order_item, pr.posting_date)
+ .where(
+ (pr.docstatus == 1)
+ & (pr.name == pr_item.parent)
+ & (pr_item.purchase_order_item.isnotnull())
+ & (pr.status.notin(("Closed", "Completed", "Cancelled")))
)
- )
+ ).run()
+
+ return frappe._dict(pr_records)
-def get_po_entries(conditions):
- return frappe.db.sql(
- """
- SELECT
+def get_po_entries(filters):
+ parent = frappe.qb.DocType("Purchase Order")
+ child = frappe.qb.DocType("Purchase Order Item")
+
+ query = (
+ frappe.qb.from_(parent)
+ .from_(child)
+ .select(
child.name,
child.parent,
child.cost_center,
@@ -297,17 +294,15 @@
parent.transaction_date,
parent.supplier,
parent.status,
- parent.owner
- FROM `tabPurchase Order` parent, `tabPurchase Order Item` child
- WHERE
- parent.docstatus = 1
- AND parent.name = child.parent
- AND parent.status not in ('Closed','Completed','Cancelled')
- {conditions}
- GROUP BY
- parent.name, child.item_code
- """.format(
- conditions=conditions
- ),
- as_dict=1,
- ) # nosec
+ parent.owner,
+ )
+ .where(
+ (parent.docstatus == 1)
+ & (parent.name == child.parent)
+ & (parent.status.notin(("Closed", "Completed", "Cancelled")))
+ )
+ .groupby(parent.name, child.item_code)
+ )
+ query = apply_filters_on_query(filters, parent, child, query)
+
+ return query.run(as_dict=True)
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
index a5c4649..e10c0e2 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -6,6 +6,7 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import IfNull
from frappe.utils import date_diff, flt, getdate
@@ -16,9 +17,7 @@
validate_filters(filters)
columns = get_columns(filters)
- conditions = get_conditions(filters)
-
- data = get_data(conditions, filters)
+ data = get_data(filters)
if not data:
return [], [], None, []
@@ -37,60 +36,61 @@
frappe.throw(_("To Date cannot be before From Date."))
-def get_conditions(filters):
- conditions = ""
- if filters.get("from_date") and filters.get("to_date"):
- conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
+def get_data(filters):
+ po = frappe.qb.DocType("Purchase Order")
+ po_item = frappe.qb.DocType("Purchase Order Item")
+ pi_item = frappe.qb.DocType("Purchase Invoice Item")
- for field in ["company", "name"]:
+ query = (
+ frappe.qb.from_(po)
+ .from_(po_item)
+ .left_join(pi_item)
+ .on(pi_item.po_detail == po_item.name)
+ .select(
+ po.transaction_date.as_("date"),
+ po_item.schedule_date.as_("required_date"),
+ po_item.project,
+ po.name.as_("purchase_order"),
+ po.status,
+ po.supplier,
+ po_item.item_code,
+ po_item.qty,
+ po_item.received_qty,
+ (po_item.qty - po_item.received_qty).as_("pending_qty"),
+ IfNull(pi_item.qty, 0).as_("billed_qty"),
+ po_item.base_amount.as_("amount"),
+ (po_item.received_qty * po_item.base_rate).as_("received_qty_amount"),
+ (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
+ (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_(
+ "pending_amount"
+ ),
+ po.set_warehouse.as_("warehouse"),
+ po.company,
+ po_item.name,
+ )
+ .where(
+ (po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1)
+ )
+ .groupby(po_item.name)
+ .orderby(po.transaction_date)
+ )
+
+ for field in ("company", "name"):
if filters.get(field):
- conditions += f" and po.{field} = %({field})s"
+ query = query.where(po[field] == filters.get(field))
+
+ if filters.get("from_date") and filters.get("to_date"):
+ query = query.where(
+ po.transaction_date.between(filters.get("from_date"), filters.get("to_date"))
+ )
if filters.get("status"):
- conditions += " and po.status in %(status)s"
+ query = query.where(po.status.isin(filters.get("status")))
if filters.get("project"):
- conditions += " and poi.project = %(project)s"
+ query = query.where(po_item.project == filters.get("project"))
- return conditions
-
-
-def get_data(conditions, filters):
- data = frappe.db.sql(
- """
- SELECT
- po.transaction_date as date,
- poi.schedule_date as required_date,
- poi.project,
- po.name as purchase_order,
- po.status, po.supplier, poi.item_code,
- poi.qty, poi.received_qty,
- (poi.qty - poi.received_qty) AS pending_qty,
- IFNULL(pii.qty, 0) as billed_qty,
- poi.base_amount as amount,
- (poi.received_qty * poi.base_rate) as received_qty_amount,
- (poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount,
- (poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount,
- po.set_warehouse as warehouse,
- po.company, poi.name
- FROM
- `tabPurchase Order` po,
- `tabPurchase Order Item` poi
- LEFT JOIN `tabPurchase Invoice Item` pii
- ON pii.po_detail = poi.name
- WHERE
- poi.parent = po.name
- and po.status not in ('Stopped', 'Closed')
- and po.docstatus = 1
- {0}
- GROUP BY poi.name
- ORDER BY po.transaction_date ASC
- """.format(
- conditions
- ),
- filters,
- as_dict=1,
- )
+ data = query.run(as_dict=True)
return data
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
index 3013b6d..a728290 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
@@ -16,8 +16,7 @@
return [], []
columns = get_columns(filters)
- conditions = get_conditions(filters)
- supplier_quotation_data = get_data(filters, conditions)
+ supplier_quotation_data = get_data(filters)
data, chart_data = prepare_data(supplier_quotation_data, filters)
message = get_message()
@@ -25,50 +24,51 @@
return columns, data, message, chart_data
-def get_conditions(filters):
- conditions = ""
+def get_data(filters):
+ sq = frappe.qb.DocType("Supplier Quotation")
+ sq_item = frappe.qb.DocType("Supplier Quotation Item")
+
+ query = (
+ frappe.qb.from_(sq_item)
+ .from_(sq)
+ .select(
+ sq_item.parent,
+ sq_item.item_code,
+ sq_item.qty,
+ sq_item.stock_qty,
+ sq_item.amount,
+ sq_item.uom,
+ sq_item.stock_uom,
+ sq_item.request_for_quotation,
+ sq_item.lead_time_days,
+ sq.supplier.as_("supplier_name"),
+ sq.valid_till,
+ )
+ .where(
+ (sq_item.parent == sq.name)
+ & (sq_item.docstatus < 2)
+ & (sq.company == filters.get("company"))
+ & (sq.transaction_date.between(filters.get("from_date"), filters.get("to_date")))
+ )
+ .orderby(sq.transaction_date, sq_item.item_code)
+ )
+
if filters.get("item_code"):
- conditions += " AND sqi.item_code = %(item_code)s"
+ query = query.where(sq_item.item_code == filters.get("item_code"))
if filters.get("supplier_quotation"):
- conditions += " AND sqi.parent in %(supplier_quotation)s"
+ query = query.where(sq_item.parent.isin(filters.get("supplier_quotation")))
if filters.get("request_for_quotation"):
- conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
+ query = query.where(sq_item.request_for_quotation == filters.get("request_for_quotation"))
if filters.get("supplier"):
- conditions += " AND sq.supplier in %(supplier)s"
+ query = query.where(sq.supplier.isin(filters.get("supplier")))
if not filters.get("include_expired"):
- conditions += " AND sq.status != 'Expired'"
+ query = query.where(sq.status != "Expired")
- return conditions
-
-
-def get_data(filters, conditions):
- supplier_quotation_data = frappe.db.sql(
- """
- SELECT
- sqi.parent, sqi.item_code,
- sqi.qty, sqi.stock_qty, sqi.amount,
- sqi.uom, sqi.stock_uom,
- sqi.request_for_quotation,
- sqi.lead_time_days, sq.supplier as supplier_name, sq.valid_till
- FROM
- `tabSupplier Quotation Item` sqi,
- `tabSupplier Quotation` sq
- WHERE
- sqi.parent = sq.name
- AND sqi.docstatus < 2
- AND sq.company = %(company)s
- AND sq.transaction_date between %(from_date)s and %(to_date)s
- {0}
- order by sq.transaction_date, sqi.item_code""".format(
- conditions
- ),
- filters,
- as_dict=1,
- )
+ supplier_quotation_data = query.run(as_dict=True)
return supplier_quotation_data
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index c17610b..7fecb18 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -426,6 +426,7 @@
if(!this.validate_company_and_party()) {
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
} else {
+ item.pricing_rules = ''
return this.frm.call({
method: "erpnext.stock.get_item_details.get_item_details",
child: item,
@@ -1045,6 +1046,7 @@
uom(doc, cdt, cdn) {
var me = this;
var item = frappe.get_doc(cdt, cdn);
+ item.pricing_rules = ''
if(item.item_code && item.uom) {
return this.frm.call({
method: "erpnext.stock.get_item_details.get_conversion_factor",
@@ -1121,6 +1123,7 @@
qty(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
+ item.pricing_rules = ''
this.conversion_factor(doc, cdt, cdn, true);
this.calculate_stock_uom_rate(doc, cdt, cdn);
this.apply_pricing_rule(item, true);
diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
index 3d9b046..ef7d6e6 100644
--- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
+++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py
@@ -4,6 +4,7 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import IfNull
from frappe.utils import cint, getdate
@@ -54,31 +55,28 @@
return columns
-def get_conditions(filters):
- conditions = ""
+def get_stock_ledger_entries(filters):
if not filters.get("from_date"):
frappe.throw(_("'From Date' is required"))
- if filters.get("to_date"):
- conditions += " and posting_date <= '%s'" % filters["to_date"]
- else:
+ if not filters.get("to_date"):
frappe.throw(_("'To Date' is required"))
- return conditions
-
-
-def get_stock_ledger_entries(filters):
- conditions = get_conditions(filters)
- return frappe.db.sql(
- """select item_code, batch_no, warehouse,
- posting_date, actual_qty
- from `tabStock Ledger Entry`
- where is_cancelled = 0
- and docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse"""
- % conditions,
- as_dict=1,
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+ query = (
+ frappe.qb.from_(sle)
+ .select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty)
+ .where(
+ (sle.is_cancelled == 0)
+ & (sle.docstatus < 2)
+ & (IfNull(sle.batch_no, "") != "")
+ & (sle.posting_date <= filters["to_date"])
+ )
+ .orderby(sle.item_code, sle.warehouse)
)
+ return query.run(as_dict=True)
+
def get_item_warehouse_batch_map(filters, float_precision):
sle = get_stock_ledger_entries(filters)
@@ -112,7 +110,7 @@
def get_item_details(filters):
item_map = {}
- for d in frappe.db.sql("select name, item_name, description from tabItem", as_dict=1):
+ for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True):
item_map.setdefault(d.name, d)
return item_map
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 291c6b5..0d57938 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -71,6 +71,8 @@
def get_stock_ledger_entries(filters):
if not filters.get("from_date"):
frappe.throw(_("'From Date' is required"))
+ if not filters.get("to_date"):
+ frappe.throw(_("'To Date' is required"))
sle = frappe.qb.DocType("Stock Ledger Entry")
query = (
@@ -85,18 +87,13 @@
.where(
(sle.docstatus < 2)
& (sle.is_cancelled == 0)
- & (sle.batch_no.isnotnull())
- & (sle.batch_no != "")
+ & (fn.IfNull(sle.batch_no, "") != "")
+ & (sle.posting_date <= filters["to_date"])
)
.groupby(sle.voucher_no, sle.batch_no, sle.item_code, sle.warehouse)
.orderby(sle.item_code, sle.warehouse)
)
- if to_date := filters.get("to_date"):
- query = query.where(sle.posting_date <= to_date)
- else:
- frappe.throw(_("'To Date' is required"))
-
query = apply_warehouse_filter(query, sle, filters)
for field in ["item_code", "batch_no", "company"]:
if filters.get(field):
@@ -136,7 +133,9 @@
def get_item_details(filters):
item_map = {}
- for d in frappe.db.sql("select name, item_name, description, stock_uom from tabItem", as_dict=1):
+ for d in (frappe.qb.from_("Item").select("name", "item_name", "description", "stock_uom")).run(
+ as_dict=1
+ ):
item_map.setdefault(d.name, d)
return item_map
diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
index 9df24d6..546a834 100644
--- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py
+++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py
@@ -21,56 +21,54 @@
return self.get_columns(), self.get_data() or []
def get_data(self, consolidated=False):
- conditions = ""
-
doctype = self.filters.get("based_on")
- child_doc = "%s Item" % doctype
+ sales_order_field = "sales_order" if doctype == "Sales Invoice" else "against_sales_order"
+
+ parent = frappe.qb.DocType(doctype)
+ child = frappe.qb.DocType(f"{doctype} Item")
+
+ query = (
+ frappe.qb.from_(child)
+ .from_(parent)
+ .select(
+ child.item_code,
+ child.item_name,
+ child.item_group,
+ child.qty,
+ child.rate,
+ child.amount,
+ child.so_detail,
+ child[sales_order_field].as_("sales_order"),
+ parent.shipping_address_name,
+ parent.po_no,
+ parent.customer,
+ parent.posting_date,
+ parent.name,
+ parent.grand_total,
+ )
+ .where(
+ (child.parent == parent.name)
+ & (parent.docstatus == 1)
+ & (parent.posting_date.between(self.filters.get("from_date"), self.filters.get("to_date")))
+ & (child[sales_order_field].notnull())
+ & (child[sales_order_field] != "")
+ )
+ )
if doctype == "Sales Invoice":
- conditions = " and `tabSales Invoice`.update_stock = 1 and `tabSales Invoice`.is_pos = 0"
+ query = query.where((parent.update_stock == 1) & (parent.is_pos == 0))
if self.filters.get("item_group"):
- conditions += " and `tab%s`.item_group = %s" % (
- child_doc,
- frappe.db.escape(self.filters.get("item_group")),
- )
-
- for field in ["customer", "customer_group", "company"]:
- if self.filters.get(field):
- conditions += " and `tab%s`.%s = %s" % (
- doctype,
- field,
- frappe.db.escape(self.filters.get(field)),
- )
-
- sales_order_field = "against_sales_order"
- if doctype == "Sales Invoice":
- sales_order_field = "sales_order"
+ query = query.where(child.item_group == self.filters.get("item_group"))
if self.filters.get("sales_order"):
- conditions = " and `tab%s`.%s = '%s'" % (
- child_doc,
- sales_order_field,
- self.filters.get("sales_order"),
- )
+ query = query.where(child[sales_order_field] == self.filters.get("sales_order"))
- self.transactions = frappe.db.sql(
- """ SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name,
- `tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount,
- `tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order,
- `tab{doctype}`.shipping_address_name, `tab{doctype}`.po_no, `tab{doctype}`.customer,
- `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total
- FROM `tab{child_doc}`, `tab{doctype}`
- WHERE
- `tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and
- `tab{doctype}`.posting_date between %(from_date)s and %(to_date)s and
- `tab{child_doc}`.{so_field} is not null and `tab{child_doc}`.{so_field} != '' {cond}
- """.format(
- cond=conditions, doctype=doctype, child_doc=child_doc, so_field=sales_order_field
- ),
- {"from_date": self.filters.get("from_date"), "to_date": self.filters.get("to_date")},
- as_dict=1,
- )
+ for field in ("customer", "customer_group", "company"):
+ if self.filters.get(field):
+ query = query.where(parent[field] == self.filters.get(field))
+
+ self.transactions = query.run(as_dict=True)
if self.transactions:
self.filter_transactions_data(consolidated)
diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py
index 87f1a42..ab47b4a 100644
--- a/erpnext/stock/report/item_prices/item_prices.py
+++ b/erpnext/stock/report/item_prices/item_prices.py
@@ -4,6 +4,7 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import IfNull, Sum
from frappe.utils import flt
@@ -12,8 +13,7 @@
filters = {}
columns = get_columns(filters)
- conditions = get_condition(filters)
- item_map = get_item_details(conditions)
+ item_map = get_item_details(filters)
pl = get_price_list()
last_purchase_rate = get_last_purchase_rate()
bom_rate = get_item_bom_rate()
@@ -63,18 +63,24 @@
return columns
-def get_item_details(conditions):
+def get_item_details(filters):
"""returns all items details"""
item_map = {}
- for i in frappe.db.sql(
- """select name, item_group, item_name, description,
- brand, stock_uom from tabItem %s
- order by item_code, item_group"""
- % (conditions),
- as_dict=1,
- ):
+ item = frappe.qb.DocType("Item")
+ query = (
+ frappe.qb.from_(item)
+ .select(item.name, item.item_group, item.item_name, item.description, item.brand, item.stock_uom)
+ .orderby(item.item_code, item.item_group)
+ )
+
+ if filters.get("items") == "Enabled Items only":
+ query = query.where(item.disabled == 0)
+ elif filters.get("items") == "Disabled Items only":
+ query = query.where(item.disabled == 1)
+
+ for i in query.run(as_dict=True):
item_map.setdefault(i.name, i)
return item_map
@@ -85,19 +91,38 @@
rate = {}
- price_list = frappe.db.sql(
- """select ip.item_code, ip.buying, ip.selling,
- concat(ifnull(cu.symbol,ip.currency), " ", round(ip.price_list_rate,2), " - ", ip.price_list) as price
- from `tabItem Price` ip, `tabPrice List` pl, `tabCurrency` cu
- where ip.price_list=pl.name and pl.currency=cu.name and pl.enabled=1""",
- as_dict=1,
- )
+ ip = frappe.qb.DocType("Item Price")
+ pl = frappe.qb.DocType("Price List")
+ cu = frappe.qb.DocType("Currency")
- for j in price_list:
- if j.price:
- rate.setdefault(j.item_code, {}).setdefault("Buying" if j.buying else "Selling", []).append(
- j.price
+ price_list = (
+ frappe.qb.from_(ip)
+ .from_(pl)
+ .from_(cu)
+ .select(
+ ip.item_code,
+ ip.buying,
+ ip.selling,
+ (IfNull(cu.symbol, ip.currency)).as_("currency"),
+ ip.price_list_rate,
+ ip.price_list,
+ )
+ .where((ip.price_list == pl.name) & (pl.currency == cu.name) & (pl.enabled == 1))
+ ).run(as_dict=True)
+
+ for d in price_list:
+ d.update(
+ {"price": "{0} {1} - {2}".format(d.currency, round(d.price_list_rate, 2), d.price_list)}
+ )
+ d.pop("currency")
+ d.pop("price_list_rate")
+ d.pop("price_list")
+
+ if d.price:
+ rate.setdefault(d.item_code, {}).setdefault("Buying" if d.buying else "Selling", []).append(
+ d.price
)
+
item_rate_map = {}
for item in rate:
@@ -112,30 +137,39 @@
def get_last_purchase_rate():
item_last_purchase_rate_map = {}
- query = """select * from (
- (select
- po_item.item_code,
- po.transaction_date as posting_date,
- po_item.base_rate
- from `tabPurchase Order` po, `tabPurchase Order Item` po_item
- where po.name = po_item.parent and po.docstatus = 1)
- union
- (select
- pr_item.item_code,
- pr.posting_date,
- pr_item.base_rate
- from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
- where pr.name = pr_item.parent and pr.docstatus = 1)
- union
- (select
- pi_item.item_code,
- pi.posting_date,
- pi_item.base_rate
- from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
- where pi.name = pi_item.parent and pi.docstatus = 1 and pi.update_stock = 1)
- ) result order by result.item_code asc, result.posting_date asc"""
+ po = frappe.qb.DocType("Purchase Order")
+ pr = frappe.qb.DocType("Purchase Receipt")
+ pi = frappe.qb.DocType("Purchase Invoice")
+ po_item = frappe.qb.DocType("Purchase Order Item")
+ pr_item = frappe.qb.DocType("Purchase Receipt Item")
+ pi_item = frappe.qb.DocType("Purchase Invoice Item")
- for d in frappe.db.sql(query, as_dict=1):
+ query = (
+ frappe.qb.from_(
+ (
+ frappe.qb.from_(po)
+ .from_(po_item)
+ .select(po_item.item_code, po.transaction_date.as_("posting_date"), po_item.base_rate)
+ .where((po.name == po_item.parent) & (po.docstatus == 1))
+ )
+ + (
+ frappe.qb.from_(pr)
+ .from_(pr_item)
+ .select(pr_item.item_code, pr.posting_date, pr_item.base_rate)
+ .where((pr.name == pr_item.parent) & (pr.docstatus == 1))
+ )
+ + (
+ frappe.qb.from_(pi)
+ .from_(pi_item)
+ .select(pi_item.item_code, pi.posting_date, pi_item.base_rate)
+ .where((pi.name == pi_item.parent) & (pi.docstatus == 1) & (pi.update_stock == 1))
+ )
+ )
+ .select("*")
+ .orderby("item_code", "posting_date")
+ )
+
+ for d in query.run(as_dict=True):
item_last_purchase_rate_map[d.item_code] = d.base_rate
return item_last_purchase_rate_map
@@ -146,12 +180,15 @@
item_bom_map = {}
- for b in frappe.db.sql(
- """select item, (total_cost/quantity) as bom_rate
- from `tabBOM` where is_active=1 and is_default=1""",
- as_dict=1,
- ):
- item_bom_map.setdefault(b.item, flt(b.bom_rate))
+ bom = frappe.qb.DocType("BOM")
+ bom_data = (
+ frappe.qb.from_(bom)
+ .select(bom.item, (bom.total_cost / bom.quantity).as_("bom_rate"))
+ .where((bom.is_active == 1) & (bom.is_default == 1))
+ ).run(as_dict=True)
+
+ for d in bom_data:
+ item_bom_map.setdefault(d.item, flt(d.bom_rate))
return item_bom_map
@@ -161,25 +198,17 @@
item_val_rate_map = {}
- for d in frappe.db.sql(
- """select item_code,
- sum(actual_qty*valuation_rate)/sum(actual_qty) as val_rate
- from tabBin where actual_qty > 0 group by item_code""",
- as_dict=1,
- ):
+ bin = frappe.qb.DocType("Bin")
+ bin_data = (
+ frappe.qb.from_(bin)
+ .select(
+ bin.item_code, Sum(bin.actual_qty * bin.valuation_rate) / Sum(bin.actual_qty).as_("val_rate")
+ )
+ .where(bin.actual_qty > 0)
+ .groupby(bin.item_code)
+ ).run(as_dict=True)
+
+ for d in bin_data:
item_val_rate_map.setdefault(d.item_code, d.val_rate)
return item_val_rate_map
-
-
-def get_condition(filters):
- """Get Filter Items"""
-
- if filters.get("items") == "Enabled Items only":
- conditions = " where disabled=0 "
- elif filters.get("items") == "Disabled Items only":
- conditions = " where disabled=1 "
- else:
- conditions = ""
-
- return conditions
diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
index 49e797d..f477d8f 100644
--- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -5,6 +5,7 @@
import frappe
from frappe import _
from frappe.utils import flt, today
+from pypika.terms import ExistsCriterion
from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_pos_reserved_qty
from erpnext.stock.utils import (
@@ -218,10 +219,26 @@
def get_bin_list(filters):
- conditions = []
+ bin = frappe.qb.DocType("Bin")
+ query = (
+ frappe.qb.from_(bin)
+ .select(
+ bin.item_code,
+ bin.warehouse,
+ bin.actual_qty,
+ bin.planned_qty,
+ bin.indented_qty,
+ bin.ordered_qty,
+ bin.reserved_qty,
+ bin.reserved_qty_for_production,
+ bin.reserved_qty_for_sub_contract,
+ bin.projected_qty,
+ )
+ .orderby(bin.item_code, bin.warehouse)
+ )
if filters.item_code:
- conditions.append("item_code = '%s' " % filters.item_code)
+ query = query.where(bin.item_code == filters.item_code)
if filters.warehouse:
warehouse_details = frappe.db.get_value(
@@ -229,21 +246,20 @@
)
if warehouse_details:
- conditions.append(
- " exists (select name from `tabWarehouse` wh \
- where wh.lft >= %s and wh.rgt <= %s and bin.warehouse = wh.name)"
- % (warehouse_details.lft, warehouse_details.rgt)
+ wh = frappe.qb.DocType("Warehouse")
+ query = query.where(
+ ExistsCriterion(
+ frappe.qb.from_(wh)
+ .select(wh.name)
+ .where(
+ (wh.lft >= warehouse_details.lft)
+ & (wh.rgt <= warehouse_details.rgt)
+ & (bin.warehouse == wh.name)
+ )
+ )
)
- bin_list = frappe.db.sql(
- """select item_code, warehouse, actual_qty, planned_qty, indented_qty,
- ordered_qty, reserved_qty, reserved_qty_for_production, reserved_qty_for_sub_contract, projected_qty
- from tabBin bin {conditions} order by item_code, warehouse
- """.format(
- conditions=" where " + " and ".join(conditions) if conditions else ""
- ),
- as_dict=1,
- )
+ bin_list = query.run(as_dict=True)
return bin_list
@@ -251,45 +267,43 @@
def get_item_map(item_code, include_uom):
"""Optimization: get only the item doc and re_order_levels table"""
- condition = ""
- if item_code:
- condition = "and item_code = {0}".format(frappe.db.escape(item_code, percent=False))
+ bin = frappe.qb.DocType("Bin")
+ item = frappe.qb.DocType("Item")
- cf_field = cf_join = ""
- if include_uom:
- cf_field = ", ucd.conversion_factor"
- cf_join = (
- "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%(include_uom)s"
+ query = (
+ frappe.qb.from_(item)
+ .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom)
+ .where(
+ (item.is_stock_item == 1)
+ & (item.disabled == 0)
+ & (
+ (item.end_of_life > today()) | (item.end_of_life.isnull()) | (item.end_of_life == "0000-00-00")
+ )
+ & (ExistsCriterion(frappe.qb.from_(bin).select(bin.name).where(bin.item_code == item.name)))
)
-
- items = frappe.db.sql(
- """
- select item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom{cf_field}
- from `tabItem` item
- {cf_join}
- where item.is_stock_item = 1
- and item.disabled=0
- {condition}
- and (item.end_of_life > %(today)s or item.end_of_life is null or item.end_of_life='0000-00-00')
- and exists (select name from `tabBin` bin where bin.item_code=item.name)""".format(
- cf_field=cf_field, cf_join=cf_join, condition=condition
- ),
- {"today": today(), "include_uom": include_uom},
- as_dict=True,
)
- condition = ""
if item_code:
- condition = "where parent={0}".format(frappe.db.escape(item_code, percent=False))
+ query = query.where(item.item_code == item_code)
+
+ if include_uom:
+ ucd = frappe.qb.DocType("UOM Conversion Detail")
+ query = query.left_join(ucd).on((ucd.parent == item.name) & (ucd.uom == include_uom))
+
+ items = query.run(as_dict=True)
+
+ ir = frappe.qb.DocType("Item Reorder")
+ query = frappe.qb.from_(ir).select("*")
+
+ if item_code:
+ query = query.where(ir.parent == item_code)
reorder_levels = frappe._dict()
- for ir in frappe.db.sql(
- """select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1
- ):
- if ir.parent not in reorder_levels:
- reorder_levels[ir.parent] = []
+ for d in query.run(as_dict=True):
+ if d.parent not in reorder_levels:
+ reorder_levels[d.parent] = []
- reorder_levels[ir.parent].append(ir)
+ reorder_levels[d.parent].append(d)
item_map = frappe._dict()
for item in items:
diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
index 5430fe6..8c76908 100644
--- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
+++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py
@@ -4,6 +4,7 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import IfNull
from frappe.utils import flt
@@ -70,31 +71,33 @@
return columns
-def get_conditions(filters):
- conditions = ""
- values = []
+def get_consumed_details(filters):
+ item = frappe.qb.DocType("Item")
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+
+ query = (
+ frappe.qb.from_(sle)
+ .from_(item)
+ .select(
+ sle.item_code,
+ item.item_name,
+ item.description,
+ item.stock_uom,
+ sle.actual_qty,
+ sle.stock_value_difference,
+ sle.voucher_no,
+ sle.voucher_type,
+ )
+ .where((sle.is_cancelled == 0) & (sle.item_code == item.name) & (sle.actual_qty < 0))
+ )
if filters.get("from_date") and filters.get("to_date"):
- conditions = "and sle.posting_date>=%s and sle.posting_date<=%s"
- values = [filters.get("from_date"), filters.get("to_date")]
+ query = query.where(
+ (sle.posting_date >= filters.get("from_date")) & (sle.posting_date <= filters.get("to_date"))
+ )
- return conditions, values
-
-
-def get_consumed_details(filters):
- conditions, values = get_conditions(filters)
consumed_details = {}
-
- for d in frappe.db.sql(
- """select sle.item_code, i.item_name, i.description,
- i.stock_uom, sle.actual_qty, sle.stock_value_difference,
- sle.voucher_no, sle.voucher_type
- from `tabStock Ledger Entry` sle, `tabItem` i
- where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s"""
- % conditions,
- values,
- as_dict=1,
- ):
+ for d in query.run(as_dict=True):
consumed_details.setdefault(d.item_code, []).append(d)
return consumed_details
@@ -104,24 +107,54 @@
item_supplier_map = {}
supplier = filters.get("supplier")
- for d in frappe.db.sql(
- """select pr.supplier, pri.item_code from
- `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
- where pr.name=pri.parent and pr.docstatus=1 and
- pri.item_code=(select name from `tabItem` where
- is_stock_item=1 and name=pri.item_code)""",
- as_dict=1,
- ):
+ item = frappe.qb.DocType("Item")
+ pr = frappe.qb.DocType("Purchase Receipt")
+ pr_item = frappe.qb.DocType("Purchase Receipt Item")
+
+ query = (
+ frappe.qb.from_(pr)
+ .from_(pr_item)
+ .select(pr.supplier, pr_item.item_code)
+ .where(
+ (pr.name == pr_item.parent)
+ & (pr.docstatus == 1)
+ & (
+ pr_item.item_code
+ == (
+ frappe.qb.from_(item)
+ .select(item.name)
+ .where((item.is_stock_item == 1) & (item.name == pr_item.item_code))
+ )
+ )
+ )
+ )
+
+ for d in query.run(as_dict=True):
item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
- for d in frappe.db.sql(
- """select pr.supplier, pri.item_code from
- `tabPurchase Invoice` pr, `tabPurchase Invoice Item` pri
- where pr.name=pri.parent and pr.docstatus=1 and
- ifnull(pr.update_stock, 0) = 1 and pri.item_code=(select name from `tabItem`
- where is_stock_item=1 and name=pri.item_code)""",
- as_dict=1,
- ):
+ pi = frappe.qb.DocType("Purchase Invoice")
+ pi_item = frappe.qb.DocType("Purchase Invoice Item")
+
+ query = (
+ frappe.qb.from_(pi)
+ .from_(pi_item)
+ .select(pi.supplier, pi_item.item_code)
+ .where(
+ (pi.name == pi_item.parent)
+ & (pi.docstatus == 1)
+ & (IfNull(pi.update_stock, 0) == 1)
+ & (
+ pi_item.item_code
+ == (
+ frappe.qb.from_(item)
+ .select(item.name)
+ .where((item.is_stock_item == 1) & (item.name == pi_item.item_code))
+ )
+ )
+ )
+ )
+
+ for d in query.run(as_dict=True):
if d.item_code not in item_supplier_map:
item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
@@ -138,7 +171,11 @@
def get_material_transfer_vouchers():
- return frappe.db.sql_list(
- """select name from `tabStock Entry` where
- purpose='Material Transfer' and docstatus=1"""
+ se = frappe.qb.DocType("Stock Entry")
+ query = (
+ frappe.qb.from_(se)
+ .select(se.name)
+ .where((se.purpose == "Material Transfer") & (se.docstatus == 1))
)
+
+ return [r[0] for r in query.run()]
diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
index 21529da..c3155bd 100644
--- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py
+++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py
@@ -4,60 +4,58 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import Sum
def execute(filters=None):
if not filters:
filters = {}
- columns = get_columns()
+ columns = get_columns(filters)
stock = get_total_stock(filters)
return columns, stock
-def get_columns():
+def get_columns(filters):
columns = [
- _("Company") + ":Link/Company:250",
- _("Warehouse") + ":Link/Warehouse:150",
_("Item") + ":Link/Item:150",
_("Description") + "::300",
_("Current Qty") + ":Float:100",
]
+ if filters.get("group_by") == "Warehouse":
+ columns.insert(0, _("Warehouse") + ":Link/Warehouse:150")
+ else:
+ columns.insert(0, _("Company") + ":Link/Company:250")
+
return columns
def get_total_stock(filters):
- conditions = ""
- columns = ""
+ bin = frappe.qb.DocType("Bin")
+ item = frappe.qb.DocType("Item")
+ wh = frappe.qb.DocType("Warehouse")
+
+ query = (
+ frappe.qb.from_(bin)
+ .inner_join(item)
+ .on(bin.item_code == item.item_code)
+ .inner_join(wh)
+ .on(wh.name == bin.warehouse)
+ .where(bin.actual_qty != 0)
+ )
if filters.get("group_by") == "Warehouse":
if filters.get("company"):
- conditions += " AND warehouse.company = %s" % frappe.db.escape(
- filters.get("company"), percent=False
- )
+ query = query.where(wh.company == filters.get("company"))
- conditions += " GROUP BY ledger.warehouse, item.item_code"
- columns += "'' as company, ledger.warehouse"
+ query = query.select(bin.warehouse).groupby(bin.warehouse)
else:
- conditions += " GROUP BY warehouse.company, item.item_code"
- columns += " warehouse.company, '' as warehouse"
+ query = query.select(wh.company).groupby(wh.company)
- return frappe.db.sql(
- """
- SELECT
- %s,
- item.item_code,
- item.description,
- sum(ledger.actual_qty) as actual_qty
- FROM
- `tabBin` AS ledger
- INNER JOIN `tabItem` AS item
- ON ledger.item_code = item.item_code
- INNER JOIN `tabWarehouse` warehouse
- ON warehouse.name = ledger.warehouse
- WHERE
- ledger.actual_qty != 0 %s"""
- % (columns, conditions)
- )
+ query = query.select(
+ item.item_code, item.description, Sum(bin.actual_qty).as_("actual_qty")
+ ).groupby(item.item_code)
+
+ return query.run()
diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
index a54373f..eedf1a0 100644
--- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
+++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
@@ -7,6 +7,7 @@
import frappe
from frappe import _
+from frappe.query_builder.functions import Count
from frappe.utils import flt
from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age
@@ -98,7 +99,7 @@
def validate_filters(filters):
if not (filters.get("item_code") or filters.get("warehouse")):
- sle_count = flt(frappe.db.sql("""select count(name) from `tabStock Ledger Entry`""")[0][0])
+ sle_count = flt(frappe.qb.from_("Stock Ledger Entry").select(Count("name")).run()[0][0])
if sle_count > 500000:
frappe.throw(_("Please set filter based on Item or Warehouse"))
if not filters.get("company"):
@@ -108,25 +109,16 @@
def get_warehouse_list(filters):
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
- condition = ""
- user_permitted_warehouse = get_permitted_documents("Warehouse")
- value = ()
- if user_permitted_warehouse:
- condition = "and name in %s"
- value = set(user_permitted_warehouse)
- elif not user_permitted_warehouse and filters.get("warehouse"):
- condition = "and name = %s"
- value = filters.get("warehouse")
+ wh = frappe.qb.DocType("Warehouse")
+ query = frappe.qb.from_(wh).select(wh.name).where(wh.is_group == 0)
- return frappe.db.sql(
- """select name
- from `tabWarehouse` where is_group = 0
- {condition}""".format(
- condition=condition
- ),
- value,
- as_dict=1,
- )
+ user_permitted_warehouse = get_permitted_documents("Warehouse")
+ if user_permitted_warehouse:
+ query = query.where(wh.name.isin(set(user_permitted_warehouse)))
+ elif filters.get("warehouse"):
+ query = query.where(wh.name == filters.get("warehouse"))
+
+ return query.run(as_dict=True)
def add_warehouse_column(columns, warehouse_list):