From 34f40955eff1e75bc0cc24d14058084733ad37b7 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Wed, 22 Jan 2025 15:09:41 +0530 Subject: [PATCH 1/7] fix: css for actions summary --- .../gst_invoice_management_system.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js index d693e4348..2aa417aee 100644 --- a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js +++ b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js @@ -530,7 +530,7 @@ class IMS extends reconciliation.reconciliation_tabs { .join(""); const action_performed_html = ` -
+
${action_performed_cards}
`; From 42d17d368d4d445c63c932c5a66118dd108133eb Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Wed, 22 Jan 2025 15:15:07 +0530 Subject: [PATCH 2/7] fix: added bill_date column and mapping for linked voucher --- .../gst_invoice_management_system.js | 7 +++++++ .../public/js/components/data_table_manager.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js index 2aa417aee..b979f0cf1 100644 --- a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js +++ b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js @@ -347,6 +347,11 @@ class IMS extends reconciliation.reconciliation_tabs { align: "center", width: 120, }, + { + label: "Date", + fieldname: "bill_date", + _value: (...args) => frappe.datetime.str_to_user(args[0]), + }, { label: "Match Status", fieldname: "match_status", @@ -415,6 +420,7 @@ class IMS extends reconciliation.reconciliation_tabs { data.push({ supplier_name_gstin: this.get_supplier_name_gstin(row), bill_no: row.bill_no, + bill_date: row.bill_date, classification: row._inward_supply.classification, ims_action: row.ims_action || "", match_status: row.match_status, @@ -424,6 +430,7 @@ class IMS extends reconciliation.reconciliation_tabs { inward_supply_name: row.inward_supply_name, pending_upload: row.pending_upload, is_supplier_return_filed: row.is_supplier_return_filed, + linked_voucher_type: row._purchase_invoice.doctype, }); }); diff --git a/india_compliance/public/js/components/data_table_manager.js b/india_compliance/public/js/components/data_table_manager.js index fe88a1eb3..02fab052a 100644 --- a/india_compliance/public/js/components/data_table_manager.js +++ b/india_compliance/public/js/components/data_table_manager.js @@ -82,7 +82,7 @@ india_compliance.DataTableManager = class DataTableManager { value = frappe.format(value, column, { always_show_decimals: true }, data); - if (column.post_format) { + if (column._after_format) { value = column._after_format(value, column, data); } From 4eb8ca3a5847e70453a54bfda894ffafca746f3b Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Wed, 22 Jan 2025 17:26:04 +0530 Subject: [PATCH 3/7] fix: more reliable way to link integration request --- .../gst_india/api_classes/taxpayer_base.py | 2 ++ .../gst_invoice_management_system.py | 14 ++++++------- .../doctype/gstr_action/gstr_action.json | 9 ++++++++- .../doctype/gstr_action/gstr_action.py | 20 +------------------ india_compliance/gst_india/utils/api.py | 15 ++++++++++++-- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index da77b0058..1a7efdcd4 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -367,9 +367,11 @@ def get(self, *args, **kwargs): return self._request("get", *args, **kwargs, params=params) def post(self, *args, **kwargs): + self.default_log_values.update(update_gstr_action=True) return self._request("post", *args, **kwargs) def put(self, *args, **kwargs): + self.default_log_values.update(update_gstr_action=True) return self._request("put", *args, **kwargs) def before_request(self, request_args): diff --git a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py index ed8bc1ad9..8a8fe44ff 100644 --- a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py +++ b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py @@ -384,31 +384,29 @@ def process_save_or_reset_ims(return_log, action): if status_cd in ["P", "PE"]: # Exclude erroneous invoices from previous IMS action update - # This is enqueued because linking of integration request is enqueued + # This is enqueued because creation of integration request is enqueued # TODO: flag for re-upload? frappe.enqueue( update_previous_ims_action, queue="long", - integration_request=doc.integration_request, + request_id=doc.request_id, error_report=response.get("error_report") or dict(), ) return response -def update_previous_ims_action(integration_request, error_report=None): - uploaded_invoices = get_uploaded_invoices(integration_request) +def update_previous_ims_action(request_id, error_report=None): + uploaded_invoices = get_uploaded_invoices(request_id) for category, invoices in uploaded_invoices.items(): _class = get_data_handler(ReturnType.IMS.value, category.upper()) _class().update_previous_ims_action(invoices, error_report.get(category, [])) -def get_uploaded_invoices(integration_request): +def get_uploaded_invoices(request_id): request_data = frappe.parse_json( - frappe.db.get_value( - "Integration Request", {"name": integration_request}, "data" - ) + frappe.db.get_value("Integration Request", {"request_id": request_id}, "data") ) if not request_data: diff --git a/india_compliance/gst_india/doctype/gstr_action/gstr_action.json b/india_compliance/gst_india/doctype/gstr_action/gstr_action.json index 39dc83d30..1517cdc02 100644 --- a/india_compliance/gst_india/doctype/gstr_action/gstr_action.json +++ b/india_compliance/gst_india/doctype/gstr_action/gstr_action.json @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "request_type", + "request_id", "token", "status", "creation_time", @@ -44,12 +45,18 @@ "label": "Integration Request", "options": "Integration Request", "read_only": 1 + }, + { + "fieldname": "request_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Request id" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-09-12 12:36:05.679413", + "modified": "2025-01-22 16:33:57.014368", "modified_by": "Administrator", "module": "GST India", "name": "GSTR Action", diff --git a/india_compliance/gst_india/doctype/gstr_action/gstr_action.py b/india_compliance/gst_india/doctype/gstr_action/gstr_action.py index 5f99f6a0a..5d4263f5c 100644 --- a/india_compliance/gst_india/doctype/gstr_action/gstr_action.py +++ b/india_compliance/gst_india/doctype/gstr_action/gstr_action.py @@ -15,6 +15,7 @@ def set_gstr_actions(doc, request_type, token, request_id, status=None): row = { "request_type": request_type, + "request_id": request_id, "token": token, "creation_time": frappe.utils.now_datetime(), } @@ -24,22 +25,3 @@ def set_gstr_actions(doc, request_type, token, request_id, status=None): doc.append("actions", row) doc.save() - enqueue_link_integration_request(token, request_id) - - -def enqueue_link_integration_request(token, request_id): - """ - Integration request is enqueued. Hence, it's name is not available immediately. - Hence, link it after the request is processed. - """ - frappe.enqueue( - link_integration_request, queue="long", token=token, request_id=request_id - ) - - -def link_integration_request(token, request_id): - doc_name = frappe.db.get_value("Integration Request", {"request_id": request_id}) - if doc_name: - frappe.db.set_value( - "GSTR Action", {"token": token}, {"integration_request": doc_name} - ) diff --git a/india_compliance/gst_india/utils/api.py b/india_compliance/gst_india/utils/api.py index efcb63df3..81cb46222 100644 --- a/india_compliance/gst_india/utils/api.py +++ b/india_compliance/gst_india/utils/api.py @@ -17,8 +17,9 @@ def create_integration_request( error=None, reference_doctype=None, reference_name=None, + update_gstr_action=False, ): - return frappe.get_doc( + doc = frappe.get_doc( { "doctype": "Integration Request", "integration_request_service": "India Compliance API", @@ -32,7 +33,17 @@ def create_integration_request( "reference_doctype": reference_doctype, "reference_docname": reference_name, } - ).insert(ignore_permissions=True) + ) + doc.insert(ignore_permissions=True) + + if update_gstr_action: + link_integration_request(request_id, doc.name) + + +def link_integration_request(request_id, doc_name): + frappe.db.set_value( + "GSTR Action", {"request_id": request_id}, {"integration_request": doc_name} + ) def pretty_json(obj): From 76fcea975f4743fcaef093851c5e5bcac2f17c22 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Wed, 22 Jan 2025 17:31:07 +0530 Subject: [PATCH 4/7] fix: reverse_charge filter for purchase reconciliation tool --- .../doctype/purchase_reconciliation_tool/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/__init__.py b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/__init__.py index fd8bd3a64..4137c0558 100644 --- a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/__init__.py +++ b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/__init__.py @@ -1137,6 +1137,7 @@ def process_data(self, reconciliation_data: list, retain_doc: bool = False): "differences": "", "action": "", "classification": "", + "is_reverse_charge": "", } for data in reconciliation_data: @@ -1154,7 +1155,13 @@ def process_data(self, reconciliation_data: list, retain_doc: bool = False): BaseUtil.update_cess_amount(purchase) def update_fields(self, data, purchase, inward_supply): - for field in ("supplier_name", "supplier_gstin", "bill_no", "bill_date"): + for field in ( + "supplier_name", + "supplier_gstin", + "bill_no", + "bill_date", + "is_reverse_charge", + ): data[field] = purchase.get(field) or inward_supply.get(field) data.update( From 957978e742a0507b8a1d1c8c81ff422ee5bae23f Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Thu, 23 Jan 2025 10:23:41 +0530 Subject: [PATCH 5/7] fix: format bill_date and bill_no --- .../gst_invoice_management_system.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js index b979f0cf1..d9ff5f443 100644 --- a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js +++ b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.js @@ -343,15 +343,10 @@ class IMS extends reconciliation.reconciliation_tabs { }, { label: "Bill No.", - fieldname: "bill_no", + fieldname: "bill_no_date", align: "center", width: 120, }, - { - label: "Date", - fieldname: "bill_date", - _value: (...args) => frappe.datetime.str_to_user(args[0]), - }, { label: "Match Status", fieldname: "match_status", @@ -419,8 +414,7 @@ class IMS extends reconciliation.reconciliation_tabs { data.push({ supplier_name_gstin: this.get_supplier_name_gstin(row), - bill_no: row.bill_no, - bill_date: row.bill_date, + bill_no_date: this.get_bill_no_bill_date(row), classification: row._inward_supply.classification, ims_action: row.ims_action || "", match_status: row.match_status, @@ -560,6 +554,14 @@ class IMS extends reconciliation.reconciliation_tabs { me.update_filter(e, "ims_action", action, me); }); } + + get_bill_no_bill_date(row) { + return ` + ${row.bill_no} +
+ ${frappe.datetime.str_to_user(row.bill_date) || ""} + `; + } } class IMSAction { From 91d7f8231823c9b10a1b0c16b446473dcfa0d570 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Thu, 23 Jan 2025 13:16:47 +0530 Subject: [PATCH 6/7] fix: delete transactions marked as rejected --- .../gst_india/utils/gstr_2/__init__.py | 20 +++++- .../gst_india/utils/gstr_2/gstr.py | 64 +++++++++---------- .../gst_india/utils/gstr_2/gstr_2a.py | 12 +--- .../gst_india/utils/gstr_2/gstr_2b.py | 41 +++++------- .../gst_india/utils/gstr_2/ims.py | 16 ++--- 5 files changed, 75 insertions(+), 78 deletions(-) diff --git a/india_compliance/gst_india/utils/gstr_2/__init__.py b/india_compliance/gst_india/utils/gstr_2/__init__.py index 8d373ad58..742d47bc1 100644 --- a/india_compliance/gst_india/utils/gstr_2/__init__.py +++ b/india_compliance/gst_india/utils/gstr_2/__init__.py @@ -306,6 +306,7 @@ def save_gstr_2b(gstin, return_period, json_data): return_type, return_period, json_data.get("docdata"), + json_data.get("docRejdata"), json_data.get("gendt"), ) update_import_history(return_period) @@ -316,7 +317,12 @@ def save_ims_invoices(gstin, return_period, json_data): def save_gstr( - gstin, return_type: ReturnType, return_period, json_data, gen_date_2b=None + gstin, + return_type: ReturnType, + return_period, + json_data, + rejected_data=None, + gen_date_2b=None, ): """Save GSTR data to Inward Supply @@ -324,6 +330,8 @@ def save_gstr( :param json_data: dict of list (GSTR category: suppliers) :param gen_date_2b: str (Date when GSTR 2B was generated) """ + if not rejected_data: + rejected_data = {} company = get_party_for_gstin(gstin, "Company") for category in GSTRCategory: @@ -331,8 +339,14 @@ def save_gstr( if not gstr: continue - gstr(company, gstin, return_period, json_data, gen_date_2b).create_transactions( - category, + gstr( + company, + gstin, + return_period, + json_data, + rejected_data.get(category.value.lower()), + gen_date_2b, + ).create_transactions( json_data.get(category.value.lower()), ) diff --git a/india_compliance/gst_india/utils/gstr_2/gstr.py b/india_compliance/gst_india/utils/gstr_2/gstr.py index e77ff2bb9..83baa2e81 100644 --- a/india_compliance/gst_india/utils/gstr_2/gstr.py +++ b/india_compliance/gst_india/utils/gstr_2/gstr.py @@ -32,40 +32,42 @@ class GSTR: } ) - def __init__(self, company, gstin, return_period, data, gen_date_2b): + def __init__(self, company, gstin, return_period, data, rejected_data, gen_date_2b): self.company = company self.gstin = gstin self.return_period = return_period self._data = data + self.rejected_data = rejected_data self.gen_date_2b = gen_date_2b + self.category = type(self).__name__[6:] self.setup() def setup(self): self.existing_transaction = self.get_existing_transaction() - def create_transactions(self, category, suppliers): - if not suppliers: - return - - transactions = self.get_all_transactions(category, suppliers) - total_transactions = len(transactions) - current_transaction = 0 - - for transaction in transactions: - create_inward_supply(transaction) - - current_transaction += 1 - frappe.publish_realtime( - "update_2a_2b_transactions_progress", - { - "current_progress": current_transaction * 100 / total_transactions, - "return_period": self.return_period, - }, - user=frappe.session.user, - ) - - if transaction.get("unique_key") in self.existing_transaction: - self.existing_transaction.pop(transaction.get("unique_key")) + def create_transactions(self, suppliers): + if suppliers: + transactions = self.get_all_transactions(suppliers) + total_transactions = len(transactions) + current_transaction = 0 + + for transaction in transactions: + create_inward_supply(transaction) + + current_transaction += 1 + frappe.publish_realtime( + "update_2a_2b_transactions_progress", + { + "current_progress": current_transaction + * 100 + / total_transactions, + "return_period": self.return_period, + }, + user=frappe.session.user, + ) + + if transaction.get("unique_key") in self.existing_transaction: + self.existing_transaction.pop(transaction.get("unique_key")) self.handle_missing_transactions() @@ -75,28 +77,26 @@ def handle_missing_transactions(self): def get_existing_transaction(self): return {} - def get_all_transactions(self, category, suppliers): + def get_all_transactions(self, suppliers): transactions = [] for supplier in suppliers: - transactions.extend(self.get_supplier_transactions(category, supplier)) + transactions.extend(self.get_supplier_transactions(supplier)) self.update_gstins() return transactions - def get_supplier_transactions(self, category, supplier): + def get_supplier_transactions(self, supplier): return [ - self.get_transaction( - category, frappe._dict(supplier), frappe._dict(invoice) - ) + self.get_transaction(frappe._dict(supplier), frappe._dict(invoice)) for invoice in supplier.get(self.get_key("invoice_key")) ] - def get_transaction(self, category, supplier, invoice): + def get_transaction(self, supplier, invoice): transaction = frappe._dict( company=self.company, company_gstin=self.gstin, - classification=category.value, + classification=self.category, **self.get_supplier_details(supplier), **self.get_invoice_details(invoice), **self.get_download_details(), diff --git a/india_compliance/gst_india/utils/gstr_2/gstr_2a.py b/india_compliance/gst_india/utils/gstr_2/gstr_2a.py index e76fd8990..3c01ed706 100644 --- a/india_compliance/gst_india/utils/gstr_2/gstr_2a.py +++ b/india_compliance/gst_india/utils/gstr_2/gstr_2a.py @@ -19,14 +19,12 @@ def setup(self): self.cancelled_gstins = {} def get_existing_transaction(self): - category = type(self).__name__[6:] - gst_is = frappe.qb.DocType("GST Inward Supply") existing_transactions = ( frappe.qb.from_(gst_is) .select(gst_is.name, gst_is.supplier_gstin, gst_is.bill_no) .where(gst_is.sup_return_period == self.return_period) - .where(gst_is.classification == category) + .where(gst_is.classification == self.category) .where(gst_is.gstr_1_filled == 0) ).run(as_dict=True) @@ -262,12 +260,8 @@ def get_invoice_details(self, invoice): } # invoice details are included in supplier details - def get_supplier_transactions(self, category, supplier): - return [ - self.get_transaction( - category, frappe._dict(supplier), frappe._dict(supplier) - ) - ] + def get_supplier_transactions(self, supplier): + return [self.get_transaction(frappe._dict(supplier), frappe._dict(supplier))] # item details are not available def get_transaction_items(self, invoice): diff --git a/india_compliance/gst_india/utils/gstr_2/gstr_2b.py b/india_compliance/gst_india/utils/gstr_2/gstr_2b.py index 9d898aa8a..4f9d7f89d 100644 --- a/india_compliance/gst_india/utils/gstr_2/gstr_2b.py +++ b/india_compliance/gst_india/utils/gstr_2/gstr_2b.py @@ -1,5 +1,4 @@ import frappe -from frappe.query_builder.functions import IfNull from india_compliance.gst_india.utils import parse_datetime from india_compliance.gst_india.utils.gstr_2.gstr import GSTR, get_mapped_value @@ -7,14 +6,12 @@ class GSTR2b(GSTR): def get_existing_transaction(self): - category = type(self).__name__[6:] - gst_is = frappe.qb.DocType("GST Inward Supply") existing_transactions = ( frappe.qb.from_(gst_is) .select(gst_is.name, gst_is.supplier_gstin, gst_is.bill_no) .where(gst_is.return_period_2b == self.return_period) - .where(gst_is.classification == category) + .where(gst_is.classification == self.category) ).run(as_dict=True) return { @@ -31,12 +28,13 @@ def handle_missing_transactions(self): In such cases, 1) we need to clear the return_period_2b as this could change in future. - 2) safer to clear delete them as well if no matching transactions are found (possibly rejected). + 2) and delete the rejected transactions. """ if not self.existing_transaction: return missing_transactions = list(self.existing_transaction.values()) + rejected_transactions = self.get_all_transactions(self.rejected_data or []) # clear return_period_2b inward_supply = frappe.qb.DocType("GST Inward Supply") @@ -48,22 +46,19 @@ def handle_missing_transactions(self): .run() ) - # delete unmatched transactions - unmatched_transactions = ( - frappe.qb.from_(inward_supply) - .select(inward_supply.name) - .where(inward_supply.name.isin(missing_transactions)) - .where(IfNull(inward_supply.link_name, "") == "") - .run(pluck=True) - ) + # delete rejected transactions + for transaction in rejected_transactions: + filters = { + "bill_no": transaction.bill_no, + "bill_date": transaction.bill_date, + "classification": transaction.classification, + "supplier_gstin": transaction.supplier_gstin, + } - for transaction_name in unmatched_transactions: - frappe.delete_doc( - "GST Inward Supply", transaction_name, ignore_permissions=True - ) + frappe.delete_doc("GST Inward Supply", filters, ignore_permissions=True) - def get_transaction(self, category, supplier, invoice): - transaction = super().get_transaction(category, supplier, invoice) + def get_transaction(self, supplier, invoice): + transaction = super().get_transaction(supplier, invoice) transaction.return_period_2b = self.return_period transaction.gen_date_2b = parse_datetime(self.gen_date_2b, day_first=True) return transaction @@ -242,9 +237,5 @@ def get_supplier_details(self, supplier): return {} # invoice details are included in supplier details - def get_supplier_transactions(self, category, supplier): - return [ - self.get_transaction( - category, frappe._dict(supplier), frappe._dict(supplier) - ) - ] + def get_supplier_transactions(self, supplier): + return [self.get_transaction(frappe._dict(supplier), frappe._dict(supplier))] diff --git a/india_compliance/gst_india/utils/gstr_2/ims.py b/india_compliance/gst_india/utils/gstr_2/ims.py index 13c0bed85..e8cac2552 100644 --- a/india_compliance/gst_india/utils/gstr_2/ims.py +++ b/india_compliance/gst_india/utils/gstr_2/ims.py @@ -43,19 +43,17 @@ def __init__(self, company=None, gstin=None, *args): self.company = company self.existing_transactions = self.get_existing_transactions() - def create_transactions(self, category, invoices): + def create_transactions(self, invoices): self.reset_previous_ims_action() - if not invoices: - return - - transactions = self.get_all_transactions(invoices) + if invoices: + transactions = self.get_all_transactions(invoices) - for transaction in transactions: - create_inward_supply(transaction) + for transaction in transactions: + create_inward_supply(transaction) - if transaction.get("unique_key") in self.existing_transactions: - self.existing_transactions.pop(transaction.get("unique_key")) + if transaction.get("unique_key") in self.existing_transactions: + self.existing_transactions.pop(transaction.get("unique_key")) self.handle_missing_transactions() From 48a456199302b68efabd1f8a811e1b447967206e Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Thu, 23 Jan 2025 13:21:01 +0530 Subject: [PATCH 7/7] fix: throw when no request data found --- .../gst_invoice_management_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py index 8a8fe44ff..9570db727 100644 --- a/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py +++ b/india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py @@ -410,7 +410,7 @@ def get_uploaded_invoices(request_id): ) if not request_data: - return {} + frappe.throw(_("Request data not found")) if isinstance(request_data, str): request_data = frappe.parse_json(request_data)