From 2504f0fc0ca483346de18ab7cc378d28adb5754e Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Thu, 19 Feb 2026 20:32:09 +0530 Subject: [PATCH 1/2] refactor(selling): add type hints to whitelisted function parameters --- erpnext/selling/doctype/customer/customer.py | 15 +++--- .../selling/doctype/quotation/quotation.py | 11 ++-- .../doctype/sales_order/sales_order.py | 54 +++++++++++-------- .../page/point_of_sale/point_of_sale.py | 20 +++---- .../selling/page/sales_funnel/sales_funnel.py | 10 ++-- .../payment_terms_status_for_sales_order.py | 4 +- 6 files changed, 69 insertions(+), 45 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d5e44e41a7f..9167da9271f 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -11,6 +11,7 @@ from frappe.contacts.address_and_contact import ( delete_contact_and_address, load_address_and_contact, ) +from frappe.model.document import Document 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 @@ -431,7 +432,7 @@ class Customer(TransactionBase): @frappe.whitelist() -def make_quotation(source_name, target_doc=None): +def make_quotation(source_name: str, target_doc: str | Document | None = None): def set_missing_values(source, target): _set_missing_values(source, target) @@ -460,7 +461,7 @@ def make_quotation(source_name, target_doc=None): @frappe.whitelist() -def make_opportunity(source_name, target_doc=None): +def make_opportunity(source_name: str, target_doc: str | Document | None = None): def set_missing_values(source, target): _set_missing_values(source, target) @@ -484,7 +485,7 @@ def make_opportunity(source_name, target_doc=None): @frappe.whitelist() -def make_payment_entry(source_name, target_doc=None): +def make_payment_entry(source_name: str, target_doc: str | Document | None = None): def set_missing_values(source, target): _set_missing_values(source, target) @@ -542,7 +543,7 @@ def _set_missing_values(source, target): @frappe.whitelist() -def get_loyalty_programs(doc): +def get_loyalty_programs(doc: Document): """returns applicable loyalty programs for a customer""" lp_details = [] @@ -644,7 +645,9 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, @frappe.whitelist() -def send_emails(customer, customer_outstanding, credit_limit, credit_controller_users_list): +def send_emails( + customer: str, customer_outstanding: float, credit_limit: float, credit_controller_users_list: str | list +): if isinstance(credit_controller_users_list, str): credit_controller_users_list = json.loads(credit_controller_users_list) subject = _("Credit limit reached for customer {0}").format(customer) @@ -852,7 +855,7 @@ def make_address(args, is_primary_address=1, is_shipping_address=1): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def get_customer_primary(doctype, txt, searchfield, start, page_len, filters): +def get_customer_primary(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict): customer = filters.get("customer") type = filters.get("type") type_doctype = qb.DocType(type) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 3db03a18513..f15e83a0b3c 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -6,6 +6,7 @@ import json import frappe from frappe import _ +from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc from frappe.utils import flt, getdate, nowdate @@ -257,7 +258,9 @@ class Quotation(SellingController): opp.set_status(status=status, update=True) @frappe.whitelist() - def declare_enquiry_lost(self, lost_reasons_list, competitors, detailed_reason=None): + def declare_enquiry_lost( + self, lost_reasons_list: list, competitors: list, detailed_reason: str | None = None + ): if not (self.is_fully_ordered() or self.is_partially_ordered()): get_lost_reasons = frappe.get_list("Quotation Lost Reason", fields=["name"]) lost_reasons_lst = [reason.get("name") for reason in get_lost_reasons] @@ -353,7 +356,7 @@ def get_list_context(context=None): @frappe.whitelist() -def make_sales_order(source_name: str, target_doc=None, args=None): +def make_sales_order(source_name: str, target_doc: Document | None = None, args: str | dict | None = None): if not frappe.db.get_singles_value( "Selling Settings", "allow_sales_order_creation_for_expired_quotation" ): @@ -495,7 +498,9 @@ def set_expired_status(): @frappe.whitelist() -def make_sales_invoice(source_name, target_doc=None, args=None): +def make_sales_invoice( + source_name: str, target_doc: str | Document | None = None, args: str | dict | None = None +): return _make_sales_invoice(source_name, target_doc, args=args) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 876b11459b4..3cfdfa15890 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -10,6 +10,7 @@ import frappe.utils from frappe import _, qb from frappe.contacts.doctype.address.address import get_company_address from frappe.desk.notifications import clear_doctype_notifications +from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc from frappe.model.utils import get_fetch_values from frappe.query_builder.functions import Sum @@ -835,8 +836,8 @@ class SalesOrder(SellingController): def create_stock_reservation_entries( self, items_details: list[dict] | None = None, - from_voucher_type: Literal["Pick List", "Purchase Receipt"] = None, - notify=True, + from_voucher_type: Literal["Pick List", "Purchase Receipt"] | None = None, + notify: bool = True, ) -> None: """Creates Stock Reservation Entries for Sales Order Items.""" @@ -852,7 +853,7 @@ class SalesOrder(SellingController): ) @frappe.whitelist() - def cancel_stock_reservation_entries(self, sre_list=None, notify=True) -> None: + def cancel_stock_reservation_entries(self, sre_list: list | None = None, notify: bool = True) -> None: """Cancel Stock Reservation Entries for Sales Order Items.""" from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( @@ -872,7 +873,7 @@ class SalesOrder(SellingController): item.delivery_date = self.delivery_date @frappe.whitelist() - def get_delivery_schedule(self, sales_order_item): + def get_delivery_schedule(self, sales_order_item: str): return frappe.get_all( "Delivery Schedule Item", filters={"sales_order_item": sales_order_item, "sales_order": self.name}, @@ -881,7 +882,7 @@ class SalesOrder(SellingController): ) @frappe.whitelist() - def create_delivery_schedule(self, child_row, schedules): + def create_delivery_schedule(self, child_row: dict | frappe._dict, schedules: str | list[dict]): if isinstance(child_row, dict): child_row = frappe._dict(child_row) @@ -978,7 +979,7 @@ def is_enable_cutoff_date_on_bulk_delivery_note_creation(): @frappe.whitelist() -def close_or_unclose_sales_orders(names, status): +def close_or_unclose_sales_orders(names: str, status: str): if not frappe.has_permission("Sales Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -1020,7 +1021,7 @@ def get_requested_item_qty(sales_order): @frappe.whitelist() -def make_material_request(source_name, target_doc=None): +def make_material_request(source_name: str, target_doc: str | Document | None = None): requested_item_qty = get_requested_item_qty(source_name) def postprocess(source, target): @@ -1125,7 +1126,7 @@ def make_material_request(source_name, target_doc=None): @frappe.whitelist() -def make_project(source_name, target_doc=None): +def make_project(source_name: str, target_doc: str | Document | None = None): def postprocess(source, doc): doc.project_type = "External" doc.project_name = source.name @@ -1152,7 +1153,9 @@ def make_project(source_name, target_doc=None): @frappe.whitelist() -def make_delivery_note(source_name, target_doc=None, kwargs=None): +def make_delivery_note( + source_name: str, target_doc: str | Document | None = None, kwargs: dict | None = None +): from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( get_sre_details_for_voucher, @@ -1325,7 +1328,12 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): @frappe.whitelist() -def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, args=None): +def make_sales_invoice( + source_name: str, + target_doc: str | Document | None = None, + ignore_permissions: bool = False, + args: str | dict | None = None, +): if args is None: args = {} if isinstance(args, str): @@ -1497,7 +1505,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, a @frappe.whitelist() -def make_maintenance_schedule(source_name, target_doc=None): +def make_maintenance_schedule(source_name: str, target_doc: str | Document | None = None): maint_schedule = frappe.db.sql( """select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 @@ -1523,7 +1531,7 @@ def make_maintenance_schedule(source_name, target_doc=None): @frappe.whitelist() -def make_maintenance_visit(source_name, target_doc=None): +def make_maintenance_visit(source_name: str, target_doc: str | Document | None = None): visit = frappe.db.sql( """select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 @@ -1550,7 +1558,7 @@ def make_maintenance_visit(source_name, target_doc=None): @frappe.whitelist() -def get_events(start, end, filters=None): +def get_events(start: str, end: str, filters: str | dict | None = None): """Returns events for Gantt / Calendar view rendering. :param start: Start date-time. @@ -1587,7 +1595,9 @@ def get_events(start, end, filters=None): @frappe.whitelist() -def make_purchase_order(source_name, selected_items=None, target_doc=None): +def make_purchase_order( + source_name: str, selected_items: str | list | None = None, target_doc: str | Document | None = None +): """Creates Purchase Order for each Supplier. Returns a list of doc objects.""" from erpnext.setup.utils import get_exchange_rate @@ -1772,7 +1782,7 @@ def is_product_bundle(item_code): @frappe.whitelist() -def make_work_orders(items, sales_order, company, project=None): +def make_work_orders(items: str, sales_order: str, company: str, project: str | None = None): """Make Work Orders against the given Sales Order for the given `items`""" items = json.loads(items).get("items") out = [] @@ -1804,13 +1814,15 @@ def make_work_orders(items, sales_order, company, project=None): @frappe.whitelist() -def update_status(status, name): +def update_status(status: str, name: str): so = frappe.get_doc("Sales Order", name) so.update_status(status) @frappe.whitelist() -def make_raw_material_request(items, company, sales_order, project=None): +def make_raw_material_request( + items: str | frappe._dict, company: str, sales_order: str, project: str | None = None +): if not frappe.has_permission("Sales Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -1870,14 +1882,14 @@ def make_raw_material_request(items, company, sales_order, project=None): @frappe.whitelist() -def make_inter_company_purchase_order(source_name, target_doc=None): +def make_inter_company_purchase_order(source_name: str, target_doc: str | Document | None = None): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction return make_inter_company_transaction("Sales Order", source_name, target_doc) @frappe.whitelist() -def create_pick_list(source_name, target_doc=None): +def create_pick_list(source_name: str, target_doc: str | Document | None = None): from erpnext.stock.doctype.packed_item.packed_item import is_product_bundle def validate_sales_order(): @@ -1976,7 +1988,7 @@ def update_produced_qty_in_so_item(sales_order, sales_order_item): @frappe.whitelist() -def get_work_order_items(sales_order, for_raw_material_request=0): +def get_work_order_items(sales_order: str, for_raw_material_request: int = 0): """Returns items with BOM that already do not have a linked work order""" if sales_order: so = frappe.get_doc("Sales Order", sales_order) @@ -2045,7 +2057,7 @@ def get_stock_reservation_status(): @frappe.whitelist() -def make_subcontracting_inward_order(source_name, target_doc=None): +def make_subcontracting_inward_order(source_name: str, target_doc: str | Document | None = None): if not is_so_fully_subcontracted(source_name): return get_mapped_subcontracting_inward_order(source_name, target_doc) else: diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 35d22e40fb7..b49805dfa5c 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -122,7 +122,7 @@ def filter_result_items(result, pos_profile): @frappe.whitelist() -def get_parent_item_group(pos_profile): +def get_parent_item_group(pos_profile: str): item_groups = get_item_groups(pos_profile) if not item_groups: @@ -132,7 +132,9 @@ def get_parent_item_group(pos_profile): @frappe.whitelist() -def get_items(start, page_length, price_list, item_group, pos_profile, search_term=""): +def get_items( + start: str, page_length: str, price_list: str, item_group: str, pos_profile: str, search_term: str = "" +): warehouse, hide_unavailable_items = frappe.db.get_value( "POS Profile", pos_profile, ["warehouse", "hide_unavailable_items"] ) @@ -296,7 +298,7 @@ def get_item_group_condition(pos_profile): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def item_group_query(doctype, txt, searchfield, start, page_len, filters): +def item_group_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict): item_groups = [] cond = "1=1" pos_profile = filters.get("pos_profile") @@ -316,7 +318,7 @@ def item_group_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() -def check_opening_entry(user): +def check_opening_entry(user: str): open_vouchers = frappe.db.get_all( "POS Opening Entry", filters={"user": user, "pos_closing_entry": ["in", ["", None]], "docstatus": 1}, @@ -328,7 +330,7 @@ def check_opening_entry(user): @frappe.whitelist() -def create_opening_voucher(pos_profile, company, balance_details): +def create_opening_voucher(pos_profile: str, company: str, balance_details: str): balance_details = json.loads(balance_details) new_pos_opening = frappe.get_doc( @@ -348,7 +350,7 @@ def create_opening_voucher(pos_profile, company, balance_details): @frappe.whitelist() -def get_past_order_list(search_term, status, limit=20): +def get_past_order_list(search_term: str, status: str, limit: int = 20): fields = ["name", "grand_total", "currency", "customer", "customer_name", "posting_time", "posting_date"] invoice_list = [] @@ -419,7 +421,7 @@ def get_past_order_list(search_term, status, limit=20): @frappe.whitelist() -def set_customer_info(fieldname, customer, value=""): +def set_customer_info(fieldname: str, customer: str, value: str = ""): if fieldname == "loyalty_program": frappe.db.set_value("Customer", customer, "loyalty_program", value) @@ -459,7 +461,7 @@ def set_customer_info(fieldname, customer, value=""): @frappe.whitelist() -def get_pos_profile_data(pos_profile): +def get_pos_profile_data(pos_profile: str): pos_profile = frappe.get_doc("POS Profile", pos_profile) pos_profile = pos_profile.as_dict() @@ -521,7 +523,7 @@ def get_invoice_filters(doctype, status, name=None): @frappe.whitelist() -def get_customer_recent_transactions(customer): +def get_customer_recent_transactions(customer: str): sales_invoices = frappe.db.get_list( "Sales Invoice", filters={ diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index 3c979a60367..300142d8814 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -19,7 +19,7 @@ def validate_filters(from_date, to_date, company): @frappe.whitelist() -def get_funnel_data(from_date, to_date, company): +def get_funnel_data(from_date: str, to_date: str, company: str): validate_filters(from_date, to_date, company) active_leads = frappe.db.sql( @@ -60,17 +60,17 @@ def get_funnel_data(from_date, to_date, company): @frappe.whitelist() -def get_opp_by_utm_source(from_date, to_date, company): +def get_opp_by_utm_source(from_date: str, to_date: str, company: str): return get_opp_by("utm_source", from_date, to_date, company) @frappe.whitelist() -def get_opp_by_utm_campaign(from_date, to_date, company): +def get_opp_by_utm_campaign(from_date: str, to_date: str, company: str): return get_opp_by("utm_campaign", from_date, to_date, company) @frappe.whitelist() -def get_opp_by_utm_medium(from_date, to_date, company): +def get_opp_by_utm_medium(from_date: str, to_date: str, company: str): return get_opp_by("utm_medium", from_date, to_date, company) @@ -128,7 +128,7 @@ def get_opp_by(by_field, from_date, to_date, company): @frappe.whitelist() -def get_pipeline_data(from_date, to_date, company): +def get_pipeline_data(from_date: str, to_date: str, company: str): validate_filters(from_date, to_date, company) opportunities = frappe.get_all( diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index 89616d0d0fd..8e596ba35c0 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -93,7 +93,9 @@ def get_descendants_of(doctype, group_name): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def get_customers_or_items(doctype, txt, searchfield, start, page_len, filters): +def get_customers_or_items( + doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: list +): filter_list = [] if isinstance(filters, list): for item in filters: From d3911cd7d82beea8a51323eb644eb012284858ec Mon Sep 17 00:00:00 2001 From: Shllokkk Date: Thu, 19 Feb 2026 21:22:09 +0530 Subject: [PATCH 2/2] fix: add missing type hints due to failing test case --- erpnext/selling/doctype/quotation/quotation.py | 4 +++- erpnext/selling/page/point_of_sale/point_of_sale.py | 7 ++++++- .../payment_terms_status_for_sales_order.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index f15e83a0b3c..603aeb9c7ec 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -356,7 +356,9 @@ def get_list_context(context=None): @frappe.whitelist() -def make_sales_order(source_name: str, target_doc: Document | None = None, args: str | dict | None = None): +def make_sales_order( + source_name: str, target_doc: str | Document | None = None, args: str | dict | None = None +): if not frappe.db.get_singles_value( "Selling Settings", "allow_sales_order_creation_for_expired_quotation" ): diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index b49805dfa5c..0e7b174668d 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -133,7 +133,12 @@ def get_parent_item_group(pos_profile: str): @frappe.whitelist() def get_items( - start: str, page_length: str, price_list: str, item_group: str, pos_profile: str, search_term: str = "" + start: str | int, + page_length: str | int, + price_list: str | None, + item_group: str, + pos_profile: str, + search_term: str = "", ): warehouse, hide_unavailable_items = frappe.db.get_value( "POS Profile", pos_profile, ["warehouse", "hide_unavailable_items"] diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index 8e596ba35c0..eb2b7cc21d8 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -94,7 +94,7 @@ def get_descendants_of(doctype, group_name): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_customers_or_items( - doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: list + doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: list | None ): filter_list = [] if isinstance(filters, list):