Merge pull request #56439 from mihir-kandoi/migrate-request-type-json

fix!: switch ERPNext to JSON request body (use_json_request_body)
This commit is contained in:
Mihir Kandoi
2026-06-24 22:37:59 +05:30
committed by GitHub
76 changed files with 138 additions and 205 deletions

View File

@@ -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:

View File

@@ -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}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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"]})

View File

@@ -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")

View File

@@ -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)

View File

@@ -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")

View File

@@ -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

View File

@@ -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:

View File

@@ -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."))

View File

@@ -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:

View File

@@ -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(",")

View File

@@ -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",

View File

@@ -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)

View File

@@ -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")

View File

@@ -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

View File

@@ -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

View File

@@ -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."))

View File

@@ -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",

View File

@@ -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]

View File

@@ -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:

View File

@@ -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"

View File

@@ -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")

View File

@@ -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")

View File

@@ -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:

View File

@@ -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"):

View File

@@ -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:

View File

@@ -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):
"""

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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}"

View File

@@ -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

View File

@@ -585,7 +585,7 @@ class BOM(WebsiteGenerator):
if isinstance(kwargs, str):
import json
kwargs = json.loads(kwargs)
kwargs = frappe.parse_json(kwargs)
return kwargs

View File

@@ -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

View File

@@ -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 []

View File

@@ -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

View File

@@ -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"))

View File

@@ -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

View File

@@ -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"))

View File

@@ -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 ""

View File

@@ -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")

View File

@@ -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": ("!=", "")}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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]

View File

@@ -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:

View File

@@ -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(
{

View File

@@ -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 = {}

View File

@@ -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 = []

View File

@@ -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)

View File

@@ -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")

View File

@@ -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)

View File

@@ -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"

View File

@@ -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)

View File

@@ -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")):

View File

@@ -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

View File

@@ -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

View File

@@ -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":

View File

@@ -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")

View File

@@ -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")

View File

@@ -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(

View File

@@ -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)

View File

@@ -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)

View File

@@ -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"]

View File

@@ -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")

View File

@@ -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")

View File

@@ -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"))

View File

@@ -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)

View File

@@ -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")]

View File

@@ -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

View File

@@ -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)