diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py index 1ee409a290c..89530b56e81 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py @@ -137,7 +137,7 @@ def get_charts_for_country(country: str, with_standard: bool = False): def _get_chart_name(content): if content: - content = json.loads(content) + content = frappe.parse_json(content) if ( content and content.get("disabled", "No") == "No" ) or frappe.local.flags.allow_unverified_charts: diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 5b0e3bf939b..bff12b0dda2 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -224,7 +224,7 @@ def disable_dimension(doc: str): def toggle_disabling(doc): - doc = json.loads(doc) + doc = frappe.parse_json(doc) if doc.get("disabled"): df = {"read_only": 1} diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index d9aab98a98f..e84136a04c8 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -1058,9 +1058,9 @@ def get_auto_reconcile_message(partially_reconciled, reconciled): @frappe.whitelist() -def reconcile_vouchers(bank_transaction_name: str | int, vouchers: str, is_new_voucher: bool = False): +def reconcile_vouchers(bank_transaction_name: str | int, vouchers: str | list, is_new_voucher: bool = False): # updated clear date of all the vouchers based on the bank transaction - vouchers = json.loads(vouchers) + vouchers = frappe.parse_json(vouchers) transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) transaction.add_payment_entries(vouchers, is_new_voucher) transaction.validate_duplicate_references() diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py index facaf80c008..4554ab6a3a2 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py @@ -290,7 +290,7 @@ def update_mapping_db(bank, template_options): for d in bank.bank_transaction_mapping: d.delete() - for d in json.loads(template_options)["column_to_field_map"].items(): + for d in frappe.parse_json(template_options)["column_to_field_map"].items(): bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1], "file_field": d[0]}) bank.save() diff --git a/erpnext/accounts/doctype/bank_statement_import_log/bank_statement_import_log.py b/erpnext/accounts/doctype/bank_statement_import_log/bank_statement_import_log.py index f50549befa1..783ebf23870 100644 --- a/erpnext/accounts/doctype/bank_statement_import_log/bank_statement_import_log.py +++ b/erpnext/accounts/doctype/bank_statement_import_log/bank_statement_import_log.py @@ -1183,8 +1183,7 @@ def update_pdf_tables(statement_import_id: str, tables: list | str): if doc.status == "Completed": frappe.throw(_("This statement has already been imported."), title=_("Already Imported")) - if isinstance(tables, str): - tables = json.loads(tables) + tables = frappe.parse_json(tables) doc.apply_pdf_tables(tables) @@ -1204,8 +1203,7 @@ def reextract_pdf_table(statement_import_id: str, page: int, table_index: int, b if doc.status == "Completed": frappe.throw(_("This statement has already been imported."), title=_("Already Imported")) - if isinstance(bbox, str): - bbox = json.loads(bbox) + bbox = frappe.parse_json(bbox) page = int(page) table_index = int(table_index) @@ -1290,8 +1288,7 @@ def update_column_mapping(statement_import_id: str, column_mapping: list | str): if doc.status == "Completed": frappe.throw(_("This statement has already been imported."), title=_("Already Imported")) - if isinstance(column_mapping, str): - column_mapping = json.loads(column_mapping) + column_mapping = frappe.parse_json(column_mapping) doc.apply_column_mapping(column_mapping) doc.save() diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py index d0d0188cbd3..c2bac737a78 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py @@ -35,12 +35,12 @@ def upload_bank_statement(): @frappe.whitelist() -def create_bank_entries(columns: str, data: str, bank_account: str): +def create_bank_entries(columns: str, data: str | list, bank_account: str): header_map = get_header_mapping(columns, bank_account) success = 0 errors = 0 - for d in json.loads(data): + for d in frappe.parse_json(data): if all(item is None for item in d) is True: continue fields = {} @@ -66,7 +66,7 @@ def get_header_mapping(columns, bank_account): mapping = get_bank_mapping(bank_account) header_map = {} - for column in json.loads(columns): + for column in frappe.parse_json(columns): if column["content"] in mapping: header_map.update({mapping[column["content"]]: column["colIndex"]}) diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index f64e957400b..0c5cf545c9c 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -248,8 +248,7 @@ def get_dunning_letter_text(dunning_type: str, doc: str | dict, language: str | DOCTYPE = "Dunning Letter Text" FIELDS = ["body_text", "closing_text", "language"] - if isinstance(doc, str): - doc = json.loads(doc) + doc = frappe.parse_json(doc) if not language: language = doc.get("language") diff --git a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py index 6d44796fb1c..d113b3b4d0e 100644 --- a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py +++ b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py @@ -1032,8 +1032,7 @@ class FormulaFieldUpdater: def get_filtered_accounts(company: str, account_rows: str | list): frappe.has_permission("Financial Report Template", ptype="read", throw=True) - if isinstance(account_rows, str): - account_rows = json.loads(account_rows, object_hook=frappe._dict) + account_rows = [frappe._dict(row) for row in frappe.parse_json(account_rows)] return DataCollector.get_filtered_accounts(company, account_rows) diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 3e2f18e1f41..bf4f9e072d0 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -317,8 +317,8 @@ class InvoiceDiscounting(AccountsController): @frappe.whitelist() -def get_invoices(filters: str): - filters = frappe._dict(json.loads(filters)) +def get_invoices(filters: str | dict): + filters = frappe._dict(frappe.parse_json(filters)) si = frappe.qb.DocType("Sales Invoice") di = frappe.qb.DocType("Discounted Invoice") diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d6b597067f6..680755343e6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1805,8 +1805,7 @@ class PaymentEntry(AccountsController): if not self.references or not matched_payment_requests: return - if isinstance(matched_payment_requests, str): - matched_payment_requests = json.loads(matched_payment_requests) + matched_payment_requests = frappe.parse_json(matched_payment_requests) # modify matched_payment_requests # like (reference_doctype, reference_name, allocated_amount): payment_request @@ -2011,8 +2010,7 @@ def validate_inclusive_tax(tax, doc): @frappe.whitelist() def get_outstanding_reference_documents(args: str | dict, validate: bool = False): - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) if args.get("party_type") == "Member": return diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 93faa06a1a2..26d5c2ce833 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -740,7 +740,7 @@ def make_payment_request(**args): # Schedule-based PRs are allowed only if no Payment Entry exists for this document. # Any existing Payment Entry forces legacy (amount-based) flow. - selected_payment_schedules = json.loads(args.get("schedules")) if args.get("schedules") else [] + selected_payment_schedules = frappe.parse_json(args.get("schedules")) if args.get("schedules") else [] # Backend guard: # If any Payment Entry exists, schedule-based PRs are not allowed. @@ -931,7 +931,7 @@ def apply_payment_references(pr, payment_reference): def set_payment_references(payment_schedules): - payment_schedules = json.loads(payment_schedules) if payment_schedules else [] + payment_schedules = frappe.parse_json(payment_schedules) if payment_schedules else [] payment_reference = [] for row in payment_schedules: diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 23ef9099b6d..71d04db1ea0 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -1036,8 +1036,7 @@ def make_sales_return(source_name: str, target_doc: Document | str | None = None def make_merge_log(invoices: str | list): import json - if isinstance(invoices, str): - invoices = json.loads(invoices) + invoices = frappe.parse_json(invoices) if len(invoices) == 0: frappe.throw(_("At least one invoice has to be selected.")) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index d691040104b..c372381850b 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -341,8 +341,7 @@ def apply_pricing_rule(args: str | dict, doc: str | dict | Document | None = Non } """ - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) args = frappe._dict(args) @@ -397,8 +396,7 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False): get_product_discount_rule, ) - if isinstance(doc, str): - doc = json.loads(doc) + doc = frappe.parse_json(doc) if doc: doc = frappe.get_doc(doc) @@ -628,9 +626,7 @@ def remove_pricing_rule_for_item( get_pricing_rule_items, ) - if isinstance(item_details, str): - item_details = json.loads(item_details) - item_details = frappe._dict(item_details) + item_details = frappe._dict(frappe.parse_json(item_details)) for d in get_applied_pricing_rules(pricing_rules): if not d or not frappe.db.exists("Pricing Rule", d): @@ -671,8 +667,7 @@ def remove_pricing_rule_for_item( @frappe.whitelist() def remove_pricing_rules(item_list: str | list): - if isinstance(item_list, str): - item_list = json.loads(item_list) + item_list = frappe.parse_json(item_list) out = [] for item in item_list: diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index ca1e6f2f129..9fabb2bdc89 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -636,7 +636,7 @@ def remove_free_item(doc): def get_applied_pricing_rules(pricing_rules): if pricing_rules: if pricing_rules.startswith("["): - return json.loads(pricing_rules) + return frappe.parse_json(pricing_rules) else: return pricing_rules.split(",") diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py index f4440345e96..21ac42a5d3a 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py @@ -542,8 +542,7 @@ def check_multi_currency(pr_doc): def is_any_doc_running(for_filter: str | dict | None = None) -> str | None: running_doc = None if for_filter: - if isinstance(for_filter, str): - for_filter = json.loads(for_filter) + for_filter = frappe.parse_json(for_filter) running_doc = frappe.db.get_value( "Process Payment Reconciliation", diff --git a/erpnext/accounts/doctype/purchase_invoice/mapper.py b/erpnext/accounts/doctype/purchase_invoice/mapper.py index 7c50121f1e5..0d0a771ea37 100644 --- a/erpnext/accounts/doctype/purchase_invoice/mapper.py +++ b/erpnext/accounts/doctype/purchase_invoice/mapper.py @@ -50,8 +50,7 @@ def make_purchase_receipt( ): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) def post_parent_process(source_parent, target_parent): remove_items_with_zero_qty(target_parent) diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py index be4b1674241..97a17c21600 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py @@ -201,9 +201,9 @@ def get_linked_advances(company, docname): @frappe.whitelist() -def create_unreconcile_doc_for_selection(selections: str | None = None): +def create_unreconcile_doc_for_selection(selections: str | list | None = None): if selections: - selections = json.loads(selections) + selections = frappe.parse_json(selections) # assuming each row is a unique voucher for row in selections: unrecon = frappe.new_doc("Unreconcile Payment") diff --git a/erpnext/accounts/services/child_item_update.py b/erpnext/accounts/services/child_item_update.py index 0871c732b56..7a8ff400a65 100644 --- a/erpnext/accounts/services/child_item_update.py +++ b/erpnext/accounts/services/child_item_update.py @@ -30,7 +30,7 @@ class ChildItemUpdater: self._ordered_items: dict | None = None self._purchased_items: dict | None = None - def update(self, trans_items: str) -> None: + def update(self, trans_items: str | list) -> None: """Process item additions, edits, and deletions from trans_items JSON.""" from erpnext.buying.doctype.supplier_quotation.supplier_quotation import get_purchased_items from erpnext.selling.doctype.quotation.mapper import get_ordered_items diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 1f0bee0f021..f2a71bb5f64 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -995,8 +995,7 @@ class Asset(AccountsController): @frappe.whitelist() def get_depreciation_rate(self, args: str | dict | Document, on_validate: bool = False): - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) rate_field_precision = frappe.get_single_value("System Settings", "float_precision") or 2 diff --git a/erpnext/assets/doctype/asset/mapper.py b/erpnext/assets/doctype/asset/mapper.py index 282d58a987c..aabe28179e7 100644 --- a/erpnext/assets/doctype/asset/mapper.py +++ b/erpnext/assets/doctype/asset/mapper.py @@ -162,8 +162,7 @@ def make_asset_movement( assets: list[dict] | str, purpose: str = "Transfer", ): - if isinstance(assets, str): - assets = json.loads(assets) + assets = frappe.parse_json(assets) if len(assets) == 0: frappe.throw(_("At least one asset has to be selected.")) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index ada205080cb..188254929d9 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -669,8 +669,7 @@ def get_service_item_details(ctx: ItemDetailsCtx) -> frappe._dict: @frappe.whitelist() def get_items_tagged_to_wip_composite_asset(params: dict | str): - if isinstance(params, str): - params = json.loads(params) + params = frappe.parse_json(params) fields = [ "item_code", diff --git a/erpnext/buying/doctype/purchase_order/mapper.py b/erpnext/buying/doctype/purchase_order/mapper.py index e379824f33f..468ab3e2e5d 100644 --- a/erpnext/buying/doctype/purchase_order/mapper.py +++ b/erpnext/buying/doctype/purchase_order/mapper.py @@ -27,8 +27,7 @@ def make_purchase_receipt( ): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) has_unit_price_items = frappe.db.get_value("Purchase Order", source_name, "has_unit_price_items") @@ -123,8 +122,7 @@ def make_purchase_invoice_from_portal(purchase_order_name: str): def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions=False, args=None): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) def postprocess(source, target): target.flags.ignore_permissions = ignore_permissions @@ -294,7 +292,7 @@ def get_mapped_subcontracting_order(source_name: str, target_doc: str | Document ) or frappe.get_value("Production Plan", target_doc.production_plan, "reserve_stock") if target_doc and isinstance(target_doc, str): - target_doc = json.loads(target_doc) + target_doc = frappe.parse_json(target_doc) for key in ["service_items", "items", "supplied_items"]: if key in target_doc: del target_doc[key] diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 976ee5bbf9e..be27000db2b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -549,11 +549,11 @@ def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= @frappe.whitelist() -def close_or_unclose_purchase_orders(names: str, status: str): +def close_or_unclose_purchase_orders(names: str | list, status: str): if not frappe.has_permission("Purchase Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) - names = json.loads(names) + names = frappe.parse_json(names) for name in names: po = frappe.get_lazy_doc("Purchase Order", name) if po.docstatus == 1: diff --git a/erpnext/buying/doctype/request_for_quotation/mapper.py b/erpnext/buying/doctype/request_for_quotation/mapper.py index b435bb607f5..77e9f02db85 100644 --- a/erpnext/buying/doctype/request_for_quotation/mapper.py +++ b/erpnext/buying/doctype/request_for_quotation/mapper.py @@ -57,8 +57,7 @@ def make_supplier_quotation_from_rfq( # This method is used to make supplier quotation from supplier's portal. @frappe.whitelist() def create_supplier_quotation(doc: str | Document | dict): - if isinstance(doc, str): - doc = json.loads(doc) + doc = frappe.parse_json(doc) if frappe.session.user not in frappe.get_all( "Portal User", {"parent": doc.get("supplier")}, pluck="user" diff --git a/erpnext/buying/doctype/supplier_quotation/mapper.py b/erpnext/buying/doctype/supplier_quotation/mapper.py index aebe5d94a4c..67bd32223e6 100644 --- a/erpnext/buying/doctype/supplier_quotation/mapper.py +++ b/erpnext/buying/doctype/supplier_quotation/mapper.py @@ -15,8 +15,7 @@ def make_purchase_order( ): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) def set_missing_values(source, target): target.run_method("set_missing_values") diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py index f661ecb5d3d..293c084520e 100644 --- a/erpnext/buying/utils.py +++ b/erpnext/buying/utils.py @@ -124,12 +124,12 @@ def check_on_hold_or_closed_status(doctype, docname) -> None: @frappe.whitelist() -def get_linked_material_requests(items: str): +def get_linked_material_requests(items: str | list): """ Retrieve Material Requests linked to a list of items. """ - items = json.loads(items) + items = frappe.parse_json(items) mr_list = [] mr = frappe.qb.DocType("Material Request") diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index a1a6c477f13..4dadc91da3b 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -45,8 +45,7 @@ def get_variant( if item_template.variant_based_on == "Manufacturer" and manufacturer: return make_variant_based_on_manufacturer(item_template, manufacturer, manufacturer_part_no) - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) attribute_args = {k: v for k, v in args.items() if k != "use_template_image"} if not attribute_args: @@ -258,8 +257,7 @@ def find_variant(template, args, variant_item_code=None): @frappe.whitelist() def create_variant(item: str, args: dict | str, use_template_image: bool = False): use_template_image = frappe.parse_json(use_template_image) - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) template = frappe.get_doc("Item", item) variant = frappe.new_doc("Item") @@ -286,10 +284,7 @@ def create_variant(item: str, args: dict | str, use_template_image: bool = False def enqueue_multiple_variant_creation(item: str, args: dict | str, use_template_image: bool = False): use_template_image = frappe.parse_json(use_template_image) # There can be innumerable attribute combinations, enqueue - if isinstance(args, str): - variants = json.loads(args) - else: - variants = args + variants = frappe.parse_json(args) variants = {key: values for key, values in variants.items() if values} if not variants: frappe.throw(_("Please select at least one attribute value")) @@ -315,8 +310,7 @@ def enqueue_multiple_variant_creation(item: str, args: dict | str, use_template_ def create_multiple_variants(item, args, use_template_image=False): count = 0 - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) args = {key: values for key, values in args.items() if values} template_item = frappe.get_doc("Item", item) @@ -483,7 +477,7 @@ def make_variant_item_code(template_item_code, template_item_name, variant): @frappe.whitelist() def create_variant_doc_for_quick_entry(template: str, args: dict | str): variant_based_on = frappe.db.get_value("Item", template, "variant_based_on") - args = json.loads(args) + args = frappe.parse_json(args) if variant_based_on == "Manufacturer": variant = get_variant(template, **args) else: diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index f6e322eca6a..74b8b8af761 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -213,8 +213,7 @@ def item_query( """ doctype = "Item" - if isinstance(filters, str): - filters = json.loads(filters) + filters = frappe.parse_json(filters) if filters and isinstance(filters, dict): if filters.get("customer") or filters.get("supplier"): diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index e350f2d950c..5354c8c6f4e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -625,8 +625,7 @@ def repost_required_for_queue(doc: StockController) -> bool: def check_item_quality_inspection(doctype: str, docstatus: str | int, items: str | list[dict]): from erpnext.stock.services.quality_inspection_service import INSPECTION_FIELDNAME_MAP - if isinstance(items, str): - items = json.loads(items) + items = frappe.parse_json(items) inspection_fieldname = INSPECTION_FIELDNAME_MAP.get(doctype) if inspection_fieldname is None: @@ -658,8 +657,7 @@ def check_item_quality_inspection(doctype: str, docstatus: str | int, items: str def make_quality_inspections( company: str, doctype: str, docname: str, items: str | list, inspection_type: str ): - if isinstance(items, str): - items = json.loads(items) + items = frappe.parse_json(items) inspections = [] for item in items: diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 11b7f8d9011..b598db625a4 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -342,7 +342,7 @@ class calculate_taxes_and_totals: self._set_in_company_currency(item, ["net_rate", "net_amount"]) def _load_item_tax_rate(self, item_tax_rate): - return json.loads(item_tax_rate) if item_tax_rate else {} + return frappe.parse_json(item_tax_rate) if item_tax_rate else {} def get_current_tax_fraction(self, tax, item_tax_map): """ diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py index d2a77e426f4..b9dc9c8b7f3 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.py +++ b/erpnext/crm/doctype/contract_template/contract_template.py @@ -35,8 +35,7 @@ class ContractTemplate(Document): @frappe.whitelist() def get_contract_template(template_name: str, doc: str | dict | Document): - if isinstance(doc, str): - doc = json.loads(doc) + doc = frappe.parse_json(doc) contract_template = frappe.get_doc("Contract Template", template_name) contract_terms = None diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 84ac1f4d300..5932a35cde4 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -391,7 +391,7 @@ def get_item_details(item_code: str): @frappe.whitelist() def set_multiple_status(names: str | list[str], status: str): - names = json.loads(names) + names = frappe.parse_json(names) for name in names: opp = frappe.get_doc("Opportunity", name) opp.status = status diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index c86876cf084..0230dda7925 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -33,7 +33,7 @@ def create_prospect_against_crm_deal(): pass if doc.contacts and len(doc.contacts): - create_contacts(json.loads(doc.contacts), prospect.company_name, "Prospect", prospect_name) + create_contacts(frappe.parse_json(doc.contacts), prospect.company_name, "Prospect", prospect_name) create_address("Prospect", prospect_name, doc.address) frappe.response["message"] = prospect_name @@ -69,8 +69,7 @@ def create_contacts(contacts, organization=None, link_doctype=None, link_docname def create_address(doctype, docname, address): if not address: return - if isinstance(address, str): - address = json.loads(address) + address = frappe.parse_json(address) try: _address = frappe.db.exists("Address", address.get("name")) if not _address: @@ -153,7 +152,7 @@ def create_customer(customer_data: dict | None = None): customer.insert(ignore_permissions=True) customer_name = customer.name - contacts = json.loads(customer_data.get("contacts")) + contacts = frappe.parse_json(customer_data.get("contacts")) create_contacts(contacts, customer_name, "Customer", customer_name) create_address("Customer", customer_name, customer_data.get("address")) return customer_name diff --git a/erpnext/edi/doctype/code_list/code_list_import.py b/erpnext/edi/doctype/code_list/code_list_import.py index 0f6a51fc993..0b8ec588b30 100644 --- a/erpnext/edi/doctype/code_list/code_list_import.py +++ b/erpnext/edi/doctype/code_list/code_list_import.py @@ -156,13 +156,15 @@ def process_genericode_import( code_column: str, title_column: str | None = None, description_column: str | None = None, - filters: str | None = None, + filters: str | dict | None = None, ): from erpnext.edi.doctype.common_code.common_code import import_genericode column_map = {"code": code_column, "title": title_column, "description": description_column} - return import_genericode(code_list_name, file_name, column_map, json.loads(filters) if filters else None) + return import_genericode( + code_list_name, file_name, column_map, frappe.parse_json(filters) if filters else None + ) def get_genericode_columns_and_examples(root): diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 36c2f8c7fc8..ccb9133eb62 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -51,8 +51,8 @@ def get_plaid_configuration(): @frappe.whitelist() -def add_institution(token: str, response: str): - response = json.loads(response) +def add_institution(token: str, response: str | dict): + response = frappe.parse_json(response) plaid = PlaidConnector() access_token = plaid.get_access_token(token) @@ -80,13 +80,8 @@ def add_institution(token: str, response: str): @frappe.whitelist() def add_bank_accounts(response: str | dict, bank: str | dict, company: str): - try: - response = json.loads(response) - except TypeError: - pass - - if isinstance(bank, str): - bank = json.loads(bank) + response = frappe.parse_json(response) + bank = frappe.parse_json(bank) result = [] parent_gl_account = frappe.db.get_all( @@ -358,8 +353,8 @@ def get_company(bank_account_name): @frappe.whitelist() -def update_bank_account_ids(response: str): - data = json.loads(response) +def update_bank_account_ids(response: str | dict): + data = frappe.parse_json(response) institution_name = data["institution"]["name"] bank = frappe.get_doc("Bank", institution_name).as_dict() bank_account_name = f"{data['account']['name']} - {institution_name}" diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 2b966df04d6..1ff17cac37d 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -712,6 +712,10 @@ default_log_clearing_doctypes = { export_python_type_annotations = True +# Send non-GET requests for ERPNext's endpoints as native `application/json` +# bodies instead of form-encoded, per-key JSON-stringified values. +use_json_request_body = True + fields_for_group_similar_items = ["qty", "amount"] # Translation diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 061fbbf43e7..0f375271607 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -585,7 +585,7 @@ class BOM(WebsiteGenerator): if isinstance(kwargs, str): import json - kwargs = json.loads(kwargs) + kwargs = frappe.parse_json(kwargs) return kwargs diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 983dd2d4cd8..70052591b6a 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -32,8 +32,7 @@ class BOMUpdateTool(Document): def enqueue_replace_bom(boms: dict | str | None = None, args: dict | str | None = None) -> "BOMUpdateLog": """Returns a BOM Update Log (that queues a job) for BOM Replacement.""" boms = boms or args - if isinstance(boms, str): - boms = json.loads(boms) + boms = frappe.parse_json(boms) update_log = create_bom_update_log(boms=boms) return update_log diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index f36373323d4..9f4db6c117c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -1685,8 +1685,7 @@ class JobCard(Document): @frappe.whitelist() def make_time_log(kwargs: str | dict): - if isinstance(kwargs, str): - kwargs = json.loads(kwargs) + kwargs = frappe.parse_json(kwargs) kwargs = frappe._dict(kwargs) doc = frappe.get_doc("Job Card", kwargs.job_card_id) @@ -1761,8 +1760,7 @@ def get_job_card_filter_conditions(jc, filters): Replaces the previous raw SQL ``get_filters_cond`` based filtering so that all user supplied values are passed as bound parameters via the query builder. """ - if isinstance(filters, str): - filters = json.loads(filters) + filters = frappe.parse_json(filters) if not filters: return [] diff --git a/erpnext/manufacturing/doctype/production_plan/services/material_request.py b/erpnext/manufacturing/doctype/production_plan/services/material_request.py index a17d434136a..c4f12d8f1b7 100644 --- a/erpnext/manufacturing/doctype/production_plan/services/material_request.py +++ b/erpnext/manufacturing/doctype/production_plan/services/material_request.py @@ -159,8 +159,7 @@ def get_items_for_material_requests( def _normalize_mr_doc(doc): - if isinstance(doc, str): - doc = frappe._dict(json.loads(doc)) + doc = frappe._dict(frappe.parse_json(doc)) return doc diff --git a/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py b/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py index 87ccdd2b5c7..615d840f08a 100644 --- a/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py +++ b/erpnext/manufacturing/doctype/production_plan/services/planning_queries.py @@ -24,8 +24,7 @@ def get_bin_details( ): frappe.has_permission("Production Plan", "read", throw=True) - if isinstance(row, str): - row = frappe._dict(json.loads(row)) + row = frappe._dict(frappe.parse_json(row)) bin = frappe.qb.DocType("Bin") subquery = _bin_warehouse_subquery(bin, company, row, for_warehouse, all_warehouse) @@ -65,8 +64,7 @@ def _bin_qty_columns(bin): def get_warehouse_list(warehouses): warehouse_list = [] - if isinstance(warehouses, str): - warehouses = json.loads(warehouses) + warehouses = frappe.parse_json(warehouses) for row in warehouses: child_warehouses = frappe.db.get_descendants("Warehouse", row.get("warehouse")) diff --git a/erpnext/manufacturing/doctype/work_order/mapper.py b/erpnext/manufacturing/doctype/work_order/mapper.py index 05ae74df42f..74377094470 100644 --- a/erpnext/manufacturing/doctype/work_order/mapper.py +++ b/erpnext/manufacturing/doctype/work_order/mapper.py @@ -148,8 +148,7 @@ def _new_work_order(item, bom_no, company, item_details, use_multi_level_bom): def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): - if isinstance(variant_items, str): - variant_items = json.loads(variant_items) + variant_items = frappe.parse_json(variant_items) for item in variant_items: _add_variant_row(item, wo_doc, bom_no, table_name) @@ -289,8 +288,7 @@ def _set_stock_entry_warehouses(stock_entry, work_order, purpose, target_warehou def make_job_card(work_order: str, operations: str | list, parent_bom: str | None = None): frappe.has_permission("Job Card", "create", throw=True) - if isinstance(operations, str): - operations = json.loads(operations) + operations = frappe.parse_json(operations) work_order = frappe.get_doc("Work Order", work_order) for row in operations: @@ -469,10 +467,10 @@ def get_work_order_operation_data(work_order, operation, workstation): @frappe.whitelist() -def create_pick_list(source_name: str, target_doc: str | None = None, for_qty: float | None = None): +def create_pick_list(source_name: str, target_doc: str | dict | None = None, for_qty: float | None = None): frappe.has_permission("Pick List", "create", throw=True) - for_qty = for_qty or json.loads(target_doc).get("for_qty") + for_qty = for_qty or frappe.parse_json(target_doc).get("for_qty") max_finished_goods_qty = frappe.db.get_value("Work Order", source_name, "qty") postprocess = partial( _set_pick_list_item_qty, for_qty=for_qty, max_finished_goods_qty=max_finished_goods_qty diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 037e46de78f..2b567ce44e1 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -629,11 +629,11 @@ def allow_to_make_project_update(project, time, frequency): @frappe.whitelist() -def create_duplicate_project(prev_doc: str, project_name: str): +def create_duplicate_project(prev_doc: str | dict, project_name: str): """Create duplicate project based on the old project""" import json - prev_doc = json.loads(prev_doc) + prev_doc = frappe.parse_json(prev_doc) if project_name == prev_doc.get("name"): frappe.throw(_("Use a name that is different from previous project name")) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index d7781df5f86..6cdfd50933a 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -363,8 +363,8 @@ def get_project(doctype: str, txt: str, searchfield: str, start: int, page_len: @frappe.whitelist() -def set_multiple_status(names: str, status: str): - names = json.loads(names) +def set_multiple_status(names: str | list, status: str): + names = frappe.parse_json(names) for name in names: task = frappe.get_doc("Task", name) task.status = status @@ -459,8 +459,8 @@ def add_node(): @frappe.whitelist() -def add_multiple_tasks(data: str, parent: str): - data = json.loads(data) +def add_multiple_tasks(data: str | list, parent: str): + data = frappe.parse_json(data) new_doc = {"doctype": "Task", "parent_task": parent if parent != "All Tasks" else ""} new_doc["project"] = frappe.db.get_value("Task", {"name": parent}, "project") or "" diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 04819e68a0f..d227b225f3b 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -497,7 +497,7 @@ def get_activity_cost( @frappe.whitelist() -def get_events(start: str, end: str, filters: str | None = None): +def get_events(start: str, end: str, filters: str | list | dict | None = None): """Returns events for Gantt / Calendar view rendering. :param start: Start date-time. :param end: End date-time. @@ -505,7 +505,7 @@ def get_events(start: str, end: str, filters: str | None = None): """ from erpnext.utilities.query import get_event_conditions_qb - filters = json.loads(filters) if filters else {} + filters = frappe.parse_json(filters) if filters else {} tsd = frappe.qb.DocType("Timesheet Detail") ts = frappe.qb.DocType("Timesheet") diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index 7c8727aa7fa..b012ae9be69 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -104,7 +104,7 @@ def prepare_invoice(invoice, progressive_number): def get_conditions(filters): - filters = json.loads(filters) + filters = frappe.parse_json(filters) conditions = {"docstatus": 1, "company_tax_id": ("!=", "")} diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py index cebc60d1aec..7d9b02ecf54 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.py +++ b/erpnext/regional/report/irs_1099/irs_1099.py @@ -84,7 +84,7 @@ def get_columns(): @frappe.whitelist() -def irs_1099_print(filters: str): +def irs_1099_print(filters: str | dict): if not filters: frappe._dict( { @@ -93,7 +93,7 @@ def irs_1099_print(filters: str): } ) else: - filters = frappe._dict(json.loads(filters)) + filters = frappe._dict(frappe.parse_json(filters)) fiscal_year_doc = get_fiscal_year(fiscal_year=filters.fiscal_year, as_dict=True) fiscal_year = cstr(fiscal_year_doc.year_start_date.year) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index e8ba3eab04d..cb5e770b141 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -559,8 +559,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, 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) + credit_controller_users_list = frappe.parse_json(credit_controller_users_list) subject = _("Credit limit reached for customer {0}").format(customer) message = _("Credit limit has been crossed for customer {0} ({1}/{2})").format( customer, customer_outstanding, credit_limit diff --git a/erpnext/selling/doctype/quotation/mapper.py b/erpnext/selling/doctype/quotation/mapper.py index 166bd5278ab..2182c969d4e 100644 --- a/erpnext/selling/doctype/quotation/mapper.py +++ b/erpnext/selling/doctype/quotation/mapper.py @@ -31,8 +31,7 @@ def make_sales_order( def _make_sales_order(source_name, target_doc=None, ignore_permissions=False, args=None): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) customer = _make_customer(source_name, ignore_permissions) ordered_items = get_ordered_items(source_name) @@ -151,8 +150,7 @@ def make_sales_invoice( def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False, args=None): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) customer = _make_customer(source_name, ignore_permissions) diff --git a/erpnext/selling/doctype/sales_order/mapper.py b/erpnext/selling/doctype/sales_order/mapper.py index d2972fdc302..9fddf353d92 100644 --- a/erpnext/selling/doctype/sales_order/mapper.py +++ b/erpnext/selling/doctype/sales_order/mapper.py @@ -430,8 +430,7 @@ def make_sales_invoice( ): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) # 0 qty is accepted, as the qty is uncertain for some items has_unit_price_items = frappe.db.get_value("Sales Order", source_name, "has_unit_price_items") @@ -675,8 +674,7 @@ def make_purchase_order( if not selected_items: return - if isinstance(selected_items, str): - selected_items = json.loads(selected_items) + selected_items = frappe.parse_json(selected_items) def set_missing_values(source, target): target.supplier = supplier @@ -843,9 +841,9 @@ def set_delivery_date(items: list, sales_order: str) -> None: @frappe.whitelist() -def make_work_orders(items: str, sales_order: str, company: str, project: str | None = None): +def make_work_orders(items: str | dict, 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") + items = frappe.parse_json(items).get("items") out = [] for i in items: @@ -912,8 +910,7 @@ def make_raw_material_request( if not frappe.has_permission("Sales Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) - if isinstance(items, str): - items = frappe._dict(json.loads(items)) + items = frappe._dict(frappe.parse_json(items)) for item in items.get("items"): item["include_exploded_items"] = items.get("include_exploded_items") @@ -1089,7 +1086,7 @@ def get_mapped_subcontracting_inward_order( target_doc.populate_items_table() if target_doc and isinstance(target_doc, str): - target_doc = json.loads(target_doc) + target_doc = frappe.parse_json(target_doc) for key in ["service_items", "items", "received_items"]: if key in target_doc: del target_doc[key] diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index c468b92f183..784feff8ed7 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -711,11 +711,11 @@ def is_enable_cutoff_date_on_bulk_delivery_note_creation(): @frappe.whitelist() -def close_or_unclose_sales_orders(names: str, status: str): +def close_or_unclose_sales_orders(names: str | list, status: str): if not frappe.has_permission("Sales Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) - names = json.loads(names) + names = frappe.parse_json(names) for name in names: so = frappe.get_lazy_doc("Sales Order", name) if so.docstatus == 1: 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 83e7bac3fef..a96fc309687 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -347,8 +347,8 @@ def check_opening_entry(user: str): @frappe.whitelist() -def create_opening_voucher(pos_profile: str, company: str, balance_details: str): - balance_details = json.loads(balance_details) +def create_opening_voucher(pos_profile: str, company: str, balance_details: str | list): + balance_details = frappe.parse_json(balance_details) new_pos_opening = frappe.get_doc( { diff --git a/erpnext/setup/doctype/department/department.py b/erpnext/setup/doctype/department/department.py index 71cf6e96743..a92c77f249d 100644 --- a/erpnext/setup/doctype/department/department.py +++ b/erpnext/setup/doctype/department/department.py @@ -77,8 +77,7 @@ def get_children( is_root: bool = False, include_disabled: str | dict | None = None, ): - if isinstance(include_disabled, str): - include_disabled = json.loads(include_disabled) + include_disabled = frappe.parse_json(include_disabled) fields = ["name as value", "is_group as expandable"] filters = {} diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index a7c26857ca3..7095da60788 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -176,7 +176,7 @@ def get_events(start: DateTimeLikeObject, end: DateTimeLikeObject, filters: str :param filters: Filters (JSON). """ if filters: - filters = json.loads(filters) + filters = frappe.parse_json(filters) else: filters = [] diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py index 32623605f51..f9fd9050d34 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py @@ -37,8 +37,7 @@ class TermsandConditions(Document): @frappe.whitelist() def get_terms_and_conditions(template_name: str, doc: str | dict): - if isinstance(doc, str): - doc = json.loads(doc) + doc = frappe.parse_json(doc) terms_and_conditions = frappe.get_doc("Terms and Conditions", template_name) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 57b133bb4cc..46c059b25c9 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -404,8 +404,7 @@ def make_batch(kwargs): def get_pos_reserved_batch_qty(filters: dict | str): import json - if isinstance(filters, str): - filters = json.loads(filters) + filters = frappe.parse_json(filters) p = frappe.qb.DocType("POS Invoice").as_("p") item = frappe.qb.DocType("POS Invoice Item").as_("item") diff --git a/erpnext/stock/doctype/delivery_note/mapper.py b/erpnext/stock/doctype/delivery_note/mapper.py index 605a2d22df6..e4a0eaefe93 100644 --- a/erpnext/stock/doctype/delivery_note/mapper.py +++ b/erpnext/stock/doctype/delivery_note/mapper.py @@ -66,8 +66,7 @@ def make_sales_invoice( if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) doc = frappe.get_doc("Delivery Note", source_name) diff --git a/erpnext/stock/doctype/material_request/mapper.py b/erpnext/stock/doctype/material_request/mapper.py index 5561ad8251f..cb2d19c0adb 100644 --- a/erpnext/stock/doctype/material_request/mapper.py +++ b/erpnext/stock/doctype/material_request/mapper.py @@ -53,8 +53,7 @@ def make_purchase_order( ): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) is_subcontracted = ( frappe.db.get_value("Material Request", source_name, "material_request_type") == "Subcontracting" diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index 7dbeb109c59..5aff12994c9 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -432,7 +432,7 @@ def on_doctype_update(): @frappe.whitelist() -def get_items_from_product_bundle(row: str): +def get_items_from_product_bundle(row: str | dict): """Item details for each component of a Product Bundle. ``row.product_bundle`` selects a specific version by document name (the buying @@ -441,7 +441,7 @@ def get_items_from_product_bundle(row: str): """ from erpnext.selling.doctype.product_bundle.product_bundle import get_active_product_bundle - row, items = ItemDetailsCtx(json.loads(row)), [] + row, items = ItemDetailsCtx(frappe.parse_json(row)), [] if bundle_name := row.get("product_bundle"): frappe.has_permission("Product Bundle", "read", bundle_name, throw=True) diff --git a/erpnext/stock/doctype/pick_list/mapper.py b/erpnext/stock/doctype/pick_list/mapper.py index 2d7f0e91ab8..b1168e112a8 100644 --- a/erpnext/stock/doctype/pick_list/mapper.py +++ b/erpnext/stock/doctype/pick_list/mapper.py @@ -113,8 +113,7 @@ def create_dn_for_pick_lists( """Get Items from Multiple Pick Lists and create a Delivery Note for filtered customer""" if kwargs is None: kwargs = {} - if isinstance(kwargs, str): - kwargs = json.loads(kwargs) + kwargs = frappe.parse_json(kwargs) pick_list = frappe.get_doc("Pick List", source_name) validate_item_locations(pick_list) @@ -282,8 +281,8 @@ def add_product_bundles_to_target(pick_list, target_doc, item_mapper, sales_orde @frappe.whitelist() -def create_stock_entry(pick_list: str): - pick_list = frappe.get_doc(json.loads(pick_list)) +def create_stock_entry(pick_list: str | dict): + pick_list = frappe.get_doc(frappe.parse_json(pick_list)) validate_item_locations(pick_list) if stock_entry_exists(pick_list.get("name")): diff --git a/erpnext/stock/doctype/purchase_receipt/mapper.py b/erpnext/stock/doctype/purchase_receipt/mapper.py index 273afcec7a9..528b6865ed5 100644 --- a/erpnext/stock/doctype/purchase_receipt/mapper.py +++ b/erpnext/stock/doctype/purchase_receipt/mapper.py @@ -60,8 +60,7 @@ def make_purchase_invoice( ): if args is None: args = {} - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) from erpnext.accounts.party import get_payment_terms_template diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py index b7dacb9c230..4f5967654ac 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py @@ -111,8 +111,7 @@ def apply_putaway_rule( purpose: Purpose of Stock Entry sync (optional): Sync with client side only for client side calls """ - if isinstance(items, str): - items = json.loads(items) + items = frappe.parse_json(items) items_not_accomodated, updated_table = [], [] item_wise_rules = defaultdict(list) @@ -198,7 +197,7 @@ def apply_putaway_rule( frappe.msgprint(_("Applied putaway rules."), alert=True) return updated_table - if sync and json.loads(sync): # sync with client side + if sync and frappe.parse_json(sync): # sync with client side return items diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 311396d2bd3..befb52c444a 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -364,8 +364,8 @@ class RepostItemValuation(Document): @frappe.whitelist() -def bulk_restart_reposting(names: str): - names = json.loads(names) +def bulk_restart_reposting(names: str | list): + names = frappe.parse_json(names) for name in names: doc = frappe.get_doc("Repost Item Valuation", name) if doc.status != "Failed": diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6ea89b2db8b..a8d9b9f1e7d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -222,8 +222,7 @@ def auto_fetch_serial_number( @frappe.whitelist() def get_pos_reserved_serial_nos(filters: str | dict): - if isinstance(filters, str): - filters = json.loads(filters) + filters = frappe.parse_json(filters) POSInvoice = frappe.qb.DocType("POS Invoice") POSInvoiceItem = frappe.qb.DocType("POS Invoice Item") diff --git a/erpnext/stock/doctype/stock_entry/services/manufacturing.py b/erpnext/stock/doctype/stock_entry/services/manufacturing.py index b3455c40b51..216d3c9eea9 100644 --- a/erpnext/stock/doctype/stock_entry/services/manufacturing.py +++ b/erpnext/stock/doctype/stock_entry/services/manufacturing.py @@ -1040,8 +1040,7 @@ def ceil_qty_if_uom_has_whole_number(qty, stock_uom): @frappe.whitelist() def move_sample_to_retention_warehouse(company: str, items: str | list): - if isinstance(items, str): - items = json.loads(items) + items = frappe.parse_json(items) retention_warehouse = frappe.get_single_value("Stock Settings", "sample_retention_warehouse") stock_entry = frappe.new_doc("Stock Entry") diff --git a/erpnext/stock/doctype/stock_entry/services/subcontracting.py b/erpnext/stock/doctype/stock_entry/services/subcontracting.py index d4b4921794d..5c3a1b89da1 100644 --- a/erpnext/stock/doctype/stock_entry/services/subcontracting.py +++ b/erpnext/stock/doctype/stock_entry/services/subcontracting.py @@ -253,8 +253,7 @@ def get_supplied_items( def get_items_from_subcontract_order(source_name: str, target_doc: str | Document | None = None): from erpnext.controllers.subcontracting_controller import make_rm_stock_entry - if isinstance(target_doc, str): - target_doc = frappe.get_doc(json.loads(target_doc)) + target_doc = frappe.get_doc(frappe.parse_json(target_doc)) order_doctype = "Purchase Order" if target_doc.purchase_order else "Subcontracting Order" target_doc = make_rm_stock_entry( diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 0ccd3a747b4..ab0496ac908 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1683,8 +1683,7 @@ def get_uom_details(item_code: str, uom: str, qty: float | None): @frappe.whitelist() def get_warehouse_details(args: str | dict): - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) args = frappe._dict(args) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index c2cf93ca1d2..341dd22c0b4 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -1239,8 +1239,7 @@ def get_stock_balance_for( item_dict = frappe.get_cached_value("Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1) - if isinstance(row, str): - row = json.loads(row) + row = frappe.parse_json(row) if isinstance(row, dict): row = frappe._dict(row) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index 4448ab8e52c..1e2d84d1b11 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -176,8 +176,7 @@ def get_children( if is_root: parent = "" - if isinstance(include_disabled, str): - include_disabled = json.loads(include_disabled) + include_disabled = frappe.parse_json(include_disabled) fields = ["name as value", "is_group as expandable"] diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index b11868347ea..11990db26b8 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -90,8 +90,7 @@ def get_item_details( item = frappe.get_cached_doc("Item", ctx.item_code) validate_item_details(ctx, item) - if isinstance(doc, str): - doc = json.loads(doc) + doc = frappe.parse_json(doc) if doc: ctx.transaction_date = doc.get("transaction_date") or doc.get("posting_date") diff --git a/erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py b/erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py index 83ff83e869d..46c0c5da4ea 100644 --- a/erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py +++ b/erpnext/stock/report/stock_qty_vs_batch_qty/stock_qty_vs_batch_qty.py @@ -100,12 +100,12 @@ def get_data(filters=None): @frappe.whitelist() -def update_batch_qty(selected_batches: str | None = None): +def update_batch_qty(selected_batches: str | list | None = None): frappe.has_permission("Batch", "write", throw=True, ignore_share_permissions=True) if not selected_batches: return - selected_batches = json.loads(selected_batches) + selected_batches = frappe.parse_json(selected_batches) for row in selected_batches: batch_name = row.get("batch") diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 1598860b165..8ebef95e47f 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -245,8 +245,7 @@ def get_incoming_rate(args: dict | str, raise_error_if_no_rate: bool = True, fal """Get Incoming Rate based on valuation method""" from erpnext.stock.stock_ledger import get_previous_sle, get_valuation_rate - if isinstance(args, str): - args = json.loads(args) + args = frappe.parse_json(args) if not args.get("posting_datetime") and args.get("posting_date"): args["posting_datetime"] = get_combine_datetime(args.get("posting_date"), args.get("posting_time")) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index e269246c0e6..adf46d0d7a2 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -217,8 +217,8 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord @frappe.whitelist() -def set_multiple_status(names: str, status: str): - for name in json.loads(names): +def set_multiple_status(names: str | list, status: str): + for name in frappe.parse_json(names): set_status(name, status) diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 0b6cbdbb830..33a0fa7f73f 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -13,13 +13,9 @@ def transaction_processing( frappe.has_permission(from_doctype, "read", throw=True) frappe.has_permission(to_doctype, "create", throw=True) - if isinstance(data, str): - deserialized_data = json.loads(data) - else: - deserialized_data = data + deserialized_data = frappe.parse_json(data) - if isinstance(args, str): - args = frappe._dict(json.loads(args)) + args = frappe._dict(frappe.parse_json(args)) skipped_records = [d for d in deserialized_data if d.get("status") in ("On Hold", "Closed")] diff --git a/erpnext/utilities/query.py b/erpnext/utilities/query.py index ef0ef3e716d..21759b36b97 100644 --- a/erpnext/utilities/query.py +++ b/erpnext/utilities/query.py @@ -62,8 +62,7 @@ def get_filter_conditions_qb(doctype, filters, ignore_permissions=None): if isinstance(filters, Criterion): return [filters] - if isinstance(filters, str): - filters = json.loads(filters) + filters = frappe.parse_json(filters) if isinstance(filters, dict): # Mirror get_filters_cond's dict normalization: a string value prefixed with "!" means diff --git a/erpnext/www/book_appointment/index.py b/erpnext/www/book_appointment/index.py index 14f5fe385b4..ef7985ed514 100644 --- a/erpnext/www/book_appointment/index.py +++ b/erpnext/www/book_appointment/index.py @@ -101,7 +101,7 @@ def get_available_slots_between(query_start_time, query_end_time, settings): @frappe.whitelist(allow_guest=True) -def create_appointment(date: str, time: str, tz: str, contact: str): +def create_appointment(date: str, time: str, tz: str, contact: str | dict): handle_appointment_booking_disabled() format_string = "%Y-%m-%d %H:%M:%S" scheduled_time = datetime.datetime.strptime(date + " " + time, format_string) @@ -112,7 +112,7 @@ def create_appointment(date: str, time: str, tz: str, contact: str): # Create a appointment document from form appointment = frappe.new_doc("Appointment") appointment.scheduled_time = scheduled_time - contact = json.loads(contact) + contact = frappe.parse_json(contact) appointment.customer_name = contact.get("name", None) appointment.customer_phone_number = contact.get("number", None) appointment.customer_skype = contact.get("skype", None)