Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions india_compliance/audit_trail/overrides/customize_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class CustomizeForm(_CustomizeForm):
@frappe.whitelist()
def fetch_to_customize(self):
def fetch_to_customize(self) -> dict:
self.set_onload(
"audit_trail_enabled",
self.doc_type
Expand All @@ -23,7 +23,7 @@ def fetch_to_customize(self):
return super().fetch_to_customize()

@frappe.whitelist()
def save_customization(self):
def save_customization(self) -> str:
self.validate_audit_trail_integrity()
return super().save_customization()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ def append_rows(self, new_records, modified_records, doctype):


@frappe.whitelist()
def get_relevant_doctypes():
def get_relevant_doctypes() -> set[str]:
doctypes = get_audit_trail_doctypes()
doctypes.remove("Accounts Settings")

Expand Down
6 changes: 3 additions & 3 deletions india_compliance/audit_trail/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def is_audit_trail_enabled():


@frappe.whitelist()
def get_audit_trail_doctypes():
def get_audit_trail_doctypes() -> set:
return set(frappe.get_hooks("audit_trail_doctypes"))


Expand All @@ -19,12 +19,12 @@ def enqueue_disable_audit_trail_notification():


@frappe.whitelist(methods=["POST"])
def disable_audit_trail_notification():
def disable_audit_trail_notification() -> None:
frappe.defaults.clear_user_default("needs_audit_trail_notification")


@frappe.whitelist(methods=["POST"])
def enable_audit_trail():
def enable_audit_trail() -> None:
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.enable_audit_trail = 1
accounts_settings.flags.ignore_version = True
Expand Down
15 changes: 11 additions & 4 deletions india_compliance/gst_india/doctype/bill_of_entry/bill_of_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ def get_asset_items(self):
return asset_items

@frappe.whitelist()
def get_items_from_purchase_invoice(self, purchase_invoices):
def get_items_from_purchase_invoice(self, purchase_invoices: list[str]) -> None:
if not purchase_invoices:
frappe.msgprint(_("No Purchase Invoices selected"))
return
Expand Down Expand Up @@ -512,7 +512,9 @@ def set_missing_values(source, target=None):


@frappe.whitelist()
def make_bill_of_entry(source_name, target_doc=None):
def make_bill_of_entry(
source_name: str, target_doc: dict | str | None = None
) -> Document:
"""
Permission checked in get_mapped_doc
"""
Expand Down Expand Up @@ -550,7 +552,9 @@ def update_item_qty(source, target, source_parent):


@frappe.whitelist()
def make_journal_entry_for_payment(source_name, target_doc=None):
def make_journal_entry_for_payment(
source_name: str, target_doc: dict | str | None = None
) -> Document:
"""
Permission checked in get_mapped_doc
"""
Expand Down Expand Up @@ -600,7 +604,9 @@ def set_missing_values(source, target):


@frappe.whitelist()
def make_landed_cost_voucher(source_name, target_doc=None):
def make_landed_cost_voucher(
source_name: str, target_doc: dict | str | None = None
) -> Document:
"""
Permission checked in get_mapped_doc
"""
Expand Down Expand Up @@ -794,6 +800,7 @@ def get_pi_items(purchase_invoices):


@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def fetch_pending_boe_invoices(doctype, txt, searchfield, start, page_len, filters):
frappe.has_permission("Purchase Invoice", "read", throw=True)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def validate(self):


@frappe.whitelist()
def update_taxes_in_item_master(taxes, hsn_code):
def update_taxes_in_item_master(taxes: list[dict] | str, hsn_code: str) -> int:
frappe.enqueue(update_item_document, taxes=taxes, hsn_code=hsn_code, queue="long")
return 1
Comment on lines +21 to 23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate mass item updates behind write permission.

This whitelisted endpoint enqueues updates to all Item records matching an HSN—privileged operation. Add an explicit write-permission check on Item before enqueue.

 @frappe.whitelist()
 def update_taxes_in_item_master(taxes: list[dict] | str, hsn_code: str) -> int:
+    frappe.has_permission("Item", "write", throw=True)
     frappe.enqueue(update_item_document, taxes=taxes, hsn_code=hsn_code, queue="long")
     return 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def update_taxes_in_item_master(taxes: list[dict] | str, hsn_code: str) -> int:
frappe.enqueue(update_item_document, taxes=taxes, hsn_code=hsn_code, queue="long")
return 1
@frappe.whitelist()
def update_taxes_in_item_master(taxes: list[dict] | str, hsn_code: str) -> int:
# enforce write permission for Item before enqueueing a potentially mass update
frappe.has_permission("Item", "write", throw=True)
frappe.enqueue(update_item_document, taxes=taxes, hsn_code=hsn_code, queue="long")
return 1
🤖 Prompt for AI Agents
In india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.py around lines
21-23, the function enqueues mass updates to Item records without verifying the
caller has write permission on the Item doctype; add an explicit permission
check before enqueueing: call frappe.has_permission("Item", ptype="write") and
if it fails either raise a PermissionError (frappe.PermissionError or
frappe.throw with appropriate HTTP 403 message) or return an error/zero, and
only enqueue update_item_document when the permission check passes.


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

class GSTInvoiceManagementSystem(Document):
@frappe.whitelist()
def autoreconcile_and_get_data(self):
def autoreconcile_and_get_data(self) -> dict:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

filters = frappe._dict(
Expand Down Expand Up @@ -142,7 +142,7 @@ def get_pending_actions(self):
)

@frappe.whitelist()
def update_action(self, invoice_names, action):
def update_action(self, invoice_names: list[str] | str, action: str) -> None:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

invoice_names = frappe.parse_json(invoice_names)
Expand Down Expand Up @@ -181,7 +181,7 @@ def update_action(self, invoice_names, action):
)

