diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py index f1ffee6bec8..96079284dd9 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py @@ -3,6 +3,8 @@ import frappe +from frappe.query_builder import functions +from frappe.query_builder.utils import DocType from frappe.tests import IntegrationTestCase from frappe.utils import add_days, flt, today @@ -81,10 +83,11 @@ class TestExchangeRateRevaluation(AccountsTestMixin, IntegrationTestCase): self.assertEqual(je.total_debit, 8500.0) self.assertEqual(je.total_credit, 8500.0) + gl = DocType("GL Entry") acc_balance = frappe.db.get_all( "GL Entry", filters={"account": self.debtors_usd, "is_cancelled": 0}, - fields=["sum(debit)-sum(credit) as balance"], + fields=[(functions.Sum(gl.debit) - functions.Sum(gl.credit)).as_("balance")], )[0] self.assertEqual(acc_balance.balance, 8500.0) @@ -146,12 +149,15 @@ class TestExchangeRateRevaluation(AccountsTestMixin, IntegrationTestCase): self.assertEqual(je.total_debit, 500.0) self.assertEqual(je.total_credit, 500.0) + gl = DocType("GL Entry") acc_balance = frappe.db.get_all( "GL Entry", filters={"account": self.debtors_usd, "is_cancelled": 0}, fields=[ - "sum(debit)-sum(credit) as balance", - "sum(debit_in_account_currency)-sum(credit_in_account_currency) as balance_in_account_currency", + (functions.Sum(gl.debit) - functions.Sum(gl.credit)).as_("balance"), + ( + functions.Sum(gl.debit_in_account_currency) - functions.Sum(gl.credit_in_account_currency) + ).as_("balance_in_account_currency"), ], )[0] # account shouldn't have balance in base and account currency @@ -193,12 +199,15 @@ class TestExchangeRateRevaluation(AccountsTestMixin, IntegrationTestCase): pe.references = [] pe.save().submit() + gl = DocType("GL Entry") acc_balance = frappe.db.get_all( "GL Entry", filters={"account": self.debtors_usd, "is_cancelled": 0}, fields=[ - "sum(debit)-sum(credit) as balance", - "sum(debit_in_account_currency)-sum(credit_in_account_currency) as balance_in_account_currency", + (functions.Sum(gl.debit) - functions.Sum(gl.credit)).as_("balance"), + ( + functions.Sum(gl.debit_in_account_currency) - functions.Sum(gl.credit_in_account_currency) + ).as_("balance_in_account_currency"), ], )[0] # account should have balance only in account currency @@ -235,12 +244,15 @@ class TestExchangeRateRevaluation(AccountsTestMixin, IntegrationTestCase): self.assertEqual(flt(je.total_debit, precision), 0.0) self.assertEqual(flt(je.total_credit, precision), 0.0) + gl = DocType("GL Entry") acc_balance = frappe.db.get_all( "GL Entry", filters={"account": self.debtors_usd, "is_cancelled": 0}, fields=[ - "sum(debit)-sum(credit) as balance", - "sum(debit_in_account_currency)-sum(credit_in_account_currency) as balance_in_account_currency", + (functions.Sum(gl.debit) - functions.Sum(gl.credit)).as_("balance"), + ( + functions.Sum(gl.debit_in_account_currency) - functions.Sum(gl.credit_in_account_currency) + ).as_("balance_in_account_currency"), ], )[0] # account shouldn't have balance in base and account currency post revaluation diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index bab64406bd0..7555e6d957f 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -72,7 +72,7 @@ class OpeningInvoiceCreationTool(Document): fields = [ "company", {"COUNT": "*", "as": "total_invoices"}, - "sum(outstanding_amount) as outstanding_amount", + {"SUM": "outstanding_amount", "as": "outstanding_amount"}, ] companies = frappe.get_all("Company", fields=["name as company", "default_currency as currency"]) if not companies: diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 99433b3fe7e..4d0888f078e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -669,7 +669,7 @@ class PaymentReconciliation(Document): "party": self.party, }, fields=[ - "parent as `name`", + "parent as name", "exchange_rate", ], as_list=1, diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index cb23d27df44..b11f20ec90b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -975,7 +975,7 @@ class TestPaymentReconciliation(IntegrationTestCase): total_credit_amount = frappe.db.get_all( "Journal Entry Account", {"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name}, - "sum(credit) as amount", + [{"SUM": "credit", "as": "amount"}], group_by="reference_name", )[0].amount @@ -1069,7 +1069,7 @@ class TestPaymentReconciliation(IntegrationTestCase): total_credit_amount = frappe.db.get_all( "Journal Entry Account", {"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name}, - "sum(credit) as amount", + [{"SUM": "credit", "as": "amount"}], group_by="reference_name", )[0].amount diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 685759dd1f3..378d349e83d 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -713,6 +713,7 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters): return frappe.get_all( "UOM Conversion Detail", filters={"parent": ("in", items), "uom": ("like", f"{txt}%")}, - fields=["distinct uom"], + fields=["uom"], as_list=1, + distinct=True, ) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index cd63a3f757d..c6324c10373 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1374,7 +1374,7 @@ class TestPurchaseInvoice(IntegrationTestCase, StockTestMixin): total_debit_amount = frappe.db.get_all( "Journal Entry Account", {"account": creditors_account, "docstatus": 1, "reference_name": pi.name}, - "sum(debit) as amount", + [{"SUM": "debit", "as": "amount"}], group_by="reference_name", )[0].amount self.assertEqual(flt(total_debit_amount, 2), 2500) @@ -1456,7 +1456,7 @@ class TestPurchaseInvoice(IntegrationTestCase, StockTestMixin): total_debit_amount = frappe.db.get_all( "Journal Entry Account", {"account": creditors_account, "docstatus": 1, "reference_name": pi_2.name}, - "sum(debit) as amount", + [{"SUM": "debit", "as": "amount"}], group_by="reference_name", )[0].amount self.assertEqual(flt(total_debit_amount, 2), 1500) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index c656cd66bfd..d5755cb3719 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -213,7 +213,10 @@ def get_allowed_types_from_settings(child_doc: bool = False): repost_docs = [ x.document_type for x in frappe.db.get_all( - "Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"] + "Repost Allowed Types", + filters={"allowed": True}, + fields=["document_type"], + distinct=True, ) ] result = repost_docs @@ -287,7 +290,11 @@ def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters filters.update({"document_type": ("like", f"%{txt}%")}) if allowed_types := frappe.db.get_all( - "Repost Allowed Types", filters=filters, fields=["distinct(document_type)"], as_list=1 + "Repost Allowed Types", + filters=filters, + fields=["document_type"], + as_list=1, + distinct=True, ): return allowed_types return [] diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 9aa9d61951b..a2983e4555b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3612,7 +3612,7 @@ class TestSalesInvoice(ERPNextTestSuite): frappe.db.get_all( "Payment Ledger Entry", filters={"against_voucher_no": si.name, "delinked": 0}, - fields=["sum(amount), sum(amount_in_account_currency)"], + fields=[{"SUM": "amount"}, {"SUM": "amount_in_account_currency"}], as_list=1, ) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index b2b05045c3e..4aa0dccc65f 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -121,7 +121,7 @@ class TestTaxWithholdingCategory(IntegrationTestCase): gl_entries = frappe.db.get_all( "GL Entry", filters={"voucher_no": pi.name}, - fields=["account", "sum(debit) as debit", "sum(credit) as credit"], + fields=["account", {"SUM": "debit", "as": "debit"}, {"SUM": "credit", "as": "credit"}], group_by="account", ) self.assertEqual(len(gl_entries), 3) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index ffdfb2f7405..eef82a398b9 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -854,8 +854,8 @@ def get_dashboard_info(party_type, party, loyalty_program=None): group_by="company", fields=[ "company", - "sum(grand_total) as grand_total", - "sum(base_grand_total) as base_grand_total", + {"SUM": "grand_total", "as": "grand_total"}, + {"SUM": "base_grand_total", "as": "base_grand_total"}, ], ) @@ -870,7 +870,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None): "expiry_date": (">=", getdate()), }, group_by="company", - fields=["company", "sum(loyalty_points) as loyalty_points"], + fields=["company", {"SUM": "loyalty_points", "as": "loyalty_points"}], as_list=1, ) ) diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index 20351846cc4..600a782c71b 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -210,7 +210,7 @@ def get_gl_balance(report_date, company): return frappe._dict( frappe.db.get_all( "GL Entry", - fields=["party", "sum(debit - credit)"], + fields=["party", {"SUM": [{"SUB": ["debit", "credit"]}], "as": "balance"}], filters={"posting_date": ("<=", report_date), "is_cancelled": 0, "company": company}, group_by="party", as_list=1, diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 262bff7d640..5b4307f0ccf 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -323,22 +323,24 @@ def get_returned_qty_map_for_row(return_against, party, row_name, doctype): party_type = "customer" fields = [ - f"sum(abs(`tab{child_doctype}`.qty)) as qty", + {"SUM": [{"ABS": f"`tab{child_doctype}`.qty"}], "as": "qty"}, ] if doctype != "Subcontracting Receipt": fields += [ - f"sum(abs(`tab{child_doctype}`.stock_qty)) as stock_qty", + {"SUM": [{"ABS": f"`tab{child_doctype}`.stock_qty"}], "as": "stock_qty"}, ] if doctype in ("Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"): fields += [ - f"sum(abs(`tab{child_doctype}`.rejected_qty)) as rejected_qty", - f"sum(abs(`tab{child_doctype}`.received_qty)) as received_qty", + {"SUM": [{"ABS": f"`tab{child_doctype}`.rejected_qty"}], "as": "rejected_qty"}, + {"SUM": [{"ABS": f"`tab{child_doctype}`.received_qty"}], "as": "received_qty"}, ] if doctype == "Purchase Receipt": - fields += [f"sum(abs(`tab{child_doctype}`.received_stock_qty)) as received_stock_qty"] + fields += [ + {"SUM": [{"ABS": f"`tab{child_doctype}`.received_stock_qty"}], "as": "received_stock_qty"} + ] # Used retrun against and supplier and is_retrun because there is an index added for it data = frappe.get_all( diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 84a11270040..ea933b2967c 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -563,11 +563,14 @@ class StatusUpdater(Document): fields=[target_ref_field, target_field], ) - sum_ref = sum(abs(record[target_ref_field]) for record in child_records) + # For operator dicts, the alias is in the "as" key; for strings, use the field name directly + ref_key = target_ref_field.get("as") if isinstance(target_ref_field, dict) else target_ref_field + + sum_ref = sum(abs(record[ref_key]) for record in child_records) if sum_ref > 0: percentage = round( - sum(min(abs(record[target_field]), abs(record[target_ref_field])) for record in child_records) + sum(min(abs(record[target_field]), abs(record[ref_key])) for record in child_records) / sum_ref * 100, 6, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 5847d9d3adf..8d4aeffd9d5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1320,7 +1320,7 @@ class StockController(AccountsController): total_returned += flt(item.returned_qty * item.rate) if total_returned < total_amount: - target_ref_field = "(amount - (returned_qty * rate))" + target_ref_field = {"SUB": ["amount", {"MUL": ["returned_qty", "rate"]}], "as": "ref_amount"} self._update_percent_field( { diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 6848a345d9b..ff7342607c3 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -292,7 +292,7 @@ class SubcontractingController(StockController): ): for row in frappe.get_all( f"{self.subcontract_data.order_doctype} Item", - fields=["item_code", "(qty - received_qty) as qty", "parent", "name"], + fields=["item_code", {"SUB": ["qty", "received_qty"], "as": "qty"}, "parent", "name"], filters={"docstatus": 1, "parent": ("in", self.subcontract_orders)}, ): self.qty_to_be_received[(row.item_code, row.parent)] += row.qty @@ -553,7 +553,9 @@ class SubcontractingController(StockController): data = [] doctype = "BOM Item" if not exploded_item else "BOM Explosion Item" - fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"] + fields = [ + {"DIV": [f"`tab{doctype}`.`stock_qty`", "`tabBOM`.`quantity`"], "as": "qty_consumed_per_unit"} + ] alias_dict = { "item_code": "rm_item_code", diff --git a/erpnext/controllers/tests/test_item_wise_inventory_account.py b/erpnext/controllers/tests/test_item_wise_inventory_account.py index dc8a9798fe9..e8b2c1343e7 100644 --- a/erpnext/controllers/tests/test_item_wise_inventory_account.py +++ b/erpnext/controllers/tests/test_item_wise_inventory_account.py @@ -328,7 +328,7 @@ class TestItemWiseInventoryAccount(IntegrationTestCase): "voucher_no": pr.name, "item_code": ("in", items), }, - fields=["sum(stock_value_difference) as value"], + fields=[{"SUM": "stock_value_difference", "as": "value"}], ) gl_value = frappe.db.get_value( @@ -435,7 +435,7 @@ class TestItemWiseInventoryAccount(IntegrationTestCase): sle_value = frappe.get_all( "Stock Ledger Entry", filters={"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": ("in", items)}, - fields=["sum(stock_value_difference) as value"], + fields=[{"SUM": "stock_value_difference", "as": "value"}], ) gl_value = ( diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py index 0bb050a6fe2..47e21d98cb3 100644 --- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py +++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py @@ -8,6 +8,7 @@ from itertools import groupby import frappe from dateutil.relativedelta import relativedelta from frappe import _ +from frappe.query_builder.custom import Month, MonthName, Quarter from frappe.utils import cint, flt, getdate from erpnext.setup.utils import get_exchange_rate @@ -82,40 +83,52 @@ class SalesPipelineAnalytics: self.filters.get("pipeline_by") ] - self.group_by_period = { - "Monthly": "month(expected_closing)", - "Quarterly": "QUARTER(expected_closing)", - }[self.filters.get("range")] + opp = frappe.qb.DocType("Opportunity") + + if self.filters.get("range") == "Monthly": + self.group_by_period = Month(opp.expected_closing) + self.duration = MonthName(opp.expected_closing).as_("month") + else: + self.group_by_period = Quarter(opp.expected_closing) + self.duration = Quarter(opp.expected_closing).as_("quarter") self.pipeline_by = {"Owner": "opportunity_owner", "Sales Stage": "sales_stage"}[ self.filters.get("pipeline_by") ] - self.duration = { - "Monthly": "monthname(expected_closing) as month", - "Quarterly": "QUARTER(expected_closing) as quarter", - }[self.filters.get("range")] - self.period_by = {"Monthly": "month", "Quarterly": "quarter"}[self.filters.get("range")] def get_data(self): self.get_fields() + opp = frappe.qb.DocType("Opportunity") + query = frappe.qb.get_query( + "Opportunity", + filters=self.get_conditions(), + ignore_permissions=True, + ) + + pipeline_field = opp._assign if self.group_by_based_on == "_assign" else opp.sales_stage + if self.filters.get("based_on") == "Number": - self.query_result = frappe.db.get_list( - "Opportunity", - filters=self.get_conditions(), - fields=[self.based_on, self.data_based_on, self.duration], - group_by=f"{self.group_by_based_on},{self.group_by_period}", - order_by=self.group_by_period, + self.query_result = ( + query.select( + pipeline_field.as_(self.pipeline_by), + frappe.query_builder.functions.Count("*").as_("count"), + self.duration, + ) + .groupby(pipeline_field, self.group_by_period) + .orderby(self.group_by_period) + .run(as_dict=True) ) if self.filters.get("based_on") == "Amount": - self.query_result = frappe.db.get_list( - "Opportunity", - filters=self.get_conditions(), - fields=[self.based_on, self.data_based_on, self.duration, "currency"], - ) + self.query_result = query.select( + pipeline_field.as_(self.pipeline_by), + opp.opportunity_amount.as_("amount"), + self.duration, + opp.currency, + ).run(as_dict=True) self.convert_to_base_currency() diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 8a7e8d4571f..46c73199fbd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1740,8 +1740,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): barcodes = frappe.get_all( "Item Barcode", - fields=["distinct parent as item_code"], + fields=["parent as item_code"], filters={"barcode": ("like", f"%{txt}%")}, + distinct=True, ) barcodes = [d.item_code for d in barcodes] @@ -1751,11 +1752,11 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): if filters and filters.get("item_code"): has_variants = frappe.get_cached_value("Item", filters.get("item_code"), "has_variants") if not has_variants: - query_filters["has_variants"] = 0 + query_filters.append(["has_variants", "=", 0]) if filters: for fieldname, value in filters.items(): - query_filters[fieldname] = value + query_filters.append([fieldname, "=", value]) return frappe.get_list( "Item", diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 1bd0cc4a58f..e2d44c150bb 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -207,7 +207,7 @@ class JobCard(Document): job_card_qty = frappe.get_all( "Job Card", - fields=["sum(for_quantity)"], + fields=[{"SUM": "for_quantity"}], filters={ "work_order": self.work_order, "operation_id": self.operation_id, @@ -933,9 +933,9 @@ class JobCard(Document): return frappe.get_all( "Job Card", fields=[ - "sum(total_time_in_mins) as time_in_mins", - "sum(total_completed_qty) as completed_qty", - "sum(process_loss_qty) as process_loss_qty", + {"SUM": "total_time_in_mins", "as": "time_in_mins"}, + {"SUM": "total_completed_qty", "as": "completed_qty"}, + {"SUM": "process_loss_qty", "as": "process_loss_qty"}, ], filters={ "docstatus": 1, @@ -1423,11 +1423,12 @@ def get_operations(doctype, txt, searchfield, start, page_len, filters): return frappe.get_all( "Work Order Operation", filters=args, - fields=["distinct operation as operation"], + fields=["operation"], limit_start=start, limit_page_length=page_len, order_by="idx asc", as_list=1, + distinct=True, ) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index ff737dff630..85a5b79efd2 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -624,7 +624,7 @@ class ProductionPlan(Document): so_wise_planned_qty = frappe._dict() data = frappe.get_all( "Production Plan Item", - fields=["sales_order", "sales_order_item", "SUM(planned_qty) as qty"], + fields=["sales_order", "sales_order_item", {"SUM": "planned_qty", "as": "qty"}], filters={ "sales_order": ("in", sales_orders), "docstatus": 1, diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 841a1e42b22..22738c5f639 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -73,9 +73,10 @@ class TestProductionPlan(IntegrationTestCase): material_requests = frappe.get_all( "Material Request Item", - fields=["distinct parent"], + fields=["parent"], filters={"production_plan": pln.name}, as_list=1, + distinct=True, ) self.assertTrue(len(material_requests), 2) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 282da4775f6..8c22611b461 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -976,8 +976,9 @@ class TestWorkOrder(IntegrationTestCase): job_cards = frappe.get_all( "Job Card Time Log", - fields=["distinct parent as name", "docstatus"], + fields=["parent as name", "docstatus"], order_by="creation asc", + distinct=True, ) for job_card in job_cards: diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 6cf512f7288..2a987313345 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -166,7 +166,7 @@ class WorkOrder(Document): operation_details = frappe._dict( frappe.get_all( "Job Card", - fields=["operation", "sum(for_quantity)"], + fields=["operation", {"SUM": "for_quantity"}], filters={"docstatus": ("<", 2), "work_order": self.name}, as_list=1, group_by="operation_id", @@ -718,7 +718,7 @@ class WorkOrder(Document): if self.production_plan_item: total_qty = frappe.get_all( "Work Order", - fields="sum(produced_qty) as produced_qty", + fields=[{"SUM": "produced_qty", "as": "produced_qty"}], filters={ "docstatus": 1, "production_plan": self.production_plan, @@ -1347,7 +1347,7 @@ class WorkOrder(Document): else: data = frappe.get_all( "Stock Entry", - fields=["timestamp(posting_date, posting_time) as posting_datetime"], + fields=[{"TIMESTAMP": ["posting_date", "posting_time"], "as": "posting_datetime"}], filters={ "work_order": self.name, "purpose": ("in", ["Material Transfer for Manufacture", "Manufacture"]), diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index 8d72ef1f36f..b1b0bf5dd82 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -59,7 +59,7 @@ def get_data(filters): job_card_time_details = {} for job_card_data in frappe.get_all( "Job Card Time Log", - fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"], + fields=[{"MIN": "from_time", "as": "from_time"}, {"MAX": "to_time", "as": "to_time"}, "parent"], filters=job_card_time_filter, group_by="parent", ): diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py index 9867db0dd1c..7130c2c63ea 100644 --- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py @@ -230,7 +230,12 @@ class ProductionPlanReport: purchased_items = frappe.get_all( "Purchase Order Item", - fields=["item_code", "min(schedule_date) as arrival_date", "qty as arrival_qty", "warehouse"], + fields=[ + "item_code", + {"MIN": "schedule_date", "as": "arrival_date"}, + "qty as arrival_qty", + "warehouse", + ], filters={ "item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses), diff --git a/erpnext/patches/v11_0/make_quality_inspection_template.py b/erpnext/patches/v11_0/make_quality_inspection_template.py index deebfa88e6e..fef31dcde5a 100644 --- a/erpnext/patches/v11_0/make_quality_inspection_template.py +++ b/erpnext/patches/v11_0/make_quality_inspection_template.py @@ -10,7 +10,7 @@ def execute(): frappe.reload_doc("stock", "doctype", "item") for data in frappe.get_all( - "Item Quality Inspection Parameter", fields=["distinct parent"], filters={"parenttype": "Item"} + "Item Quality Inspection Parameter", fields=["parent"], filters={"parenttype": "Item"}, distinct=True ): qc_doc = frappe.new_doc("Quality Inspection Template") qc_doc.quality_inspection_template_name = "QIT/%s" % data.parent diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index d377281f0ba..b529e3df7a7 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -401,8 +401,6 @@ def get_project_list(doctype, txt, filters, limit_start, limit_page_length=20, o meta = frappe.get_meta(doctype) - fields = "distinct *" - or_filters = [] if txt: @@ -424,13 +422,14 @@ def get_project_list(doctype, txt, filters, limit_start, limit_page_length=20, o return frappe.get_list( doctype, - fields=fields, + fields="*", filters=filters, or_filters=or_filters, limit_start=limit_start, limit_page_length=limit_page_length, order_by=order_by, ignore_permissions=ignore_permissions, + distinct=True, ) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 7cf86adbe01..fa4b2dc6693 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -179,7 +179,11 @@ def get_reverse_charge_total(filters): try: return ( frappe.db.get_all( - "Purchase Invoice", filters=query_filters, fields=["sum(base_total)"], as_list=True, limit=1 + "Purchase Invoice", + filters=query_filters, + fields=[{"SUM": "base_total"}], + as_list=True, + limit=1, )[0][0] or 0 ) @@ -219,7 +223,11 @@ def get_reverse_charge_recoverable_total(filters): try: return ( frappe.db.get_all( - "Purchase Invoice", filters=query_filters, fields=["sum(base_total)"], as_list=True, limit=1 + "Purchase Invoice", + filters=query_filters, + fields=[{"SUM": "base_total"}], + as_list=True, + limit=1, )[0][0] or 0 ) @@ -274,7 +282,11 @@ def get_standard_rated_expenses_total(filters): try: return ( frappe.db.get_all( - "Purchase Invoice", filters=query_filters, fields=["sum(base_total)"], as_list=True, limit=1 + "Purchase Invoice", + filters=query_filters, + fields=[{"SUM": "base_total"}], + as_list=True, + limit=1, )[0][0] or 0 ) @@ -292,7 +304,7 @@ def get_standard_rated_expenses_tax(filters): frappe.db.get_all( "Purchase Invoice", filters=query_filters, - fields=["sum(recoverable_standard_rated_expenses)"], + fields=[{"SUM": "recoverable_standard_rated_expenses"}], as_list=True, limit=1, )[0][0] @@ -310,7 +322,7 @@ def get_tourist_tax_return_total(filters): try: return ( frappe.db.get_all( - "Sales Invoice", filters=query_filters, fields=["sum(base_total)"], as_list=True, limit=1 + "Sales Invoice", filters=query_filters, fields=[{"SUM": "base_total"}], as_list=True, limit=1 )[0][0] or 0 ) @@ -328,7 +340,7 @@ def get_tourist_tax_return_tax(filters): frappe.db.get_all( "Sales Invoice", filters=query_filters, - fields=["sum(tourist_tax_return)"], + fields=[{"SUM": "tourist_tax_return"}], as_list=True, limit=1, )[0][0] diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 4fc2dac10e8..2af62cfb181 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -14,6 +14,7 @@ from frappe.contacts.address_and_contact import ( from frappe.model.mapper import get_mapped_doc from frappe.model.naming import set_name_by_naming_series, set_name_from_naming_options from frappe.model.utils.rename_doc import update_linked_doctypes +from frappe.query_builder import Field, functions from frappe.utils import cint, cstr, flt, get_formatted_email, today from frappe.utils.user import get_users_with_role @@ -503,11 +504,11 @@ def get_loyalty_programs(doc): loyalty_programs = frappe.get_all( "Loyalty Program", fields=["name", "customer_group", "customer_territory"], - filters={ - "auto_opt_in": 1, - "from_date": ["<=", today()], - "ifnull(to_date, '2500-01-01')": [">=", today()], - }, + filters=[ + ["auto_opt_in", "=", 1], + ["from_date", "<=", today()], + [functions.IfNull(Field("to_date"), "2500-01-01"), ">=", today()], + ], ) for loyalty_program in loyalty_programs: diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index e894e36b8ca..8ca6688852f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -630,7 +630,7 @@ def get_ordered_items(quotation: str): frappe.get_all( "Sales Order Item", filters={"prevdoc_docname": quotation, "docstatus": 1}, - fields=["quotation_item", "sum(qty)"], + fields=["quotation_item", {"SUM": "qty"}], group_by="quotation_item", as_list=1, ) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index fbda1f0b41a..811c7d0c08c 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -992,7 +992,11 @@ def get_requested_item_qty(sales_order): for d in frappe.db.get_all( "Material Request Item", filters={"docstatus": 1, "sales_order": sales_order}, - fields=["sales_order_item", "sum(qty) as qty", "sum(received_qty) as received_qty"], + fields=[ + "sales_order_item", + {"SUM": "qty", "as": "qty"}, + {"SUM": "received_qty", "as": "received_qty"}, + ], group_by="sales_order_item", ): result[d.sales_order_item] = frappe._dict({"qty": d.qty, "received_qty": d.received_qty}) diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py index 84da765d930..d9caa9b8bad 100644 --- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py +++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py @@ -95,7 +95,7 @@ def get_data(filters=None): items = get_selling_items(filters) item_stock_map = frappe.get_all( - "Bin", fields=["item_code", "sum(actual_qty) AS available"], group_by="item_code" + "Bin", fields=["item_code", {"SUM": "actual_qty", "as": "available"}], group_by="item_code" ) item_stock_map = {item.item_code: item.available for item in item_stock_map} price_list_map = fetch_item_prices( diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index b7d80af85f5..3777e330e75 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -799,7 +799,7 @@ class EmailDigest(Document): "status": ["not in", ("Cancelled")], "company": self.company, }, - fields=[{"COUNT": "*", "as": "count"}, "sum(grand_total) as grand_total"], + fields=[{"COUNT": "*", "as": "count"}, {"SUM": "grand_total", "as": "grand_total"}], ) def get_from_to_date(self): diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py index b8d01d56ef5..f5a5216b51d 100644 --- a/erpnext/startup/leaderboard.py +++ b/erpnext/startup/leaderboard.py @@ -63,7 +63,7 @@ def get_all_customers(date_range, company, field, limit=None): return frappe.get_list( "Sales Invoice", - fields=["customer as name", "sum(outstanding_amount) as value"], + fields=["customer as name", {"SUM": "outstanding_amount", "as": "value"}], filters=filters, group_by="customer", order_by="value desc", @@ -80,7 +80,7 @@ def get_all_customers(date_range, company, field, limit=None): return frappe.get_list( "Sales Order", - fields=["customer as name", f"sum({select_field}) as value"], + fields=["customer as name", {"SUM": select_field, "as": "value"}], filters=filters, group_by="customer", order_by="value desc", @@ -91,10 +91,10 @@ def get_all_customers(date_range, company, field, limit=None): @frappe.whitelist() def get_all_items(date_range, company, field, limit=None): if field in ("available_stock_qty", "available_stock_value"): - select_field = "sum(actual_qty)" if field == "available_stock_qty" else "sum(stock_value)" + sum_field = "actual_qty" if field == "available_stock_qty" else "stock_value" results = frappe.db.get_all( "Bin", - fields=["item_code as name", f"{select_field} as value"], + fields=["item_code as name", {"SUM": sum_field, "as": "value"}], group_by="item_code", order_by="value desc", limit=limit, @@ -125,7 +125,7 @@ def get_all_items(date_range, company, field, limit=None): select_doctype, fields=[ f"`tab{child_doctype}`.item_code as name", - f"sum(`tab{child_doctype}`.{select_field}) as value", + {"SUM": f"`tab{child_doctype}`.{select_field}", "as": "value"}, ], filters=filters, order_by="value desc", @@ -145,7 +145,7 @@ def get_all_suppliers(date_range, company, field, limit=None): return frappe.get_list( "Purchase Invoice", - fields=["supplier as name", "sum(outstanding_amount) as value"], + fields=["supplier as name", {"SUM": "outstanding_amount", "as": "value"}], filters=filters, group_by="supplier", order_by="value desc", @@ -162,7 +162,7 @@ def get_all_suppliers(date_range, company, field, limit=None): return frappe.get_list( "Purchase Order", - fields=["supplier as name", f"sum({select_field}) as value"], + fields=["supplier as name", {"SUM": select_field, "as": "value"}], filters=filters, group_by="supplier", order_by="value desc", @@ -186,7 +186,7 @@ def get_all_sales_partner(date_range, company, field, limit=None): "Sales Order", fields=[ "sales_partner as name", - f"sum({select_field}) as value", + {"SUM": select_field, "as": "value"}, ], filters=filters, group_by="sales_partner", @@ -210,7 +210,7 @@ def get_all_sales_person(date_range, company, field=None, limit=0): "Sales Order", fields=[ "`tabSales Team`.sales_person as name", - "sum(`tabSales Team`.allocated_amount) as value", + {"SUM": "`tabSales Team`.allocated_amount", "as": "value"}, ], filters=filters, group_by="`tabSales Team`.sales_person", diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index cbc4fc76ecf..13ac541256e 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -31,7 +31,7 @@ def get( warehouses = frappe.get_list( "Bin", - fields=["warehouse", "sum(stock_value) stock_value"], + fields=["warehouse", {"SUM": "stock_value", "as": "stock_value"}], filters={"warehouse": ["IN", warehouses], "stock_value": [">", 0]}, group_by="warehouse", order_by="stock_value DESC", diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 2c08635e03f..217a5c15806 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -405,8 +405,9 @@ def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None): serial_nos = get_serial_nos(serial_no) batches = frappe.get_all( "Serial No", - fields=["distinct batch_no"], + fields=["batch_no"], filters={"item_code": item_code, "warehouse": warehouse, "name": ("in", serial_nos)}, + distinct=True, ) if not batches: diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index f6c53beca2b..aac2ae46ed2 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -320,12 +320,13 @@ def get_inventory_documents( return frappe.get_all( "DocField", - fields=["distinct parent"], + fields=["parent"], filters=and_filters, or_filters=or_filters, start=start, page_length=page_len, as_list=1, + distinct=True, ) @@ -382,7 +383,7 @@ def get_inventory_dimensions(): return frappe.get_all( "Inventory Dimension", fields=[ - "distinct target_fieldname as fieldname", + "target_fieldname as fieldname", "source_fieldname", "reference_document as doctype", "validate_negative_stock", @@ -390,6 +391,7 @@ def get_inventory_dimensions(): ], filters={"disabled": 0}, order_by="creation", + distinct=True, ) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index d24d241e6ab..7effd0ea7ba 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -593,7 +593,7 @@ class TestPickList(IntegrationTestCase): for dn in frappe.get_all( "Delivery Note", filters={"against_pick_list": pick_list.name, "customer": "_Test Customer"}, - fields={"name"}, + fields=["name"], ): for dn_item in frappe.get_doc("Delivery Note", dn.name).get("items"): self.assertEqual(dn_item.item_code, "_Test Item") @@ -604,7 +604,7 @@ class TestPickList(IntegrationTestCase): for dn in frappe.get_all( "Delivery Note", filters={"against_pick_list": pick_list.name, "customer": "_Test Customer 1"}, - fields={"name"}, + fields=["name"], ): for dn_item in frappe.get_doc("Delivery Note", dn.name).get("items"): self.assertEqual(dn_item.item_code, "_Test Item 2") @@ -637,7 +637,7 @@ class TestPickList(IntegrationTestCase): pick_list_1.submit() create_delivery_note(pick_list_1.name) for dn in frappe.get_all( - "Delivery Note", filters={"against_pick_list": pick_list_1.name}, fields={"name"} + "Delivery Note", filters={"against_pick_list": pick_list_1.name}, fields=["name"] ): for dn_item in frappe.get_doc("Delivery Note", dn.name).get("items"): if dn_item.item_code == "_Test Item": diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 9708f3dfa1a..238f0ea0590 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1333,7 +1333,7 @@ def get_item_wise_returned_qty(pr_doc): "Purchase Receipt", fields=[ "`tabPurchase Receipt Item`.purchase_receipt_item", - "sum(abs(`tabPurchase Receipt Item`.qty)) as qty", + {"SUM": [{"ABS": "`tabPurchase Receipt Item`.qty"}], "as": "qty"}, ], filters=[ ["Purchase Receipt", "docstatus", "=", 1], diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 946997ecfe3..48acf7b0649 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1900,7 +1900,7 @@ class TestPurchaseReceipt(IntegrationTestCase): data = frappe.get_all( "Stock Ledger Entry", filters={"voucher_no": pr_return.name, "docstatus": 1}, - fields=["SUM(stock_value_difference) as stock_value_difference"], + fields=[{"SUM": "stock_value_difference", "as": "stock_value_difference"}], )[0] self.assertEqual(abs(data["stock_value_difference"]), 400.00) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index b386955f9a5..9e7935a4abf 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2420,7 +2420,7 @@ class StockEntry(StockController, SubcontractingInwardController): data = frappe.get_all( "Work Order Operation", filters={"parent": self.work_order}, - fields=["max(process_loss_qty) as process_loss_qty"], + fields=[{"MAX": "process_loss_qty", "as": "process_loss_qty"}], ) if data and data[0].process_loss_qty is not None: @@ -3145,7 +3145,7 @@ class StockEntry(StockController, SubcontractingInwardController): stock_entries_child_list.append(d.ste_detail) transferred_qty = frappe.get_all( "Stock Entry Detail", - fields=["sum(qty) as qty"], + fields=[{"SUM": "qty", "as": "qty"}], filters={ "against_stock_entry": d.against_stock_entry, "ste_detail": d.ste_detail, diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 7c6361a4626..c0ea44c59d5 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -8,6 +8,7 @@ from uuid import uuid4 import frappe from frappe.core.page.permission_manager.permission_manager import reset from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe.query_builder.functions import Timestamp from frappe.tests import IntegrationTestCase from frappe.utils import add_days, add_to_date, flt, today @@ -1281,12 +1282,16 @@ class TestStockLedgerEntry(IntegrationTestCase, StockTestMixin): item=item, from_warehouse=source_warehouse, to_warehouse=target_warehouse, qty=1_728.0 ) - filters = {"voucher_no": transfer.name, "voucher_type": transfer.doctype, "is_cancelled": 0} - sles = frappe.get_all( - "Stock Ledger Entry", - fields=["*"], - filters=filters, - order_by="timestamp(posting_date, posting_time), creation", + sle = frappe.qb.DocType("Stock Ledger Entry") + sles = ( + frappe.qb.from_(sle) + .select("*") + .where(sle.voucher_no == transfer.name) + .where(sle.voucher_type == transfer.doctype) + .where(sle.is_cancelled == 0) + .orderby(Timestamp(sle.posting_date, sle.posting_time)) + .orderby(sle.creation) + .run(as_dict=True) ) self.assertEqual(abs(sles[0].stock_value_difference), sles[1].stock_value_difference) diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py index d3c5d2e8db5..101b6a21461 100644 --- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py +++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py @@ -115,7 +115,7 @@ def get_stock_ledger_entries(report_filters): "posting_time", "company", "warehouse", - "(stock_value_difference / actual_qty) as valuation_rate", + {"DIV": ["stock_value_difference", "actual_qty"], "as": "valuation_rate"}, ] filters = {"is_cancelled": 0} diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.py b/erpnext/stock/report/item_variant_details/item_variant_details.py index e3a2a65d8fe..9e6c89193d8 100644 --- a/erpnext/stock/report/item_variant_details/item_variant_details.py +++ b/erpnext/stock/report/item_variant_details/item_variant_details.py @@ -143,9 +143,9 @@ def get_stock_details_map(variant_list): stock_details = frappe.db.get_all( "Bin", fields=[ - "sum(planned_qty) as planned_qty", - "sum(actual_qty) as actual_qty", - "sum(projected_qty) as projected_qty", + {"SUM": "planned_qty", "as": "planned_qty"}, + {"SUM": "actual_qty", "as": "actual_qty"}, + {"SUM": "projected_qty", "as": "projected_qty"}, "item_code", ], filters={"item_code": ["in", variant_list]}, @@ -167,7 +167,7 @@ def get_buying_price_map(variant_list): buying = frappe.db.get_all( "Item Price", fields=[ - "avg(price_list_rate) as avg_rate", + {"AVG": "price_list_rate", "as": "avg_rate"}, "item_code", ], filters={"item_code": ["in", variant_list], "buying": 1}, @@ -185,7 +185,7 @@ def get_selling_price_map(variant_list): selling = frappe.db.get_all( "Item Price", fields=[ - "avg(price_list_rate) as avg_rate", + {"AVG": "price_list_rate", "as": "avg_rate"}, "item_code", ], filters={"item_code": ["in", variant_list], "selling": 1}, diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py index 431670d5775..8d26e74a0f5 100644 --- a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py @@ -183,14 +183,15 @@ def get_voucher_type(doctype, txt, searchfield, start, page_len, filters): child_doctypes = frappe.get_all( "DocField", filters={"fieldname": "serial_and_batch_bundle"}, - fields=["distinct parent as parent"], + fields=["parent"], + distinct=True, ) query_filters = {"options": ["in", [d.parent for d in child_doctypes]]} if txt: query_filters["parent"] = ["like", f"%{txt}%"] - return frappe.get_all("DocField", filters=query_filters, fields=["distinct parent"], as_list=True) + return frappe.get_all("DocField", filters=query_filters, fields=["parent"], as_list=True, distinct=True) @frappe.whitelist() diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 0db7e40b77f..172e0fa6a41 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -61,7 +61,7 @@ def get_stock_ledger_data(report_filters, filters): "name", "voucher_type", "voucher_no", - "sum(stock_value_difference) as stock_value", + {"SUM": "stock_value_difference", "as": "stock_value"}, "posting_date", "posting_time", ], @@ -88,7 +88,10 @@ def get_gl_data(report_filters, filters): "name", "voucher_type", "voucher_no", - "sum(debit_in_account_currency) - sum(credit_in_account_currency) as account_value", + { + "SUB": [{"SUM": "debit_in_account_currency"}, {"SUM": "credit_in_account_currency"}], + "as": "account_value", + }, ], group_by="voucher_type, voucher_no", ) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index b9275417847..b55a8e43f67 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -546,7 +546,10 @@ def get_opening_balance_from_batch(filters, columns, sl_entries): opening_data = frappe.get_all( "Stock Ledger Entry", - fields=["sum(actual_qty) as qty_after_transaction", "sum(stock_value_difference) as stock_value"], + fields=[ + {"SUM": "actual_qty", "as": "qty_after_transaction"}, + {"SUM": "stock_value_difference", "as": "stock_value"}, + ], filters=query_filters, )[0] diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 686478d07b2..208f9c048a0 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1511,7 +1511,7 @@ def get_batchwise_qty(voucher_type, voucher_no): batches = frappe.get_all( "Serial and Batch Entry", filters={"parent": ("in", bundles), "batch_no": ("is", "set")}, - fields=["batch_no", "SUM(qty) as qty"], + fields=["batch_no", {"SUM": "qty", "as": "qty"}], group_by="batch_no", as_list=1, ) diff --git a/erpnext/stock/tests/test_utils.py b/erpnext/stock/tests/test_utils.py index 4a127a1b9aa..085d3d41cbe 100644 --- a/erpnext/stock/tests/test_utils.py +++ b/erpnext/stock/tests/test_utils.py @@ -1,6 +1,7 @@ import json import frappe +from frappe.query_builder.functions import Timestamp from frappe.tests import IntegrationTestCase from erpnext.stock.utils import scan_barcode @@ -20,11 +21,23 @@ class StockTestMixin: filters = {"voucher_no": doc.name, "voucher_type": doc.doctype, "is_cancelled": 0} if sle_filters: filters.update(sle_filters) - sles = frappe.get_all( - "Stock Ledger Entry", - fields=["*"], - filters=filters, - order_by="timestamp(posting_date, posting_time), creation", + + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select("*") + .where(sle.voucher_no == doc.name) + .where(sle.voucher_type == doc.doctype) + .where(sle.is_cancelled == 0) + ) + if sle_filters: + for key, value in sle_filters.items(): + query = query.where(sle[key] == value) + + sles = ( + query.orderby(Timestamp(sle.posting_date, sle.posting_time)) + .orderby(sle.creation) + .run(as_dict=True) ) self.assertGreaterEqual(len(sles), len(expected_sles))