@frappe.whitelist()
def get_invoice_details(self, purchase_name, inward_supply_name):
def get_invoice_details(self, purchase_name: str, inward_supply_name: str) -> dict:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

inward_supply = InwardSupply().get_all(
Expand All @@ -205,7 +205,9 @@ def get_invoice_details(self, purchase_name, inward_supply_name):
return reconciliation_data[0]

@frappe.whitelist()
def link_documents(self, purchase_invoice_name, inward_supply_name, link_doctype):
def link_documents(
self, purchase_invoice_name: str, inward_supply_name: str, link_doctype: str
) -> None:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

purchases, inward_supplies = _link_documents(
Expand All @@ -215,15 +217,15 @@ def link_documents(self, purchase_invoice_name, inward_supply_name, link_doctype
return self.get_invoice_data(inward_supplies, purchases)

@frappe.whitelist()
def unlink_documents(self, data):
def unlink_documents(self, data: dict | str) -> dict:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

purchases, inward_supplies = _unlink_documents(data)

return self.get_invoice_data(inward_supplies, purchases)

Comment on lines +220 to 226
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Return type mismatch for unlink_documents

This returns self.get_invoice_data(...) (list), but annotation says -> dict.

Apply this diff:

-    def unlink_documents(self, data: dict | str) -> dict:
+    def unlink_documents(self, data: dict | str) -> list[dict]:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def unlink_documents(self, data: dict | str) -> dict:
frappe.has_permission("GST Invoice Management System", "write", throw=True)
purchases, inward_supplies = _unlink_documents(data)
return self.get_invoice_data(inward_supplies, purchases)
def unlink_documents(self, data: dict | str) -> list[dict]:
frappe.has_permission("GST Invoice Management System", "write", throw=True)
purchases, inward_supplies = _unlink_documents(data)
return self.get_invoice_data(inward_supplies, purchases)
🤖 Prompt for AI Agents
In
india_compliance/gst_india/doctype/gst_invoice_management_system/gst_invoice_management_system.py
around lines 220 to 226, the method unlink_documents is annotated to return dict
but actually returns the result of self.get_invoice_data(...) which is a list;
update the signature to reflect the correct return type (e.g., -> list[dict] or
-> list) to match get_invoice_data, or alternatively change the method to return
a dict by wrapping or converting the list into the expected dict structure;
ensure imports/typing are adjusted if using list[dict].

@frappe.whitelist()
def get_link_options(self, doctype, filters):
def get_link_options(self, doctype, filters: dict) -> list[dict]:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

if isinstance(filters, dict):
Expand All @@ -245,7 +247,7 @@ def get_link_options(self, doctype, filters):

@frappe.whitelist()
@otp_handler
def download_invoices(company_gstin):
def download_invoices(company_gstin: str) -> None:
frappe.has_permission("GST Invoice Management System", "write", throw=True)

TaxpayerBaseAPI(company_gstin).validate_auth_token()
Expand All @@ -255,7 +257,7 @@ def download_invoices(company_gstin):

@frappe.whitelist()
@otp_handler
def save_invoices(company_gstin):
def save_invoices(company_gstin: str) -> None:
frappe.has_permission("GST Invoice Management System", "write", throw=True)
frappe.has_permission("GST Return Log", "write", throw=True)

Expand All @@ -264,7 +266,7 @@ def save_invoices(company_gstin):

@frappe.whitelist()
@otp_handler
def reset_invoices(company_gstin):
def reset_invoices(company_gstin: str) -> None:
frappe.has_permission("GST Invoice Management System", "write", throw=True)
frappe.has_permission("GST Return Log", "write", throw=True)

Expand All @@ -273,7 +275,7 @@ def reset_invoices(company_gstin):

@frappe.whitelist()
@otp_handler
def sync_with_gstn_and_reupload(company_gstin):
def sync_with_gstn_and_reupload(company_gstin: str) -> None:
frappe.has_permission("GST Invoice Management System", "write", throw=True)
frappe.has_permission("GST Return Log", "write", throw=True)

Expand All @@ -288,7 +290,7 @@ def sync_with_gstn_and_reupload(company_gstin):

@frappe.whitelist()
@otp_handler
def check_action_status(company_gstin, action):
def check_action_status(company_gstin: str, action: str) -> dict:
frappe.has_permission("GST Return Log", "write", throw=True)

ims_log = frappe.get_doc(
Expand All @@ -300,15 +302,15 @@ def check_action_status(company_gstin, action):


@frappe.whitelist()
def download_excel_report(data, doc):
def download_excel_report(data: str, doc: str) -> None:
frappe.has_permission("GST Invoice Management System", "export", throw=True)

build_data = BuildExcelIMS(doc, data)
build_data.export_data()


@frappe.whitelist()
def get_period_options(company, company_gstin):
def get_period_options(company: str, company_gstin: str) -> list[str]:
def format_period(period):
return period[2:] + period[:2]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def get_unprocessed_action(self, action):


@frappe.whitelist()
def download_file():
def download_file() -> None:
frappe.has_permission("GST Return Log", "read", throw=True)

data = frappe._dict(frappe.local.form_dict)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,13 +405,13 @@ def is_gstr1_api_enabled(self, gstin, warn_for_missing_credentials=False):


@frappe.whitelist()
def disable_api_promo():
def disable_api_promo() -> None:
if frappe.has_permission("GST Settings", "write"):
_disable_api_promo()


@frappe.whitelist()
def enqueue_update_gst_category():
def enqueue_update_gst_category() -> None:
frappe.has_permission("GST Settings", "write", throw=True)
frappe.enqueue(update_gst_category, queue="long", timeout=6000)
frappe.msgprint(
Expand Down
12 changes: 8 additions & 4 deletions india_compliance/gst_india/doctype/gstin/gstin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ def before_save(self):
self.cancelled_date = self.registration_date

@frappe.whitelist()
def update_gstin_status(self):
def update_gstin_status(self) -> None:
"""
Permission check not required as GSTIN details are public and user has access to doc.
"""
# hard refresh will always use public API
create_or_update_gstin_status(self.gstin, throw=True, doc=self)

@frappe.whitelist()
def update_transporter_id_status(self):
def update_transporter_id_status(self) -> None:
"""
Permission check not required as GSTIN details are public and user has access to doc.
"""
Expand Down Expand Up @@ -134,7 +134,9 @@ def get_and_validate_gstin_status(gstin, doc):


@frappe.whitelist()
def get_gstin_status(gstin, doc=None, force_update=False):
def get_gstin_status(
gstin: str, doc: str | dict | None = None, force_update: bool = False
) -> Document | None:
"""
Get GSTIN status. Responds immediately, and best suited for Frontend use.
Permission check not required as GSTIN details are public where GSTIN is known.
Expand Down Expand Up @@ -242,7 +244,9 @@ def is_status_refresh_required(gstin, transaction_date, docstatus=0):


@frappe.whitelist()
def validate_gst_transporter_id(transporter_id, doc=None):
def validate_gst_transporter_id(
transporter_id: str, doc: str | dict | None = None
) -> None:
"""
Validates GST Transporter ID and warns user if transporter_id is not Active.
Just suggestive and not enforced.
Expand Down
Loading
Loading