├── india_compliance ├── public │ ├── .gitkeep │ ├── images │ │ ├── mail-box.png │ │ └── india-compliance-logo.png │ └── js │ │ ├── gstr1.bundle.js │ │ ├── india_compliance_account │ │ ├── utils.js │ │ ├── constants.js │ │ ├── store │ │ │ └── index.js │ │ ├── components │ │ │ ├── PageTitle.vue │ │ │ ├── PreLoader.vue │ │ │ ├── Message.vue │ │ │ ├── auth │ │ │ │ └── MarketingInfoCheckIcon.vue │ │ │ ├── Loading.vue │ │ │ ├── invoice_history_table.html │ │ │ └── TheFooter.vue │ │ ├── pages │ │ │ └── PageNotFound.vue │ │ ├── router.js │ │ └── services │ │ │ ├── AccountService.js │ │ │ └── AuthService.js │ │ ├── ims.bundle.js │ │ ├── purchase_reconciliation_tool.bundle.js │ │ ├── india_compliance.bundle.js │ │ ├── custom_number_card.js │ │ ├── components │ │ ├── set_gstin_options.js │ │ └── number_card.js │ │ ├── regex_constants.js │ │ ├── new_gst_category_notification.js │ │ └── quick_info_popover.js ├── www │ └── __init__.py ├── config │ ├── __init__.py │ ├── docs.py │ └── desktop.py ├── gst_india │ ├── __init__.py │ ├── page │ │ ├── __init__.py │ │ └── india_compliance_account │ │ │ ├── india_compliance_account.js │ │ │ └── india_compliance_account.json │ ├── doctype │ │ ├── __init__.py │ │ ├── gstin │ │ │ ├── __init__.py │ │ │ ├── gstin.js │ │ │ └── test_gstin.py │ │ ├── gstr_1 │ │ │ ├── __init__.py │ │ │ └── test_gstr_1.py │ │ ├── pan │ │ │ ├── __init__.py │ │ │ ├── test_pan.py │ │ │ ├── pan.js │ │ │ └── pan.json │ │ ├── gst_account │ │ │ ├── __init__.py │ │ │ └── gst_account.py │ │ ├── gst_uom_map │ │ │ ├── __init__.py │ │ │ ├── gst_uom_map.py │ │ │ └── gst_uom_map.json │ │ ├── gstr_action │ │ │ ├── __init__.py │ │ │ ├── gstr_action.py │ │ │ └── gstr_action.json │ │ ├── bill_of_entry │ │ │ └── __init__.py │ │ ├── e_invoice_log │ │ │ ├── __init__.py │ │ │ ├── e_invoice_log.js │ │ │ ├── test_e_invoice_log.py │ │ │ ├── e_invoice_log.py │ │ │ └── e_invoice_log_list.js │ │ ├── e_waybill_log │ │ │ ├── __init__.py │ │ │ ├── e_waybill_log.js │ │ │ ├── test_e_waybill_log.py │ │ │ ├── e_waybill_log_list.js │ │ │ └── e_waybill_log.py │ │ ├── gst_credential │ │ │ ├── __init__.py │ │ │ └── gst_credential.py │ │ ├── gst_hsn_code │ │ │ ├── __init__.py │ │ │ ├── gst_hsn_code.js │ │ │ └── gst_hsn_code.json │ │ ├── gst_return_log │ │ │ ├── __init__.py │ │ │ └── test_gst_return_log.py │ │ ├── gst_settings │ │ │ └── __init__.py │ │ ├── gstr_3b_report │ │ │ ├── __init__.py │ │ │ └── gstr_3b_report_list.js │ │ ├── gstr_import_log │ │ │ ├── __init__.py │ │ │ ├── gstr_import_log.js │ │ │ └── test_gstr_import_log.py │ │ ├── bill_of_entry_item │ │ │ ├── __init__.py │ │ │ └── bill_of_entry_item.py │ │ ├── company_print_options │ │ │ ├── __init__.py │ │ │ ├── company_print_options.py │ │ │ └── company_print_options.json │ │ ├── gst_inward_supply │ │ │ ├── __init__.py │ │ │ ├── gst_inward_supply.js │ │ │ └── test_gst_inward_supply.py │ │ ├── gst_inward_supply_item │ │ │ ├── __init__.py │ │ │ ├── gst_inward_supply_item.py │ │ │ └── gst_inward_supply_item.json │ │ ├── e_invoice_applicable_company │ │ │ ├── __init__.py │ │ │ ├── e_invoice_applicable_company.py │ │ │ └── e_invoice_applicable_company.json │ │ ├── india_compliance_taxes_and_charges │ │ │ ├── __init__.py │ │ │ └── india_compliance_taxes_and_charges.py │ │ └── purchase_reconciliation_tool │ │ │ └── gstr_download_history.html │ ├── overrides │ │ ├── __init__.py │ │ ├── cross_app_hsn_code.py │ │ ├── email_template.py │ │ ├── test_setup_wizard.py │ │ ├── tax_category.py │ │ ├── gl_entry.py │ │ ├── test_party.py │ │ ├── journal_entry.py │ │ ├── test_sales_invoice.py │ │ ├── unreconcile_payment.py │ │ ├── item.py │ │ ├── delivery_note.py │ │ └── purchase_receipt.py │ ├── report │ │ ├── __init__.py │ │ ├── gst_balance │ │ │ ├── __init__.py │ │ │ └── gst_balance.json │ │ ├── gstin_status │ │ │ ├── __init__.py │ │ │ └── gstin_status.json │ │ ├── e_invoice_summary │ │ │ ├── __init__.py │ │ │ └── e_invoice_summary.json │ │ ├── gstr_3b_details │ │ │ ├── __init__.py │ │ │ └── gstr_3b_details.json │ │ ├── bill_of_entry_summary │ │ │ ├── __init__.py │ │ │ ├── bill_of_entry_summary.json │ │ │ └── bill_of_entry_summary.js │ │ ├── gst_advance_detail │ │ │ ├── __init__.py │ │ │ └── gst_advance_detail.json │ │ ├── gst_purchase_register │ │ │ ├── __init__.py │ │ │ └── gst_purchase_register.json │ │ ├── gst_sales_register │ │ │ ├── __init__.py │ │ │ └── gst_sales_register.json │ │ ├── summary_of_itc_availed │ │ │ ├── __init__.py │ │ │ └── summary_of_itc_availed.json │ │ ├── gst_account_wise_summary │ │ │ ├── __init__.py │ │ │ └── gst_account_wise_summary.json │ │ ├── gst_job_work_stock_movement │ │ │ ├── __init__.py │ │ │ └── gst_job_work_stock_movement.json │ │ ├── gst_tax_rate_wise_summary │ │ │ ├── __init__.py │ │ │ └── gst_tax_rate_wise_summary.json │ │ ├── india_compliance_api_usage │ │ │ ├── __init__.py │ │ │ ├── india_compliance_api_usage.json │ │ │ └── india_compliance_api_usage.js │ │ ├── hsn_wise_summary_of_inward_supplies │ │ │ ├── __init__.py │ │ │ ├── hsn_wise_summary_of_inward_supplies.py │ │ │ └── hsn_wise_summary_of_inward_supplies.json │ │ ├── hsn_wise_summary_of_outward_supplies │ │ │ ├── __init__.py │ │ │ └── hsn_wise_summary_of_outward_supplies.json │ │ └── utils.js │ ├── api_classes │ │ ├── __init__.py │ │ └── nic │ │ │ └── __init__.py │ ├── print_format │ │ ├── __init__.py │ │ ├── e_invoice │ │ │ ├── __init__.py │ │ │ ├── e_invoice.css │ │ │ └── e_invoice.json │ │ ├── e_waybill │ │ │ ├── __init__.py │ │ │ └── e_waybill.json │ │ ├── gst_pos_invoice │ │ │ └── __init__.py │ │ ├── gst_tax_invoice │ │ │ └── __init__.py │ │ ├── e_waybill_detailed │ │ │ ├── __init__.py │ │ │ └── e_waybill_detailed.json │ │ └── gst_purchase_invoice │ │ │ └── __init__.py │ ├── web_template │ │ ├── __init__.py │ │ ├── upi_qr_code │ │ │ ├── __init__.py │ │ │ ├── upi_qr_code.html │ │ │ └── upi_qr_code.json │ │ └── e_invoice_qr │ │ │ ├── __init__.py │ │ │ ├── e_invoice_qr.html │ │ │ └── e_invoice_qr.json │ ├── data │ │ ├── gstr1_excel_template_v2.0.xlsx │ │ ├── gstr1_excel_template_v2.1.xlsx │ │ ├── gstr3b_excel_utility_v5.7.xlsx │ │ └── address_template.html │ ├── constants │ │ └── e_invoice.py │ ├── client_scripts │ │ ├── document_naming_settings.js │ │ ├── document_naming_rule.js │ │ ├── customer.js │ │ ├── expense_claim.js │ │ ├── item.js │ │ ├── subcontracting_order.js │ │ ├── supplier.js │ │ ├── delivery_note.js │ │ ├── purchase_receipt.js │ │ └── journal_entry.js │ ├── number_card │ │ ├── pending_e_waybill │ │ │ └── pending_e_waybill.json │ │ ├── pending_e_invoices │ │ │ └── pending_e_invoices.json │ │ └── invoice_cancelled_but_not_e_invoice │ │ │ └── invoice_cancelled_but_not_e_invoice.json │ └── utils │ │ └── api.py ├── patches │ ├── __init__.py │ ├── v14 │ │ ├── __init__.py │ │ ├── set_default_for_overridden_accounts_setting.py │ │ ├── set_sandbox_mode_in_gst_settings.py │ │ ├── update_default_e_invoice_time_limit.py │ │ ├── remove_ecommerce_gstin_from_purchase_invoice.py │ │ ├── update_gstin_status_refresh_interval.py │ │ ├── delete_not_generated_gstr_import_log.py │ │ ├── rename_reverse_charge_to_purchase_reverse_charge.py │ │ ├── update_default_gstr1_settings.py │ │ ├── rename_gstr1_settings.py │ │ ├── delete_purchase_receipt_standard_custom_fields.py │ │ ├── update_purchase_reco_email_template.py │ │ ├── update_default_auto_reconciliation_settings.py │ │ ├── enable_sales_through_ecommerce_operator.py │ │ ├── set_autogenerate_e_waybill_with_e_invoice.py │ │ ├── update_tax_withholding_categories.py │ │ ├── stop_unnecessary_scheduled_jobs.py │ │ ├── set_reverse_charge_applicability_in_supplier.py │ │ ├── set_enable_retry_einv_ewb_generation.py │ │ ├── set_item_details_from_purchase_invoice_to_bill_of_entry.py │ │ ├── unset_inward_supply_link_for_cancelled_purchase.py │ │ ├── update_e_invoice_status.py │ │ ├── add_match_found_in_purchase_reconciliation_status.py │ │ ├── set_default_for_audit_trail_notification.py │ │ ├── update_total_taxes_for_gst_inward_supply.py │ │ ├── set_pending_boe_qty.py │ │ ├── set_reconciliation_status_for_manual_match.py │ │ └── set_correct_root_account_for_rcm.py │ ├── v15 │ │ ├── __init__.py │ │ ├── unset_auth_token.py │ │ ├── set_default_hsn_bifurcation_date.py │ │ ├── update_supplier_filing_status.py │ │ ├── make_e_invoice_log_extensible.py │ │ ├── remove_duplicate_web_template.py │ │ ├── update_action_for_gst_inward_supply.py │ │ ├── migrate_print_options_to_new_field.py │ │ ├── remove_itc_amount_custom_fields.py │ │ ├── multiple_pi_in_boe.py │ │ ├── set_default_for_new_gst_category_notification.py │ │ ├── remove_ignore_reconciliation_field.py │ │ ├── migrate_gstr1_log_to_returns_log.py │ │ ├── set_download_document_for_2a_2b.py │ │ ├── update_action_for_rejected_invoices_in_gst_inward_supply.py │ │ ├── update_return_logs_with_filing_preference.py │ │ └── migrate_boe_taxes_to_ic_taxes.py │ ├── post_install │ │ ├── __init__.py │ │ ├── update_custom_role_for_e_invoice_summary.py │ │ ├── update_default_gstr3b_status.py │ │ ├── update_state_name_to_puducherry.py │ │ ├── update_tax_category_for_rcm.py │ │ ├── remove_consumer_gst_category.py │ │ ├── update_state_code_for_daman_and_diu.py │ │ ├── rename_import_of_capital_goods.py │ │ ├── update_gst_category.py │ │ ├── update_payment_entry_fields.py │ │ ├── remove_deprecated_docs.py │ │ ├── update_vehicle_no_field_in_purchase_receipt.py │ │ ├── add_company_link_to_einvoice_settings.py │ │ ├── setup_custom_fields_for_gst.py │ │ ├── set_gst_tax_type_in_journal_entry.py │ │ ├── migrate_fields_for_gstr3b.py │ │ ├── update_reverse_charge_and_export_type.py │ │ ├── manual_patch_to_update_gst_details.py │ │ ├── remove_old_fields.py │ │ ├── update_reconciliation_status.py │ │ ├── merge_utgst_account_into_sgst_account.py │ │ ├── set_gst_tax_type.py │ │ ├── set_gst_category.py │ │ ├── update_gst_treatment_for_taxable_nil_transaction_item.py │ │ └── update_itc_classification_field.py │ └── v16 │ │ └── remove_legacy_report_fixtures.py ├── templates │ ├── __init__.py │ └── pages │ │ └── __init__.py ├── utils │ └── __init__.py ├── vat_india │ ├── __init__.py │ └── doctype │ │ ├── __init__.py │ │ ├── c_form │ │ ├── __init__.py │ │ ├── README.md │ │ ├── test_c_form.py │ │ └── c_form.js │ │ └── c_form_invoice_detail │ │ ├── __init__.py │ │ ├── README.md │ │ └── c_form_invoice_detail.py ├── audit_trail │ ├── __init__.py │ ├── report │ │ ├── __init__.py │ │ └── audit_trail │ │ │ ├── __init__.py │ │ │ └── audit_trail.json │ ├── constants │ │ ├── __init__.py │ │ └── custom_fields.py │ ├── client_scripts │ │ └── customize_form.js │ ├── overrides │ │ ├── version.py │ │ ├── test_account_settings.py │ │ ├── test_customize_form.py │ │ ├── accounts_settings.py │ │ ├── customize_form.py │ │ ├── property_setter.py │ │ ├── test_version.py │ │ └── test_property_setter.py │ ├── utils.py │ └── setup.py ├── income_tax_india │ ├── __init__.py │ ├── doctype │ │ └── __init__.py │ ├── uninstall.py │ ├── setup.py │ ├── overrides │ │ └── tax_withholding_category.py │ └── constants │ │ └── __init__.py ├── modules.txt ├── __init__.py ├── exceptions.py ├── test_patches.py └── uninstall.py ├── .prettierrc.yaml ├── setup.py ├── .github ├── dependabot.yml ├── helper │ └── site_config.json ├── workflows │ ├── docs-required.yml │ ├── linters.yml │ ├── initiate_release.yml │ ├── semantic-commits.yml │ └── on_release.yml └── ISSUE_TEMPLATE │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .prettierignore ├── MANIFEST.in ├── commitlint.config.js ├── codecov.yml ├── .releaserc ├── .git-blame-ignore-revs ├── pyproject.toml ├── .pre-commit-config.yaml └── .flake8 /india_compliance/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/www/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/patches/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/templates/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/vat_india/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/page/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/report/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/income_tax_india/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/templates/pages/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/constants/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/api_classes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstin/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_1/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/pan/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/income_tax_india/doctype/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/api_classes/nic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_account/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_uom_map/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_action/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_balance/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gstin_status/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/report/audit_trail/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/bill_of_entry/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_log/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_waybill_log/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_credential/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_hsn_code/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_return_log/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_3b_report/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_import_log/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_invoice/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_waybill/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/e_invoice_summary/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gstr_3b_details/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/upi_qr_code/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/bill_of_entry_item/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/company_print_options/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_inward_supply/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/gst_pos_invoice/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/gst_tax_invoice/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/bill_of_entry_summary/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_advance_detail/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_purchase_register/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_sales_register/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/summary_of_itc_availed/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/e_invoice_qr/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form_invoice_detail/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_inward_supply_item/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_waybill_detailed/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/gst_purchase_invoice/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_account_wise_summary/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_job_work_stock_movement/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_tax_rate_wise_summary/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/india_compliance_api_usage/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | tabWidth: 4 2 | printWidth: 88 3 | arrowParens: "avoid" 4 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_applicable_company/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/india_compliance_taxes_and_charges/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/hsn_wise_summary_of_inward_supplies/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/hsn_wise_summary_of_outward_supplies/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/modules.txt: -------------------------------------------------------------------------------- 1 | GST India 2 | Income Tax India 3 | VAT India 4 | Audit Trail -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | name = "india_compliance" 4 | 5 | setup() 6 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form/README.md: -------------------------------------------------------------------------------- 1 | C Form (India specific only) - Will be deprecated. 2 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form_invoice_detail/README.md: -------------------------------------------------------------------------------- 1 | Invoice detail for parent C-Form. 2 | -------------------------------------------------------------------------------- /india_compliance/public/images/mail-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resilient-tech/india-compliance/HEAD/india_compliance/public/images/mail-box.png -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/upi_qr_code/upi_qr_code.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/e_invoice_qr/e_invoice_qr.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/public/js/gstr1.bundle.js: -------------------------------------------------------------------------------- 1 | import "./components/filter_group"; 2 | import "./components/data_table_manager"; 3 | import "./components/view_group"; 4 | -------------------------------------------------------------------------------- /india_compliance/public/images/india-compliance-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resilient-tech/india-compliance/HEAD/india_compliance/public/images/india-compliance-logo.png -------------------------------------------------------------------------------- /india_compliance/patches/v15/unset_auth_token.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_value("GST Credential", {"service": "Returns"}, "auth_token", None) 6 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/utils.js: -------------------------------------------------------------------------------- 1 | export const getReadableNumber = function (num, precision = 2) { 2 | return format_number(num, null, precision); 3 | }; 4 | -------------------------------------------------------------------------------- /india_compliance/gst_india/data/gstr1_excel_template_v2.0.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resilient-tech/india-compliance/HEAD/india_compliance/gst_india/data/gstr1_excel_template_v2.0.xlsx -------------------------------------------------------------------------------- /india_compliance/gst_india/data/gstr1_excel_template_v2.1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resilient-tech/india-compliance/HEAD/india_compliance/gst_india/data/gstr1_excel_template_v2.1.xlsx -------------------------------------------------------------------------------- /india_compliance/gst_india/data/gstr3b_excel_utility_v5.7.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resilient-tech/india-compliance/HEAD/india_compliance/gst_india/data/gstr3b_excel_utility_v5.7.xlsx -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_default_for_overridden_accounts_setting.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_default("add_taxes_from_item_tax_template", 0) 6 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/constants.js: -------------------------------------------------------------------------------- 1 | export const UiState = Object.freeze({ 2 | initial: 0, 3 | loading: 1, 4 | success: 2, 5 | error: 3, 6 | }); 7 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/set_default_hsn_bifurcation_date.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_single_value("GST Settings", "hsn_bifurcation_from", "2025-04-01") 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.egg-info 4 | *.swp 5 | tags 6 | .vscode/ 7 | __pycache__/ 8 | 9 | # docs 10 | india_compliance/docs/current 11 | 12 | # JS 13 | dist/ 14 | node_modules 15 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/cross_app_hsn_code.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def before_update(doc, method=None): 5 | if doc.gst_hsn_code: 6 | frappe.flags.cross_app_hsn_code = doc.gst_hsn_code 7 | -------------------------------------------------------------------------------- /india_compliance/gst_india/constants/e_invoice.py: -------------------------------------------------------------------------------- 1 | CANCEL_REASON_CODES = { 2 | "Duplicate": "1", 3 | "Order Cancelled": "3", 4 | "Data Entry Mistake": "2", 5 | "Others": "4", 6 | } 7 | 8 | ITEM_LIMIT = 1000 9 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_sandbox_mode_in_gst_settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if frappe.conf.ic_api_sandbox_mode: 6 | frappe.db.set_single_value("GST Settings", "sandbox_mode", 1) 7 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_default_e_invoice_time_limit.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_single_value( 6 | "GST Settings", {"e_invoice_reporting_time_limit_days": 30} 7 | ) 8 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/remove_ecommerce_gstin_from_purchase_invoice.py: -------------------------------------------------------------------------------- 1 | from india_compliance.utils.custom_fields import delete_old_fields 2 | 3 | 4 | def execute(): 5 | delete_old_fields("ecommerce_gstin", "Purchase Invoice") 6 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_custom_role_for_e_invoice_summary.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_value( 6 | "Custom Role", {"report": "E-Invoice Summary"}, "report", "e-Invoice Summary" 7 | ) 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore files with {% include ... %} 2 | india_compliance/income_tax_india/report/income_tax_deductions/income_tax_deductions.js 3 | 4 | # JSON files - most are autogenerated 5 | *.json 6 | 7 | # Other 8 | node_modules 9 | dist/ 10 | -------------------------------------------------------------------------------- /india_compliance/public/js/ims.bundle.js: -------------------------------------------------------------------------------- 1 | import "./components/data_table_manager"; 2 | import "./components/number_card"; 3 | import "./components/set_gstin_options"; 4 | import "./components/filter_group"; 5 | import "./reconciliation_components/actions"; 6 | -------------------------------------------------------------------------------- /india_compliance/public/js/purchase_reconciliation_tool.bundle.js: -------------------------------------------------------------------------------- 1 | import "./gstr_2b"; 2 | import "./components/data_table_manager"; 3 | import "./components/filter_group"; 4 | import "./components/number_card"; 5 | import "./reconciliation_components/actions"; 6 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/pan/test_pan.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TestPAN(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on('e-Invoice Log', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | // }); 9 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on('e-Waybill Log', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | // }); 9 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_1/test_gstr_1.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TestGSTR1(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_import_log/gstr_import_log.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('GSTR Import Log', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | }); 9 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_inward_supply/gst_inward_supply.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('GST Inward Supply', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | }); 9 | -------------------------------------------------------------------------------- /india_compliance/income_tax_india/uninstall.py: -------------------------------------------------------------------------------- 1 | from india_compliance.income_tax_india.constants.custom_fields import CUSTOM_FIELDS 2 | from india_compliance.utils.custom_fields import delete_custom_fields 3 | 4 | 5 | def before_uninstall(): 6 | delete_custom_fields(CUSTOM_FIELDS) 7 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_default_gstr3b_status.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | doc = frappe.qb.DocType("GSTR 3B Report") 6 | frappe.qb.update(doc).set(doc.generation_status, "Generated").where( 7 | doc.generation_status.isnull() 8 | ).run() 9 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_gstin_status_refresh_interval.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if frappe.db.get_single_value("GST Settings", "gstin_status_refresh_interval") < 15: 6 | frappe.db.set_single_value("GST Settings", "gstin_status_refresh_interval", 15) 7 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_log/test_e_invoice_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TesteInvoiceLog(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_waybill_log/test_e_waybill_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TesteWaybillLog(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_return_log/test_gst_return_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TestGSTReturnLog(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_account/gst_account.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class GSTAccount(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_uom_map/gst_uom_map.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class GSTUOMMap(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_import_log/test_gstr_import_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TestGSTRImportLog(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/delete_not_generated_gstr_import_log.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.delete( 6 | "GSTR Import Log", 7 | filters={ 8 | "data_not_found": 1, 9 | "return_type": "GSTR2b", 10 | }, 11 | ) 12 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class eInvoiceLog(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_inward_supply/test_gst_inward_supply.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase 6 | 7 | 8 | class TestGSTInwardSupply(IntegrationTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/document_naming_settings.js: -------------------------------------------------------------------------------- 1 | frappe.require("assets/india_compliance/js/transaction.js", function () { 2 | frappe.ui.form.on("Document Naming Settings", { 3 | transaction_type(frm) { 4 | show_gst_invoice_no_banner(frm); 5 | }, 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_credential/gst_credential.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class GSTCredential(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/rename_reverse_charge_to_purchase_reverse_charge.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_value( 6 | "GST Account", 7 | {"account_type": "Reverse Charge"}, 8 | "account_type", 9 | "Purchase Reverse Charge", 10 | ) 11 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/document_naming_rule.js: -------------------------------------------------------------------------------- 1 | frappe.require("assets/india_compliance/js/transaction.js", function () { 2 | frappe.ui.form.on("Document Naming Rule", { 3 | async document_type(frm) { 4 | await show_gst_invoice_no_banner(frm); 5 | }, 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/bill_of_entry_item/bill_of_entry_item.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class BillofEntryItem(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/company_print_options/company_print_options.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class CompanyPrintOptions(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_inward_supply_item/gst_inward_supply_item.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class GSTInwardSupplyItem(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/update_supplier_filing_status.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | GSTR2 = frappe.qb.DocType("GST Inward Supply") 6 | ( 7 | frappe.qb.update(GSTR2) 8 | .set("is_supplier_return_filed", 1) 9 | .where(GSTR2.gstr_1_filled == 1) 10 | .run() 11 | ) 12 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_state_name_to_puducherry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_value( 6 | "Address", 7 | {"gst_state": "Pondicherry"}, 8 | { 9 | "gst_state": "Puducherry", 10 | "state": "Puducherry", 11 | }, 12 | ) 13 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form_invoice_detail/c_form_invoice_detail.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors 2 | # License: GNU General Public License v3. See license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class CFormInvoiceDetail(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/__init__.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.utils.user import is_website_user 3 | 4 | __version__ = "16.0.0-dev" 5 | 6 | 7 | def check_app_permission(): 8 | if frappe.session.user == "Administrator": 9 | return True 10 | 11 | if is_website_user(): 12 | return False 13 | 14 | return True 15 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/customer.js: -------------------------------------------------------------------------------- 1 | const DOCTYPE = "Customer"; 2 | 3 | validate_pan(DOCTYPE); 4 | validate_gstin(DOCTYPE); 5 | update_gstin_in_other_documents(DOCTYPE); 6 | show_overseas_disabled_warning(DOCTYPE); 7 | set_gstin_options_and_status(DOCTYPE); 8 | set_gst_category(DOCTYPE); 9 | set_pan_status(DOCTYPE); 10 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_default_gstr1_settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_single_value( 6 | "GST Settings", 7 | { 8 | "enable_gstr_1_api": 1, 9 | "freeze_transactions": 1, 10 | "filing_frequency": "Monthly", 11 | }, 12 | ) 13 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore } from 'vuex' 2 | import authStore from "./modules/auth"; 3 | import accountStore from "./modules/account"; 4 | 5 | export default createStore({ 6 | modules: { 7 | auth: authStore, 8 | account: accountStore, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_applicable_company/e_invoice_applicable_company.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class eInvoiceApplicableCompany(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | # source_link = "https://github.com/[org_name]/india_compliance" 6 | # headline = "App that does everything" 7 | # sub_heading = "Yes, you got that right the first time, everything" 8 | 9 | 10 | def get_context(context): 11 | context.brand_html = "India Compliance" 12 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/make_e_invoice_log_extensible.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | e_invoice_log = frappe.qb.DocType("e-Invoice Log") 6 | 7 | frappe.qb.update(e_invoice_log).set( 8 | e_invoice_log.reference_name, e_invoice_log.sales_invoice 9 | ).set(e_invoice_log.reference_doctype, "Sales Invoice").run() 10 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/remove_duplicate_web_template.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | template_name = "e-Invoice QR Code" 6 | if frappe.db.exists( 7 | "Web Template Field", 8 | {"parent": template_name, "fieldname": "e_invoice_qr_text"}, 9 | ): 10 | frappe.db.delete("Web Template", template_name) 11 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/india_compliance_taxes_and_charges/india_compliance_taxes_and_charges.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class IndiaComplianceTaxesandCharges(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/update_action_for_gst_inward_supply.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | # Discard the action "Accept Supplier Values" 6 | frappe.db.set_value( 7 | "GST Inward Supply", 8 | {"action": ["in", ["Accept My Values", "Accept Supplier Values"]]}, 9 | "action", 10 | "Accept", 11 | ) 12 | -------------------------------------------------------------------------------- /india_compliance/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return [ 6 | { 7 | "module_name": "India Compliance", 8 | "color": "grey", 9 | "icon": "octicon octicon-file-directory", 10 | "type": "module", 11 | "label": _("India Compliance"), 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_tax_category_for_rcm.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | tax_category = frappe.qb.DocType("Tax Category") 6 | 7 | frappe.qb.update(tax_category).set(tax_category.is_reverse_charge, 1).where( 8 | tax_category.name.isin(("Reverse Charge Out-State", "Reverse Charge In-State")) 9 | ).run() 10 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form/test_c_form.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | # test_records = frappe.get_test_records('C-Form') 7 | 8 | IGNORE_TEST_RECORD_DEPENDENCIES = ["Company", "Customer", "Sales Invoice", "Territory"] 9 | 10 | 11 | class TestCForm(unittest.TestCase): 12 | pass 13 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/pan/pan.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("PAN", { 5 | refresh(frm) { 6 | frm.disable_form(); 7 | 8 | frm.add_custom_button(__("Refresh PAN Status"), () => { 9 | frm.call("update_pan_status"); 10 | }); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/remove_consumer_gst_category.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | for doctype in ("Customer", "Supplier", "Address"): 6 | if not frappe.db.has_column(doctype, "gst_category"): 7 | continue 8 | 9 | frappe.db.set_value( 10 | doctype, {"gst_category": "Consumer"}, "gst_category", "Unregistered" 11 | ) 12 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/rename_gstr1_settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.utils import sbool 3 | 4 | 5 | def execute(): 6 | settings = frappe.get_cached_doc("GST Settings") 7 | if not sbool(settings.get("compare_gstr_1_data")): 8 | return 9 | 10 | frappe.db.set_single_value( 11 | "GST Settings", {"enable_gstr_1_api": 1, "compare_unfiled_data": 1} 12 | ) 13 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/delete_purchase_receipt_standard_custom_fields.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | FIELDS = [ 4 | "transporter_name", 5 | "lr_no", 6 | "lr_date", 7 | ] 8 | 9 | 10 | def execute(): 11 | frappe.db.delete( 12 | "Custom Field", 13 | filters={ 14 | "dt": "Purchase Receipt", 15 | "fieldname": ("in", FIELDS), 16 | }, 17 | ) 18 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/email_template.py: -------------------------------------------------------------------------------- 1 | from india_compliance.gst_india.setup import update_default_email_template 2 | 3 | 4 | def after_rename(doc, method=None, *args, **kwargs): 5 | old_name = args[0] 6 | new_name = args[1] 7 | 8 | update_default_email_template(old_name, new_name) 9 | 10 | 11 | def on_trash(doc, method=None): 12 | update_default_email_template(doc.name, None) 13 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_state_code_for_daman_and_diu.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | address = frappe.qb.DocType("Address") 6 | 7 | ( 8 | frappe.qb.update(address) 9 | .set(address.gst_state, "Dadra and Nagar Haveli and Daman and Diu") 10 | .set(address.gst_state_number, "26") 11 | .where(address.gst_state == "Daman and Diu") 12 | ).run() 13 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_purchase_reco_email_template.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.gst_india.setup import EMAIL_TEMPLATE_DATA 4 | 5 | 6 | def execute(): 7 | doctype = EMAIL_TEMPLATE_DATA.pop("doctype") 8 | name = EMAIL_TEMPLATE_DATA.pop("name") 9 | 10 | if not frappe.db.exists(doctype, name): 11 | return 12 | 13 | frappe.db.set_value(doctype, name, EMAIL_TEMPLATE_DATA) 14 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/rename_import_of_capital_goods.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if "eligibility_for_itc" not in frappe.db.get_table_columns("Purchase Invoice"): 6 | return 7 | 8 | frappe.db.set_value( 9 | "Purchase Invoice", 10 | {"eligibility_for_itc": "Import Of Capital Goods"}, 11 | "eligibility_for_itc", 12 | "Import Of Goods", 13 | ) 14 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_gst_category.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | for doctype, field in ( 6 | { 7 | "Sales Invoice": "billing_address_gstin", 8 | "Purchase Invoice": "supplier_gstin", 9 | } 10 | ).items(): 11 | frappe.db.set_value( 12 | doctype, {field: ("in", (None, ""))}, "gst_category", "Unregistered" 13 | ) 14 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_default_auto_reconciliation_settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.set_single_value( 6 | "GST Settings", 7 | { 8 | "inward_supply_period": 2, 9 | "reconcile_on_tuesday": 1, 10 | "reconcile_on_friday": 1, 11 | "reconcile_for_b2b": 1, 12 | "reconcile_for_cdnr": 1, 13 | }, 14 | ) 15 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/expense_claim.js: -------------------------------------------------------------------------------- 1 | frappe.ui.form.on("Expense Claim", { 2 | company: set_gstin_options, 3 | }); 4 | 5 | frappe.ui.form.on("Expense Taxes and Charges", { 6 | account_head: toggle_gstin_for_expense_claim, 7 | accounts_head_remove: toggle_gstin_for_expense_claim, 8 | }); 9 | 10 | function toggle_gstin_for_expense_claim(frm) { 11 | toggle_company_gstin(frm, "taxes", "account_head"); 12 | } -------------------------------------------------------------------------------- /india_compliance/patches/v14/enable_sales_through_ecommerce_operator.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.patches.post_install.set_default_gst_settings import ( 4 | enable_sales_through_ecommerce_operators, 5 | ) 6 | 7 | 8 | def execute(): 9 | new_settings = {} 10 | 11 | enable_sales_through_ecommerce_operators(new_settings) 12 | 13 | if new_settings: 14 | frappe.db.set_single_value("GST Settings", new_settings) 15 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/migrate_print_options_to_new_field.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.has_column("Company Print Options", "autofield"): 6 | return 7 | 8 | doc = frappe.qb.DocType("Company Print Options") 9 | 10 | ( 11 | frappe.qb.update(doc) 12 | .set(doc.print_label, doc.autofield) 13 | .set(doc.print_value, doc.autofield_value) 14 | .run() 15 | ) 16 | -------------------------------------------------------------------------------- /india_compliance/income_tax_india/setup.py: -------------------------------------------------------------------------------- 1 | from india_compliance.income_tax_india.constants.custom_fields import CUSTOM_FIELDS 2 | from india_compliance.utils.custom_fields import get_custom_fields_creator 3 | 4 | _create_custom_fields = get_custom_fields_creator("Income Tax India") 5 | 6 | 7 | def after_install(): 8 | create_custom_fields() 9 | 10 | 11 | def create_custom_fields(): 12 | _create_custom_fields(CUSTOM_FIELDS, ignore_validate=True) 13 | -------------------------------------------------------------------------------- /india_compliance/gst_india/page/india_compliance_account/india_compliance_account.js: -------------------------------------------------------------------------------- 1 | const PAGE_NAME = "india-compliance-account"; 2 | let icAccountPage; 3 | 4 | frappe.pages[PAGE_NAME].on_page_load = async function (wrapper) { 5 | await frappe.require([ 6 | "india_compliance_account.bundle.js", 7 | "india_compliance_account.bundle.css", 8 | ]); 9 | 10 | icAccountPage = new india_compliance.pages.IndiaComplianceAccountPage(wrapper, PAGE_NAME); 11 | }; 12 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_autogenerate_e_waybill_with_e_invoice.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | settings = frappe.get_single("GST Settings") 6 | 7 | settings.db_set( 8 | "generate_e_waybill_with_e_invoice", 9 | ( 10 | 0 11 | if ( 12 | settings.auto_generate_e_invoice 13 | and not settings.auto_generate_e_waybill 14 | ) 15 | else 1 16 | ), 17 | ) 18 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_tax_withholding_categories.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.income_tax_india.overrides.company import ( 4 | create_or_update_tax_withholding_category, 5 | ) 6 | 7 | 8 | def execute(): 9 | company_list = frappe.get_all( 10 | "Company", filters={"country": "India"}, pluck="name", order_by="lft asc" 11 | ) 12 | 13 | for company in company_list: 14 | create_or_update_tax_withholding_category(company) 15 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/stop_unnecessary_scheduled_jobs.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | settings = frappe.get_single("GST Settings") 6 | 7 | frappe.db.set_value( 8 | "Scheduled Job Type", 9 | { 10 | "method": "india_compliance.gst_india.doctype.purchase_reconciliation_tool.purchase_reconciliation_tool.auto_refresh_authtoken" 11 | }, 12 | "stopped", 13 | not settings.enable_auto_reconciliation, 14 | ) 15 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/remove_itc_amount_custom_fields.py: -------------------------------------------------------------------------------- 1 | from india_compliance.utils.custom_fields import delete_old_fields 2 | 3 | 4 | def execute(): 5 | # these fields are not required 6 | fields_to_delete = [ 7 | "gst_col_break", 8 | "itc_integrated_tax", 9 | "itc_central_tax", 10 | "itc_state_tax", 11 | "itc_cess_amount", 12 | ] 13 | 14 | for field in fields_to_delete: 15 | delete_old_fields(field, "Purchase Invoice") 16 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/multiple_pi_in_boe.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | boe = frappe.qb.DocType("Bill of Entry", alias="boe") 6 | boe_item = frappe.qb.DocType("Bill of Entry Item", alias="boe_item") 7 | 8 | # link BOE Item to it's purchase invoice 9 | ( 10 | frappe.qb.update(boe_item) 11 | .join(boe) 12 | .on(boe_item.parent == boe.name) 13 | .set(boe_item.purchase_invoice, boe.purchase_invoice) 14 | .run(as_dict=True) 15 | ) 16 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance.bundle.js: -------------------------------------------------------------------------------- 1 | import "./utils"; 2 | import "./gst_api_handler"; 3 | import "./quick_entry"; 4 | import "./transaction"; 5 | import "./audit_trail_notification"; 6 | import "./item_tax_template_notification"; 7 | import "./new_gst_category_notification"; 8 | import "./quick_info_popover"; 9 | import "./custom_number_card"; 10 | import "./taxes_controller"; 11 | import "./help_links"; 12 | import "./reconciliation_components/tabs"; 13 | import "./components/set_gstin_options"; 14 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/PageTitle.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 26 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_payment_entry_fields.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.utils.custom_fields import delete_old_fields 4 | 5 | 6 | def execute(): 7 | if not frappe.db.has_column("Payment Entry", "customer_gstin"): 8 | return 9 | 10 | payment_entry = frappe.qb.DocType("Payment Entry") 11 | frappe.qb.update(payment_entry).set( 12 | payment_entry.billing_address_gstin, payment_entry.customer_gstin 13 | ).run() 14 | 15 | delete_old_fields("customer_gstin", "Payment Entry") 16 | -------------------------------------------------------------------------------- /india_compliance/gst_india/data/address_template.html: -------------------------------------------------------------------------------- 1 | {{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}
{% endif -%}{{ city }}
2 | {% if gst_state %}{{ gst_state }}{% endif -%} 3 | {% if gst_state_number %}, State Code: {{ gst_state_number }}
{% endif -%} 4 | {% if pincode %}PIN Code: {{ pincode }}
{% endif -%} 5 | {{ country }}
6 | {% if phone %}Phone: {{ phone }}
{% endif -%} 7 | {% if fax %}Fax: {{ fax }}
{% endif -%} 8 | {% if email_id %}Email: {{ email_id }}
{% endif -%} 9 | {% if gstin %}GSTIN: {{ gstin }}
{% endif -%} -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_reverse_charge_applicability_in_supplier.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | reverse_charge_tax_category = frappe.db.get_all( 6 | "Tax Category", 7 | filters={"is_reverse_charge": "1"}, 8 | pluck="name", 9 | ) 10 | 11 | if not reverse_charge_tax_category: 12 | return 13 | 14 | frappe.db.set_value( 15 | "Supplier", 16 | {"tax_category": ("in", reverse_charge_tax_category)}, 17 | "is_reverse_charge_applicable", 18 | 1, 19 | ) 20 | -------------------------------------------------------------------------------- /india_compliance/public/js/custom_number_card.js: -------------------------------------------------------------------------------- 1 | let FrappeNumberCard = frappe.widget.widget_factory.number_card; 2 | 3 | class CustomNumberCard extends FrappeNumberCard { 4 | render_number() { 5 | if ( 6 | [ 7 | "Pending e-Waybill", 8 | "Pending e-Invoices", 9 | "Invoice Cancelled But Not e-Invoice", 10 | ].includes(this.card_doc.name) && 11 | !this.formatted_number 12 | ) 13 | this.card_doc.color = null; 14 | 15 | super.render_number(); 16 | } 17 | } 18 | 19 | frappe.widget.widget_factory.number_card = CustomNumberCard; 20 | -------------------------------------------------------------------------------- /.github/helper/site_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_host": "127.0.0.1", 3 | "db_port": 3306, 4 | "db_name": "test_frappe", 5 | "db_password": "test_frappe", 6 | "db_type": "mariadb", 7 | "auto_email_id": "test@example.com", 8 | "mail_server": "smtp.example.com", 9 | "mail_login": "test@example.com", 10 | "mail_password": "test", 11 | "admin_password": "admin", 12 | "root_login": "root", 13 | "root_password": "travis", 14 | "host_name": "http://test_site:8000", 15 | "install_apps": ["erpnext"], 16 | "ic_api_secret": "*****", 17 | "throttle_user_limit": 100 18 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstin/gstin.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("GSTIN", { 5 | refresh(frm) { 6 | frm.disable_form(); 7 | 8 | frm.add_custom_button(__("Refresh GSTIN Status"), () => { 9 | frm.call("update_gstin_status"); 10 | }); 11 | 12 | frm.add_custom_button(__("Refresh Transporter ID Status"), () => { 13 | frm.call("update_transporter_id_status"); 14 | }); 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/upi_qr_code/upi_qr_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "creation": "2024-05-03 10:34:51.575300", 3 | "docstatus": 0, 4 | "doctype": "Web Template", 5 | "fields": [ 6 | { 7 | "fieldname": "upi_qr_text", 8 | "fieldtype": "Text", 9 | "label": "UPI QR Code", 10 | "reqd": 0 11 | } 12 | ], 13 | "idx": 0, 14 | "modified": "2024-06-03 13:19:22.517387", 15 | "modified_by": "Administrator", 16 | "module": "GST India", 17 | "name": "UPI QR Code", 18 | "owner": "Administrator", 19 | "standard": 1, 20 | "template": "", 21 | "type": "Component" 22 | } -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/pages/PageNotFound.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /india_compliance/gst_india/web_template/e_invoice_qr/e_invoice_qr.json: -------------------------------------------------------------------------------- 1 | { 2 | "creation": "2024-06-27 12:22:43.643151", 3 | "docstatus": 0, 4 | "doctype": "Web Template", 5 | "fields": [ 6 | { 7 | "fieldname": "e_invoice_qr_text", 8 | "fieldtype": "Text", 9 | "label": "QR Text", 10 | "reqd": 0 11 | } 12 | ], 13 | "idx": 0, 14 | "modified": "2024-07-01 11:39:21.368461", 15 | "modified_by": "Administrator", 16 | "module": "GST India", 17 | "name": "e-Invoice QR", 18 | "owner": "Administrator", 19 | "standard": 1, 20 | "template": "", 21 | "type": "Component" 22 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report_list.js: -------------------------------------------------------------------------------- 1 | frappe.listview_settings["GSTR 3B Report"] = { 2 | hide_name_column: true, 3 | add_fields: ["generation_status"], 4 | get_indicator: function (doc) { 5 | var colors = { 6 | "In Process": "orange", 7 | Generated: "green", 8 | Failed: "red", 9 | }; 10 | return [ 11 | __(doc.generation_status), 12 | colors[doc.generation_status], 13 | "generation_status,=," + doc.generation_status, 14 | ]; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_enable_retry_einv_ewb_generation.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | frappe.db.sql( 6 | """ 7 | UPDATE `tabSingles` AS t1 8 | SET `value` = ( 9 | SELECT IFNULL( 10 | (SELECT `value` FROM `tabSingles` AS t2 11 | WHERE t2.`doctype` = 'GST Settings' 12 | AND t2.`field` = 'enable_retry_e_invoice_generation'), 0) 13 | ) 14 | WHERE t1.`doctype` = 'GST Settings' 15 | AND t1.`field` = 'enable_retry_einv_ewb_generation' 16 | """ 17 | ) 18 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/utils.js: -------------------------------------------------------------------------------- 1 | function fetch_gstins(report) { 2 | const company = report.get_filter_value('company'); 3 | const gstin_field = report.get_filter('company_gstin'); 4 | 5 | if (!company) { 6 | gstin_field.df.options = [""]; 7 | gstin_field.refresh(); 8 | return; 9 | } 10 | 11 | frappe.call({ 12 | method:'india_compliance.gst_india.utils.get_gstin_list', 13 | async: false, 14 | args: { 15 | party: company 16 | }, 17 | callback(r) { 18 | r.message.unshift(""); 19 | gstin_field.df.options = r.message; 20 | gstin_field.refresh(); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /india_compliance/income_tax_india/overrides/tax_withholding_category.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def on_change(doc, method=None): 5 | frappe.cache.delete_value("tax_withholding_accounts") 6 | 7 | 8 | def get_tax_withholding_accounts(company): 9 | def _get_tax_withholding_accounts(): 10 | return set( 11 | frappe.get_all( 12 | "Tax Withholding Account", pluck="account", filters={"company": company} 13 | ) 14 | ) 15 | 16 | return frappe.cache.hget( 17 | "tax_withholding_accounts", company, generator=_get_tax_withholding_accounts 18 | ) 19 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/PreLoader.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_item_details_from_purchase_invoice_to_bill_of_entry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | boe_item = frappe.qb.DocType("Bill of Entry Item", alias="boe_item") 6 | pi_item = frappe.qb.DocType("Purchase Invoice Item") 7 | 8 | ( 9 | frappe.qb.update(boe_item) 10 | .left_join(pi_item) 11 | .on(pi_item.name == boe_item.pi_detail) 12 | .set(boe_item.gst_hsn_code, pi_item.gst_hsn_code) 13 | .set(boe_item.qty, pi_item.qty) 14 | .set(boe_item.uom, pi_item.uom) 15 | .where(boe_item.docstatus == 1) 16 | .run() 17 | ) 18 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/test_setup_wizard.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.tests import IntegrationTestCase 3 | 4 | 5 | class TestSetupWizard(IntegrationTestCase): 6 | def test_setup_wizard_with_valid_gstin(self): 7 | setup_company = "Wind Power LLP" 8 | company = frappe.get_doc("Company", setup_company) 9 | 10 | self.assertDocumentEqual( 11 | {"gst_category": "Unregistered", "gstin": None, "default_gst_rate": "18.0"}, 12 | company, 13 | ) 14 | 15 | self.assertEqual( 16 | frappe.db.get_single_value("Accounts Settings", "enable_audit_trail"), 0 17 | ) 18 | -------------------------------------------------------------------------------- /india_compliance/income_tax_india/constants/__init__.py: -------------------------------------------------------------------------------- 1 | TDS_SECTIONS = [ 2 | "193", 3 | "194", 4 | "194BB", 5 | "194EE", 6 | "194A", 7 | "194B", 8 | "194C", 9 | "194D", 10 | "194F", 11 | "194G", 12 | "194H", 13 | "194I", 14 | "194JA", 15 | "194JB", 16 | "194LA", 17 | "194I(a)", 18 | "194I(b)", 19 | "194LBA", 20 | "194DA", 21 | "192A", 22 | "192B", 23 | "194LBB", 24 | "194IA", 25 | "194N", 26 | "194Q", 27 | "194T", 28 | "195", 29 | "206C(1H)", 30 | ] 31 | 32 | TDS_ENTITY_TYPE = ["Individual", "Company", "Company Assessee", "No PAN / Invalid PAN"] 33 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include india_compliance *.css 8 | recursive-include india_compliance *.csv 9 | recursive-include india_compliance *.html 10 | recursive-include india_compliance *.ico 11 | recursive-include india_compliance *.js 12 | recursive-include india_compliance *.json 13 | recursive-include india_compliance *.md 14 | recursive-include india_compliance *.png 15 | recursive-include india_compliance *.py 16 | recursive-include india_compliance *.svg 17 | recursive-include india_compliance *.txt 18 | recursive-exclude india_compliance *.pyc -------------------------------------------------------------------------------- /india_compliance/gst_india/page/india_compliance_account/india_compliance_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": null, 3 | "creation": "2022-02-25 13:28:27.343408", 4 | "docstatus": 0, 5 | "doctype": "Page", 6 | "idx": 0, 7 | "modified": "2022-08-03 20:09:15.429993", 8 | "modified_by": "Administrator", 9 | "module": "GST India", 10 | "name": "india-compliance-account", 11 | "owner": "Administrator", 12 | "page_name": "india-compliance-account", 13 | "restrict_to_domain": "", 14 | "roles": [ 15 | { 16 | "role": "System Manager" 17 | } 18 | ], 19 | "script": null, 20 | "standard": "Yes", 21 | "style": null, 22 | "system_page": 0, 23 | "title": "India Compliance Account" 24 | } -------------------------------------------------------------------------------- /india_compliance/patches/v14/unset_inward_supply_link_for_cancelled_purchase.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | DOCTYPES = ("Purchase Invoice", "Bill of Entry") 4 | 5 | 6 | def execute(): 7 | for doctype in DOCTYPES: 8 | cancelled_doc = get_cancelled_doc(doctype) 9 | frappe.db.set_value( 10 | "GST Inward Supply", 11 | {"link_name": ("in", cancelled_doc), "link_doctype": doctype}, 12 | {"link_name": "", "link_doctype": "", "match_status": ""}, 13 | ) 14 | 15 | 16 | def get_cancelled_doc(doctype): 17 | return frappe.get_all( 18 | doctype, 19 | filters={"docstatus": 2}, 20 | pluck="name", 21 | ) 22 | -------------------------------------------------------------------------------- /.github/workflows/docs-required.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Required 2 | 3 | on: 4 | pull_request: 5 | types: [ opened, synchronize, reopened, edited ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | timeout-minutes: 10 11 | 12 | steps: 13 | - name: 'Setup Environment' 14 | uses: actions/setup-python@v6 15 | with: 16 | python-version: '3.11' 17 | - uses: actions/checkout@v6 18 | 19 | - name: Validate Docs 20 | env: 21 | PR_NUMBER: ${{ github.event.number }} 22 | run: | 23 | pip install requests --quiet 24 | python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER 25 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/set_default_for_new_gst_category_notification.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | import frappe.defaults 3 | from frappe.utils.user import get_users_with_role 4 | 5 | 6 | def execute(): 7 | if not frappe.db.exists("Company", {"country": "India"}): 8 | return 9 | 10 | # Users affected by creation of new GST category 11 | if not frappe.db.exists( 12 | "Purchase Invoice", {"itc_classification": "Input Service Distributor"} 13 | ): 14 | return 15 | 16 | for user in get_users_with_role("Accounts Manager"): 17 | frappe.defaults.set_user_default( 18 | "needs_new_gst_category_notification", 1, user=user 19 | ) 20 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/remove_ignore_reconciliation_field.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.utils.custom_fields import delete_old_fields 4 | 5 | DOCTYPES = ("Purchase Invoice", "Bill of Entry") 6 | 7 | 8 | def execute(): 9 | fieldname = "ignore_reconciliation" 10 | 11 | for doctype in DOCTYPES: 12 | if fieldname not in frappe.db.get_table_columns(doctype): 13 | continue 14 | 15 | frappe.db.set_value(doctype, {fieldname: 1}, "reconciliation_status", "Ignored") 16 | frappe.db.sql_ddl( 17 | "alter table `tab{0}` drop column {1}".format(doctype, fieldname) 18 | ) 19 | 20 | delete_old_fields(fieldname, DOCTYPES) 21 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_e_invoice_status.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import IfNull 3 | 4 | from india_compliance.gst_india.doctype.gst_settings.gst_settings import ( 5 | update_e_invoice_status, 6 | ) 7 | 8 | 9 | def execute(): 10 | update_e_invoice_status() 11 | set_pending_cancellation_status() 12 | 13 | 14 | def set_pending_cancellation_status(): 15 | sales_invoice = frappe.qb.DocType("Sales Invoice") 16 | 17 | ( 18 | frappe.qb.update(sales_invoice) 19 | .set("einvoice_status", "Pending Cancellation") 20 | .where((sales_invoice.docstatus == 2) & (IfNull(sales_invoice.irn, "") != "")) 21 | .run() 22 | ) 23 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/migrate_gstr1_log_to_returns_log.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.table_exists("GSTR-1 Log"): 6 | return 7 | 8 | old = frappe.qb.DocType("GSTR-1 Log") 9 | old_docs = frappe.qb.from_(old).select("*").run(as_dict=True) 10 | 11 | for doc in old_docs: 12 | new_doc = frappe.get_doc( 13 | {**doc, "doctype": "GST Return Log", "name": None, "return_type": "GSTR1"} 14 | ) 15 | new_doc.insert(ignore_if_duplicate=True) 16 | 17 | # Drop the old table 18 | frappe.db.delete("GSTR-1 Log") 19 | 20 | # Clear all fields saved in GSTR-1 21 | frappe.db.delete("Singles", {"doctype": "GSTR-1"}) 22 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/report/audit_trail/audit_trail.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2023-07-14 18:21:29.788627", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "modified": "2023-07-25 16:35:23.470355", 12 | "modified_by": "Administrator", 13 | "module": "Audit Trail", 14 | "name": "Audit Trail", 15 | "owner": "Administrator", 16 | "prepared_report": 0, 17 | "ref_doctype": "Version", 18 | "report_name": "Audit Trail", 19 | "report_type": "Script Report", 20 | "roles": [ 21 | { 22 | "role": "System Manager" 23 | }, 24 | { 25 | "role": "Administrator" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserPreset: "conventional-changelog-conventionalcommits", 3 | rules: { 4 | "subject-empty": [2, "never"], 5 | "type-case": [2, "always", "lower-case"], 6 | "type-empty": [2, "never"], 7 | "type-enum": [ 8 | 2, 9 | "always", 10 | [ 11 | "build", 12 | "chore", 13 | "ci", 14 | "docs", 15 | "feat", 16 | "fix", 17 | "perf", 18 | "refactor", 19 | "revert", 20 | "style", 21 | "test", 22 | ], 23 | ], 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: true 3 | 4 | comment: 5 | behavior: default 6 | layout: reach,diff,flags,tree,reach 7 | show_carryforward_flags: false 8 | 9 | coverage: 10 | precision: 2 11 | round: down 12 | status: 13 | changes: false 14 | default_rules: 15 | flag_coverage_not_uploaded_behavior: include 16 | 17 | patch: 18 | default: 19 | base: auto 20 | branches: 21 | - ^develop$ 22 | if_ci_failed: ignore 23 | only_pulls: true 24 | target: 85% 25 | threshold: 1% 26 | 27 | project: 28 | default: 29 | base: auto 30 | threshold: 20% 31 | 32 | github_checks: 33 | annotations: true 34 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/add_match_found_in_purchase_reconciliation_status.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | DOCTYPES = ("Purchase Invoice", "Bill of Entry") 4 | 5 | 6 | def execute(): 7 | for doctype in DOCTYPES: 8 | doc_names = frappe.get_all( 9 | "GST Inward Supply", 10 | filters={ 11 | "action": "No Action", 12 | "link_name": ("!=", ""), 13 | "link_doctype": doctype, 14 | }, 15 | pluck="link_name", 16 | ) 17 | 18 | if not doc_names: 19 | continue 20 | 21 | frappe.db.set_value( 22 | doctype, {"name": ("in", doc_names)}, "reconciliation_status", "Match Found" 23 | ) 24 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/remove_deprecated_docs.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | to_delete = { 6 | "DocType": [ 7 | "E Invoice Request Log", 8 | "E Invoice Settings", 9 | "E Invoice User", 10 | "HSN Tax Rate", 11 | ], 12 | "Print Format": [ 13 | "GST E-Invoice", 14 | ], 15 | "Report": [ 16 | "Eway Bill", 17 | ], 18 | } 19 | 20 | for doctype, names in to_delete.items(): 21 | frappe.delete_doc( 22 | doctype, 23 | names, 24 | force=True, 25 | ignore_permissions=True, 26 | ignore_missing=True, 27 | ) 28 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["version-14", "version-15"], 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", { 5 | "preset": "angular", 6 | "releaseRules": [ 7 | {"breaking": true, "release": false} 8 | ] 9 | }, 10 | "@semantic-release/release-notes-generator", 11 | [ 12 | "@semantic-release/exec", { 13 | "prepareCmd": 'sed -ir -E "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${nextRelease.version}\"/" india_compliance/__init__.py' 14 | } 15 | ], 16 | [ 17 | "@semantic-release/git", { 18 | "assets": ["india_compliance/__init__.py"], 19 | "message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}" 20 | } 21 | ], 22 | "@semantic-release/github" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/item.js: -------------------------------------------------------------------------------- 1 | frappe.ui.form.on('Item', { 2 | onload: function(frm) { 3 | india_compliance.set_hsn_code_query(frm.get_field("gst_hsn_code")); 4 | }, 5 | 6 | gst_hsn_code: function(frm) { 7 | if ((!frm.doc.taxes || !frm.doc.taxes.length) && frm.doc.gst_hsn_code) { 8 | frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => { 9 | $.each(hsn_doc.taxes || [], function(_, tax) { 10 | let a = frappe.model.add_child(frm.doc, 'Item Tax', 'taxes'); 11 | a.item_tax_template = tax.item_tax_template; 12 | a.tax_category = tax.tax_category; 13 | a.valid_from = tax.valid_from; 14 | frm.refresh_field('taxes'); 15 | }); 16 | }); 17 | } 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /india_compliance/public/js/components/set_gstin_options.js: -------------------------------------------------------------------------------- 1 | frappe.provide("india_compliance"); 2 | 3 | india_compliance.set_gstin_options = async function ( 4 | frm, 5 | show_all_option = false, 6 | exclude_isd = false 7 | ) { 8 | const { query, params } = india_compliance.get_gstin_query( 9 | frm.doc.company, 10 | "Company", 11 | exclude_isd 12 | ); 13 | const { message } = await frappe.call({ 14 | method: query, 15 | args: params, 16 | }); 17 | 18 | if (!message) return []; 19 | if (show_all_option) message.unshift("All"); 20 | 21 | const gstin_field = frm.get_field("company_gstin"); 22 | gstin_field.set_data(message); 23 | return message; 24 | }; 25 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/Message.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | 28 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/set_download_document_for_2a_2b.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if frappe.db.has_column("GST Inward Supply", "is_downloaded_from_2a"): 6 | # set "is_downloaded_from_2a" to "1" for all GST Inward Supply 7 | frappe.db.set_value( 8 | "GST Inward Supply", {"name": ["is", "set"]}, "is_downloaded_from_2a", 1 9 | ) 10 | 11 | if frappe.db.has_column("GST Inward Supply", "is_downloaded_from_2b"): 12 | # set "is_downloaded_from_2b" to "1" where 2B return period is set 13 | frappe.db.set_value( 14 | "GST Inward Supply", 15 | {"return_period_2b": ["is", "set"]}, 16 | "is_downloaded_from_2b", 17 | 1, 18 | ) 19 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/update_action_for_rejected_invoices_in_gst_inward_supply.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import IfNull 3 | 4 | 5 | def execute(): 6 | """ 7 | Update the action for invoices where ims_action is "Rejected" 8 | 1. If invoice is "Rejected" then mark action as "Ignore" only if invoice is not matched. 9 | 2. And copy current action to previous action. 10 | """ 11 | GSTR2 = frappe.qb.DocType("GST Inward Supply") 12 | 13 | ( 14 | frappe.qb.update(GSTR2) 15 | .set("previous_action", GSTR2.action) 16 | .set("action", "Ignore") 17 | .where(GSTR2.ims_action == "Rejected") 18 | .where(IfNull(GSTR2.link_name, "") == "") 19 | .run() 20 | ) 21 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_invoice/e_invoice.css: -------------------------------------------------------------------------------- 1 | .print-format .section-heading { 2 | margin-top: 15px; 3 | margin-bottom: 10px; 4 | font-weight: bold; 5 | } 6 | 7 | 8 | .print-format .print-heading span.docname { 9 | font-size: 15px; 10 | color: inherit; 11 | } 12 | 13 | .print-format .info-section { 14 | border-bottom: 1px solid #d1d8dd; 15 | padding-bottom: 16px; 16 | } 17 | 18 | .print-format h6.address-heading { 19 | font-weight: bold; 20 | margin-top: 8px; 21 | margin-bottom: 8px; 22 | } 23 | 24 | .print-format .e-invoice-table { 25 | margin: 10px 0; 26 | } 27 | 28 | .print-format .qrcode { 29 | width: 175px; 30 | } 31 | 32 | .print-format .no-preview-available { 33 | color: var(--gray-500); 34 | font-size: 14px; 35 | } 36 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_balance/gst_balance.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2023-06-05 14:48:26.693826", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "modified": "2023-06-05 14:48:26.693826", 12 | "modified_by": "Administrator", 13 | "module": "GST India", 14 | "name": "GST Balance", 15 | "owner": "Administrator", 16 | "prepared_report": 0, 17 | "ref_doctype": "GL Entry", 18 | "report_name": "GST Balance", 19 | "report_type": "Script Report", 20 | "roles": [ 21 | { 22 | "role": "Accounts User" 23 | }, 24 | { 25 | "role": "Accounts Manager" 26 | }, 27 | { 28 | "role": "Auditor" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_default_for_audit_trail_notification.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | import frappe.defaults 3 | from frappe.utils.user import get_users_with_role 4 | 5 | 6 | def execute(): 7 | # All companies in India where PAN is of a registered company or is not set 8 | notification_required = frappe.get_all( 9 | "Company", 10 | filters={"country": "India"}, 11 | or_filters=( 12 | ("pan", "like", "___C%"), 13 | ("pan", "is", "not set"), 14 | ), 15 | limit=1, 16 | ) 17 | 18 | if not notification_required: 19 | return 20 | 21 | for user in get_users_with_role("Accounts Manager"): 22 | frappe.defaults.set_user_default("needs_audit_trail_notification", 1, user=user) 23 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_sales_register/gst_sales_register.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2024-03-11 16:02:41.778873", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "letterhead": null, 12 | "modified": "2024-03-11 16:03:18.740603", 13 | "modified_by": "Administrator", 14 | "module": "GST India", 15 | "name": "GST Sales Register", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Sales Invoice", 19 | "report_name": "GST Sales Register", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Accounts Manager" 24 | }, 25 | { 26 | "role": "Accounts User" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/report/india_compliance_api_usage/india_compliance_api_usage.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2025-02-11 12:15:09.380757", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "letterhead": null, 12 | "modified": "2025-02-11 12:17:30.611180", 13 | "modified_by": "Administrator", 14 | "module": "GST India", 15 | "name": "India Compliance API Usage", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Integration Request", 19 | "report_name": "India Compliance API Usage", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "System Manager" 24 | } 25 | ], 26 | "timeout": 0 27 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_advance_detail/gst_advance_detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2023-09-17 16:44:05.262440", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "json": "{}", 12 | "letterhead": null, 13 | "modified": "2023-09-17 16:44:32.575646", 14 | "modified_by": "Administrator", 15 | "module": "GST India", 16 | "name": "GST Advance Detail", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "ref_doctype": "Payment Entry", 20 | "report_name": "GST Advance Detail", 21 | "report_type": "Script Report", 22 | "roles": [ 23 | { 24 | "role": "Accounts User" 25 | }, 26 | { 27 | "role": "Accounts Manager" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /india_compliance/audit_trail/client_scripts/customize_form.js: -------------------------------------------------------------------------------- 1 | frappe.ui.form.on("Customize Form", { 2 | refresh: function (frm) { 3 | const audit_trail_enabled = 4 | frm.doc.doc_type && frm.doc.__onload?.audit_trail_enabled; 5 | 6 | // this should never happen, but just in case 7 | if (audit_trail_enabled && !frm.doc.track_changes) { 8 | frm.set_value("track_changes", 1); 9 | } 10 | 11 | frm.set_df_property("track_changes", "read_only", audit_trail_enabled); 12 | frm.set_df_property( 13 | "track_changes", 14 | "description", 15 | audit_trail_enabled 16 | ? __("This setting cannot be edited to ensure Audit Trail integrity.") 17 | : "" 18 | ); 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/e_invoice_summary/e_invoice_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2021-03-12 11:23:37.312294", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "json": "{}", 13 | "letter_head": "", 14 | "modified": "2022-03-01 15:49:13.912294", 15 | "modified_by": "Administrator", 16 | "module": "GST India", 17 | "name": "e-Invoice Summary", 18 | "owner": "Administrator", 19 | "prepared_report": 0, 20 | "ref_doctype": "Sales Invoice", 21 | "report_name": "e-Invoice Summary", 22 | "report_type": "Script Report", 23 | "roles": [ 24 | { 25 | "role": "Accounts Manager" 26 | }, 27 | { 28 | "role": "Accounts User" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/subcontracting_order.js: -------------------------------------------------------------------------------- 1 | frappe.ui.form.on("Subcontracting Order", { 2 | setup(frm) { 3 | frm.set_query("taxes_and_charges", function () { 4 | return { 5 | filters: [ 6 | ["disabled", "=", 0], 7 | ["company", "=", frm.doc.company], 8 | ], 9 | }; 10 | }); 11 | }, 12 | onload(frm) { 13 | frm.taxes_controller = new india_compliance.taxes_controller(frm, { 14 | total_taxable_value: "total", 15 | }); 16 | }, 17 | 18 | taxes_and_charges(frm) { 19 | frm.taxes_controller.update_taxes(frm); 20 | }, 21 | }); 22 | 23 | frappe.ui.form.on( 24 | "Subcontracting Order Item", 25 | india_compliance.taxes_controller_events 26 | ); 27 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/update_total_taxes_for_gst_inward_supply.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import Sum 3 | 4 | 5 | def execute(): 6 | # Get GST Inward Supply Items 7 | dt = frappe.qb.DocType("GST Inward Supply Item") 8 | inward_supply_values = ( 9 | frappe.qb.from_(dt) 10 | .select( 11 | dt.parent.as_("name"), 12 | Sum(dt.taxable_value).as_("taxable_value"), 13 | Sum(dt.igst).as_("igst"), 14 | Sum(dt.cgst).as_("cgst"), 15 | Sum(dt.sgst).as_("sgst"), 16 | Sum(dt.cess).as_("cess"), 17 | ) 18 | .groupby(dt.parent) 19 | .run(as_dict=True) 20 | ) 21 | 22 | frappe.db.bulk_update( 23 | "GST Inward Supply", {d.pop("name"): d for d in inward_supply_values} 24 | ) 25 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gstin_status/gstin_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2025-01-28 17:10:48.875369", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "json": "{}", 12 | "letterhead": null, 13 | "modified": "2025-01-29 13:02:30.885362", 14 | "modified_by": "Administrator", 15 | "module": "GST India", 16 | "name": "GSTIN Status", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "ref_doctype": "GSTIN", 20 | "report_name": "GSTIN Status", 21 | "report_type": "Script Report", 22 | "roles": [ 23 | { 24 | "role": "System Manager" 25 | }, 26 | { 27 | "role": "Accounts Manager" 28 | }, 29 | { 30 | "role": "Accounts User" 31 | } 32 | ], 33 | "timeout": 1500 34 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2023-07-17 17:06:02.333972", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "letter_head": "SMC - New", 12 | "letterhead": null, 13 | "modified": "2023-07-17 17:06:02.333972", 14 | "modified_by": "Administrator", 15 | "module": "GST India", 16 | "name": "GSTR-3B Details", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "ref_doctype": "Purchase Invoice", 20 | "report_name": "GSTR-3B Details", 21 | "report_type": "Script Report", 22 | "roles": [ 23 | { 24 | "role": "Accounts User" 25 | }, 26 | { 27 | "role": "Accounts Manager" 28 | }, 29 | { 30 | "role": "Auditor" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log_list.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.listview_settings["e-Invoice Log"] = { 5 | hide_name_column: true, 6 | 7 | button: { 8 | show: function (doc) { 9 | return doc.reference_name; 10 | }, 11 | 12 | get_label: function () { 13 | return __("Open Reference"); 14 | }, 15 | 16 | get_description: function (doc) { 17 | return __("Open {0}", [ 18 | `${__(doc.reference_doctype)}: ${doc.reference_name}`, 19 | ]); 20 | }, 21 | 22 | action: function (doc) { 23 | frappe.set_route("Form", doc.reference_doctype, doc.reference_name); 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log_list.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.listview_settings["e-Waybill Log"] = { 5 | hide_name_column: true, 6 | 7 | button: { 8 | show: function (doc) { 9 | return doc.reference_name; 10 | }, 11 | 12 | get_label: function () { 13 | return __("Open Reference"); 14 | }, 15 | 16 | get_description: function (doc) { 17 | return __("Open {0}", [ 18 | `${__(doc.reference_doctype)}: ${doc.reference_name}`, 19 | ]); 20 | }, 21 | 22 | action: function (doc) { 23 | frappe.set_route("Form", doc.reference_doctype, doc.reference_name); 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_action/gstr_action.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class GSTRAction(Document): 9 | pass 10 | 11 | 12 | def set_gstr_actions(doc, request_type, token, request_id, status=None): 13 | if not token: 14 | return 15 | 16 | row = { 17 | "request_type": request_type, 18 | "request_id": request_id, 19 | "token": token, 20 | "creation_time": frappe.utils.now_datetime(), 21 | } 22 | 23 | if status: 24 | row["status"] = status 25 | 26 | doc.append("actions", row) 27 | # Integration Requests may be cleared by a scheduler job 28 | doc.flags.ignore_links = True 29 | doc.save() 30 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/supplier.js: -------------------------------------------------------------------------------- 1 | const DOCTYPE = "Supplier"; 2 | 3 | validate_pan(DOCTYPE); 4 | validate_gstin(DOCTYPE); 5 | update_gstin_in_other_documents(DOCTYPE); 6 | show_overseas_disabled_warning(DOCTYPE); 7 | set_gstin_options_and_status(DOCTYPE); 8 | set_gst_category(DOCTYPE); 9 | set_pan_status(DOCTYPE) 10 | 11 | frappe.ui.form.on(DOCTYPE, { 12 | gstin(frm) { 13 | if ( 14 | !frm.doc.is_transporter || 15 | !frm.doc.gstin || 16 | frm.doc.gstin.length < 15 || 17 | frm.doc.gst_transporter_id 18 | ) 19 | return; 20 | 21 | frm.set_value("gst_transporter_id", frm.doc.gstin); 22 | }, 23 | 24 | gst_transporter_id(frm) { 25 | india_compliance.validate_gst_transporter_id(frm.doc.gst_transporter_id, frm.doc); 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2018-04-26 10:49:29.159400", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "modified": "2023-05-08 18:32:52.251457", 12 | "modified_by": "Administrator", 13 | "module": "GST India", 14 | "name": "HSN-wise-summary of outward supplies", 15 | "owner": "Administrator", 16 | "prepared_report": 0, 17 | "ref_doctype": "Sales Invoice", 18 | "report_name": "HSN-wise-summary of outward supplies", 19 | "report_type": "Script Report", 20 | "roles": [ 21 | { 22 | "role": "Accounts Manager" 23 | }, 24 | { 25 | "role": "Accounts User" 26 | }, 27 | { 28 | "role": "Auditor" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Since version 2.23 (released in August 2019), git-blame has a feature 2 | # to ignore or bypass certain commits. 3 | # 4 | # This file contains a list of commits that are not likely what you 5 | # are looking for in a blame, such as mass reformatting or renaming. 6 | # You can set this file as a default ignore file for blame by running 7 | # the following command. 8 | # 9 | # $ git config blame.ignoreRevsFile .git-blame-ignore-revs 10 | 11 | # absolufy imports 12 | 79da1480c6187839aff417b99aea2e1b71014a00 13 | 14 | # formatting with black 15 | 702dd2a60de6957e0cddb1e82e887dd90124baa0 16 | 17 | # sort lines in hooks 18 | dec2438be2d63eff24da45d6c936ec846d8ab0df 19 | 20 | # refactor window.ic => window.india_compliance 21 | 38a53480c70a2c820049f023cbabbdaa3ea8aad6 22 | 23 | # updated black in pre-commit 24 | d8c2469651974cc82a2bb50c93e22f723cbb5b42 25 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/tax_category.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | 4 | 5 | def validate(doc, method=None): 6 | if doc.get("gst_state") and frappe.db.get_value( 7 | "Tax Category", 8 | { 9 | "gst_state": doc.gst_state, 10 | "is_inter_state": doc.is_inter_state, 11 | "is_reverse_charge": doc.is_reverse_charge, 12 | }, 13 | ): 14 | if doc.is_inter_state: 15 | frappe.throw( 16 | _("Inter State tax category for GST State {0} already exists").format( 17 | doc.gst_state 18 | ) 19 | ) 20 | else: 21 | frappe.throw( 22 | _("Intra State tax category for GST State {0} already exists").format( 23 | doc.gst_state 24 | ) 25 | ) 26 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Resilient Tech and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe import _ 6 | from frappe.model.document import Document 7 | 8 | from india_compliance.gst_india.utils import send_updated_doc 9 | from india_compliance.gst_india.utils.e_waybill import _fetch_e_waybill_data 10 | 11 | 12 | class eWaybillLog(Document): 13 | def before_print(self, print_settings=None): 14 | if self.data and self.is_latest_data: 15 | return 16 | 17 | doc = frappe.get_doc(self.reference_doctype, self.reference_name) 18 | _fetch_e_waybill_data(doc, self) 19 | send_updated_doc(self) 20 | frappe.msgprint( 21 | _("Fetched latest e-Waybill data"), alert=True, indicator="green" 22 | ) 23 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_vehicle_no_field_in_purchase_receipt.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | """ 6 | 'lr_no' field is labeled as 'Vehicle Number' in Purchase Receipt(ERPNext). 7 | Values will be identified using regex pattern and updated to vehicle_no field. 8 | REGEX pattern will identify foll0wing sequences: 9 | - GJO6AB1234 10 | - gj 06 a 1234 11 | - gj06-ab-1234 12 | - Gj06 abc 1234 13 | """ 14 | REGEX_PATTERN = r"^[a-zA-Z]{2}[-\s]?[0-9]{2}[-\s]?[a-zA-Z]{1,3}[-\s]?[0-9]{4}$" 15 | pr = frappe.qb.DocType("Purchase Receipt") 16 | 17 | ( 18 | frappe.qb.update(pr) 19 | .set(pr.vehicle_no, pr.lr_no) 20 | .where(pr.lr_no.regexp(REGEX_PATTERN)) 21 | .run() 22 | ) 23 | 24 | (frappe.qb.update(pr).set(pr.lr_no, "").where(pr.lr_no.regexp(REGEX_PATTERN)).run()) 25 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_purchase_register/gst_purchase_register.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2024-08-26 20:42:05.592632", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "letterhead": null, 12 | "modified": "2024-08-26 20:42:05.592632", 13 | "modified_by": "Administrator", 14 | "module": "GST India", 15 | "name": "GST Purchase Register", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Purchase Invoice", 19 | "report_name": "GST Purchase Register", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Purchase User" 24 | }, 25 | { 26 | "role": "Accounts User" 27 | }, 28 | { 29 | "role": "Auditor" 30 | }, 31 | { 32 | "role": "Accounts Manager" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /.github/workflows/linters.yml: -------------------------------------------------------------------------------- 1 | name: Linters 2 | 3 | on: 4 | workflow_call: 5 | pull_request: 6 | paths-ignore: 7 | - '**.md' 8 | - '**.csv' 9 | 10 | jobs: 11 | 12 | linters: 13 | name: linters 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | 18 | - name: Set up Python 3.10 19 | uses: actions/setup-python@v6 20 | with: 21 | python-version: '3.10' 22 | 23 | - name: Install and Run Pre-commit 24 | uses: pre-commit/action@v3.0.1 25 | 26 | - name: Download Semgrep rules 27 | run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules 28 | 29 | - name: Download semgrep 30 | run: pip install semgrep 31 | 32 | - name: Run Semgrep rules 33 | run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness -------------------------------------------------------------------------------- /india_compliance/gst_india/report/hsn_wise_summary_of_inward_supplies/hsn_wise_summary_of_inward_supplies.py: -------------------------------------------------------------------------------- 1 | from india_compliance.gst_india.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies import ( 2 | get_columns, 3 | process_hsn_data, 4 | validate_filters, 5 | ) 6 | from india_compliance.gst_india.utils.gstr3b.gstr3b_data import GSTR3BInvoices 7 | 8 | 9 | def execute(filters=None): 10 | if not filters: 11 | filters = {} 12 | 13 | validate_filters(filters) 14 | 15 | columns = get_columns(filters) 16 | data = get_data(filters) 17 | 18 | return columns, data 19 | 20 | 21 | def get_data(filters): 22 | _class = GSTR3BInvoices(filters) 23 | invoices = [] 24 | 25 | for doctype in ("Purchase Invoice", "Bill of Entry"): 26 | invoices.extend(_class.get_data(doctype)) 27 | 28 | return process_hsn_data(invoices) 29 | -------------------------------------------------------------------------------- /india_compliance/patches/v16/remove_legacy_report_fixtures.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | legacy_reports = [ 6 | "GSTR-1", 7 | "GST Sales Register Beta", 8 | "GST Purchase Register Beta", 9 | "GST Itemised Sales Register", 10 | "GST Itemised Purchase Register", 11 | ] 12 | 13 | try: 14 | reports = frappe.get_all( 15 | "Report", 16 | filters={"reference_report": ("in", legacy_reports)}, 17 | fields="name", 18 | pluck="name", 19 | ) 20 | 21 | frappe.delete_doc( 22 | "Report", 23 | legacy_reports + reports, 24 | force=True, 25 | ignore_permissions=True, 26 | delete_permanently=True, 27 | ) 28 | 29 | except Exception: 30 | frappe.log_error(title="remove_legacy_report_fixtures failed") 31 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_waybill/e_waybill.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_value": 0, 3 | "align_labels_right": 0, 4 | "creation": "2022-03-27 16:32:27.037976", 5 | "custom_format": 0, 6 | "default_print_language": "en", 7 | "disabled": 0, 8 | "doc_type": "e-Waybill Log", 9 | "docstatus": 0, 10 | "doctype": "Print Format", 11 | "font_size": 14, 12 | "html": "", 13 | "idx": 0, 14 | "line_breaks": 0, 15 | "margin_bottom": 0.0, 16 | "margin_left": 0.0, 17 | "margin_right": 0.0, 18 | "margin_top": 0.0, 19 | "modified": "2022-04-12 11:05:49.333577", 20 | "modified_by": "Administrator", 21 | "module": "GST India", 22 | "name": "e-Waybill", 23 | "owner": "Administrator", 24 | "page_number": "Hide", 25 | "print_format_builder": 0, 26 | "print_format_builder_beta": 0, 27 | "print_format_type": "Jinja", 28 | "raw_printing": 0, 29 | "show_section_headings": 0, 30 | "standard": "Yes" 31 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_waybill_detailed/e_waybill_detailed.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_value": 0, 3 | "align_labels_right": 0, 4 | "creation": "2025-01-16 11:59:28.744326", 5 | "custom_format": 0, 6 | "default_print_language": "en", 7 | "disabled": 0, 8 | "doc_type": "e-Waybill Log", 9 | "docstatus": 0, 10 | "doctype": "Print Format", 11 | "font_size": 14, 12 | "idx": 0, 13 | "line_breaks": 0, 14 | "margin_bottom": 0.0, 15 | "margin_left": 0.0, 16 | "margin_right": 0.0, 17 | "margin_top": 0.0, 18 | "modified": "2025-01-21 10:42:52.756385", 19 | "modified_by": "Administrator", 20 | "module": "GST India", 21 | "name": "e-Waybill Detailed", 22 | "owner": "Administrator", 23 | "page_number": "Hide", 24 | "print_format_builder": 0, 25 | "print_format_builder_beta": 0, 26 | "print_format_type": "Jinja", 27 | "raw_printing": 0, 28 | "show_section_headings": 0, 29 | "standard": "Yes" 30 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/delivery_note.js: -------------------------------------------------------------------------------- 1 | const DOCTYPE = "Delivery Note"; 2 | setup_e_waybill_actions(DOCTYPE); 3 | 4 | frappe.ui.form.on(DOCTYPE, { 5 | setup(frm) { 6 | frm.set_query("port_address", { 7 | filters: { 8 | country: "India", 9 | }, 10 | }); 11 | }, 12 | refresh(frm) { 13 | if (!gst_settings.enable_e_waybill || !gst_settings.enable_e_waybill_from_dn) 14 | return; 15 | show_sandbox_mode_indicator(); 16 | }, 17 | 18 | after_save(frm) { 19 | if (is_e_waybill_applicable(frm) && !is_e_waybill_generatable(frm)) 20 | frappe.show_alert( 21 | { 22 | message: __("Billing Address is required to create e-Waybill"), 23 | indicator: "yellow", 24 | }, 25 | 10 26 | ); 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_account_wise_summary/gst_account_wise_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "add_translate_data": 0, 4 | "columns": [], 5 | "creation": "2025-04-25 16:27:21.521616", 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letter_head": "", 13 | "letterhead": null, 14 | "modified": "2025-04-25 16:27:21.521616", 15 | "modified_by": "Administrator", 16 | "module": "GST India", 17 | "name": "GST Account-wise Summary", 18 | "owner": "Administrator", 19 | "prepared_report": 0, 20 | "ref_doctype": "Purchase Invoice", 21 | "report_name": "GST Account-wise Summary", 22 | "report_type": "Script Report", 23 | "roles": [ 24 | { 25 | "role": "Accounts User" 26 | }, 27 | { 28 | "role": "Auditor" 29 | }, 30 | { 31 | "role": "Accounts Manager" 32 | } 33 | ], 34 | "timeout": 0 35 | } 36 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_tax_rate_wise_summary/gst_tax_rate_wise_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "add_translate_data": 0, 4 | "columns": [], 5 | "creation": "2025-04-03 11:01:41.181976", 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letter_head": "", 13 | "letterhead": null, 14 | "modified": "2025-04-03 12:09:20.116328", 15 | "modified_by": "Administrator", 16 | "module": "GST India", 17 | "name": "GST Tax Rate-wise Summary", 18 | "owner": "Administrator", 19 | "prepared_report": 0, 20 | "ref_doctype": "Sales Invoice", 21 | "report_name": "GST Tax Rate-wise Summary", 22 | "report_type": "Script Report", 23 | "roles": [ 24 | { 25 | "role": "Accounts User" 26 | }, 27 | { 28 | "role": "Accounts Manager" 29 | }, 30 | { 31 | "role": "Auditor" 32 | } 33 | ], 34 | "timeout": 0 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/initiate_release.yml: -------------------------------------------------------------------------------- 1 | 2 | # This workflow is agnostic to branches. Only maintain on develop branch. 3 | # To add/remove versions just modify the matrix. 4 | 5 | name: Initiate Release 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | release: 11 | name: Release 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | version: ["14", "15"] 17 | 18 | steps: 19 | - uses: octokit/request-action@v2.x 20 | with: 21 | route: POST /repos/{owner}/{repo}/pulls 22 | owner: resilient-tech 23 | repo: india-compliance 24 | title: |- 25 | "chore: release v${{ matrix.version }}" 26 | body: "Automated Release." 27 | base: version-${{ matrix.version }} 28 | head: version-${{ matrix.version }}-hotfix 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} 31 | -------------------------------------------------------------------------------- /india_compliance/gst_india/print_format/e_invoice/e_invoice.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_value": 0, 3 | "align_labels_right": 1, 4 | "creation": "2022-04-04 21:24:15.372327", 5 | "custom_format": 0, 6 | "default_print_language": "en-US", 7 | "disabled": 0, 8 | "doc_type": "Sales Invoice", 9 | "docstatus": 0, 10 | "doctype": "Print Format", 11 | "font": "Default", 12 | "font_size": 14, 13 | "html": "", 14 | "idx": 0, 15 | "line_breaks": 1, 16 | "margin_bottom": 15.0, 17 | "margin_left": 15.0, 18 | "margin_right": 15.0, 19 | "margin_top": 15.0, 20 | "modified": "2022-04-12 10:39:40.065409", 21 | "modified_by": "Administrator", 22 | "module": "GST India", 23 | "name": "e-Invoice", 24 | "owner": "Administrator", 25 | "page_number": "Hide", 26 | "print_format_builder": 0, 27 | "print_format_builder_beta": 0, 28 | "print_format_type": "Jinja", 29 | "raw_printing": 0, 30 | "show_section_headings": 1, 31 | "standard": "Yes" 32 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/report/bill_of_entry_summary/bill_of_entry_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "add_translate_data": 0, 4 | "columns": [], 5 | "creation": "2023-05-08 12:24:58.886166", 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letterhead": null, 13 | "modified": "2025-11-19 15:25:58.567516", 14 | "modified_by": "Administrator", 15 | "module": "GST India", 16 | "name": "Bill of Entry Summary", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "ref_doctype": "Bill of Entry", 20 | "report_name": "Bill of Entry Summary", 21 | "report_type": "Script Report", 22 | "roles": [ 23 | { 24 | "role": "Purchase User" 25 | }, 26 | { 27 | "role": "Accounts User" 28 | }, 29 | { 30 | "role": "Accounts Manager" 31 | }, 32 | { 33 | "role": "Auditor" 34 | } 35 | ], 36 | "timeout": 0 37 | } 38 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/summary_of_itc_availed/summary_of_itc_availed.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "add_translate_data": 0, 4 | "columns": [], 5 | "creation": "2025-04-21 18:24:49.646289", 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letterhead": null, 13 | "modified": "2025-04-21 18:26:50.175272", 14 | "modified_by": "Administrator", 15 | "module": "GST India", 16 | "name": "Summary of ITC Availed", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "ref_doctype": "Purchase Invoice", 20 | "report_name": "Summary of ITC Availed", 21 | "report_type": "Script Report", 22 | "roles": [ 23 | { 24 | "role": "Accounts User" 25 | }, 26 | { 27 | "role": "Purchase User" 28 | }, 29 | { 30 | "role": "Accounts Manager" 31 | }, 32 | { 33 | "role": "Auditor" 34 | } 35 | ], 36 | "timeout": 0 37 | } 38 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/version.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | 4 | from india_compliance.audit_trail.utils import ( 5 | get_audit_trail_doctypes, 6 | is_audit_trail_enabled, 7 | ) 8 | 9 | 10 | def validate(doc, method=None): 11 | if doc.is_new() or not is_audit_trail_enabled(): 12 | return 13 | 14 | validate_protected_version(doc) 15 | if old_doc := doc.get_doc_before_save(): 16 | validate_protected_version(old_doc) 17 | 18 | 19 | def on_trash(doc, method=None): 20 | if not is_audit_trail_enabled(): 21 | return 22 | 23 | validate_protected_version(doc) 24 | 25 | 26 | def validate_protected_version(doc): 27 | if doc.ref_doctype in get_audit_trail_doctypes(): 28 | frappe.throw( 29 | _( 30 | "Cannot alter Versions of {0}, since they are required for Audit Trail" 31 | ).format(_(doc.ref_doctype)) 32 | ) 33 | -------------------------------------------------------------------------------- /india_compliance/gst_india/number_card/pending_e_waybill/pending_e_waybill.json: -------------------------------------------------------------------------------- 1 | { 2 | "aggregate_function_based_on": "", 3 | "color": "#EC864B", 4 | "creation": "2023-08-04 11:30:55.485885", 5 | "docstatus": 0, 6 | "doctype": "Number Card", 7 | "document_type": "Sales Invoice", 8 | "dynamic_filters_json": "[]", 9 | "filters_json": "[[\"Sales Invoice\",\"e_waybill_status\",\"=\",\"Pending\"],[\"Sales Invoice\",\"docstatus\",\"=\",\"1\"]]", 10 | "function": "Count", 11 | "idx": 0, 12 | "is_public": 1, 13 | "is_standard": 1, 14 | "label": "Pending e-Waybills", 15 | "modified": "2025-11-21 18:02:20.611417", 16 | "modified_by": "Administrator", 17 | "module": "GST India", 18 | "name": "Pending e-Waybill", 19 | "owner": "Administrator", 20 | "parent_document_type": "", 21 | "report_function": "Sum", 22 | "show_full_number": 0, 23 | "show_percentage_stats": 0, 24 | "stats_time_interval": "Daily", 25 | "type": "Document Type" 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/semantic-commits.yml: -------------------------------------------------------------------------------- 1 | name: Semantic Commits 2 | 3 | on: 4 | pull_request: {} 5 | 6 | permissions: 7 | contents: read 8 | 9 | concurrency: 10 | group: commitcheck-frappe-${{ github.event.number }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | commitlint: 15 | name: Check Commit Titles 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v6 19 | with: 20 | fetch-depth: 200 21 | 22 | - uses: actions/setup-node@v6 23 | with: 24 | node-version: 20 25 | check-latest: true 26 | 27 | - name: Check commit titles 28 | run: | 29 | npm install @commitlint/cli @commitlint/config-conventional 30 | npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} 31 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/utils.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | import frappe.defaults 3 | 4 | 5 | def is_audit_trail_enabled(): 6 | return bool(frappe.db.get_single_value("Accounts Settings", "enable_audit_trail")) 7 | 8 | 9 | @frappe.whitelist() 10 | def get_audit_trail_doctypes(): 11 | return set(frappe.get_hooks("audit_trail_doctypes")) 12 | 13 | 14 | def enqueue_disable_audit_trail_notification(): 15 | frappe.enqueue( 16 | "india_compliance.audit_trail.utils.disable_audit_trail_notification", 17 | queue="short", 18 | ) 19 | 20 | 21 | @frappe.whitelist(methods=["POST"]) 22 | def disable_audit_trail_notification(): 23 | frappe.defaults.clear_user_default("needs_audit_trail_notification") 24 | 25 | 26 | @frappe.whitelist(methods=["POST"]) 27 | def enable_audit_trail(): 28 | accounts_settings = frappe.get_doc("Accounts Settings") 29 | accounts_settings.enable_audit_trail = 1 30 | accounts_settings.save() 31 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/gl_entry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | 4 | from india_compliance.gst_india.overrides.transaction import ( 5 | is_indian_registered_company, 6 | ) 7 | from india_compliance.gst_india.utils import get_all_gst_accounts 8 | 9 | 10 | def validate(doc, method=None): 11 | if doc.company_gstin or not is_indian_registered_company(doc): 12 | return 13 | 14 | gst_accounts = get_all_gst_accounts(doc.company) 15 | if doc.account not in gst_accounts: 16 | return 17 | 18 | frappe.throw( 19 | _( 20 | "Company GSTIN is a mandatory field for accounting of GST Accounts." 21 | " Run `Update GSTIN` patch from GST Balance Report to update GSTIN in all transactions." 22 | ) 23 | ) 24 | 25 | 26 | def update_gl_dict_with_regional_fields(doc, gl_dict): 27 | if doc.get("company_gstin"): 28 | gl_dict["company_gstin"] = doc.company_gstin 29 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "india_compliance" 3 | authors = [ 4 | { name = "Resilient Tech", email = "hello@indiacompliance.app" } 5 | ] 6 | description = "ERPNext app to simplify compliance with Indian Rules and Regulations" 7 | readme = "README.md" 8 | dynamic = ["version"] 9 | dependencies = [ 10 | "python-barcode~=0.15.1", 11 | "titlecase~=2.3", 12 | "pycryptodome~=3.19.0", 13 | 14 | # Not used directly - required by PyQRCode for PNG generation 15 | "pypng~=0.20220715.0", 16 | ] 17 | 18 | [build-system] 19 | requires = ["flit_core >=3.4,<4"] 20 | build-backend = "flit_core.buildapi" 21 | 22 | [tool.isort] 23 | profile = "black" 24 | known_frappe = "frappe" 25 | known_erpnext = "erpnext" 26 | no_lines_before = ["ERPNEXT"] 27 | sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FRAPPE", "ERPNEXT", "FIRSTPARTY", "LOCALFOLDER"] 28 | 29 | [tool.bench.dev-dependencies] 30 | parameterized = "~=0.8.1" 31 | time-machine = "~=2.10.0" 32 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/hsn_wise_summary_of_inward_supplies/hsn_wise_summary_of_inward_supplies.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "add_translate_data": 0, 4 | "columns": [], 5 | "creation": "2025-04-11 19:47:29.046936", 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letter_head": "", 13 | "letterhead": null, 14 | "modified": "2025-04-11 19:47:29.046936", 15 | "modified_by": "Administrator", 16 | "module": "GST India", 17 | "name": "HSN-wise-summary of inward supplies", 18 | "owner": "Administrator", 19 | "prepared_report": 0, 20 | "ref_doctype": "Purchase Invoice", 21 | "report_name": "HSN-wise-summary of inward supplies", 22 | "report_type": "Script Report", 23 | "roles": [ 24 | { 25 | "role": "Accounts User" 26 | }, 27 | { 28 | "role": "Auditor" 29 | }, 30 | { 31 | "role": "Accounts Manager" 32 | } 33 | ], 34 | "timeout": 0 35 | } 36 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/test_account_settings.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import frappe 4 | from frappe.tests import IntegrationTestCase 5 | 6 | 7 | class TestAccountsSettings(IntegrationTestCase): 8 | def test_validate_change_in_enable_audit_trail_and_validate_delete_linked_ledger_entries( 9 | self, 10 | ): 11 | doc = frappe.get_doc("Accounts Settings") 12 | 13 | doc.enable_audit_trail = 1 14 | doc.save() 15 | 16 | doc.delete_linked_ledger_entries = 1 17 | self.assertRaisesRegex( 18 | frappe.ValidationError, 19 | re.compile(r"cannot be enabled to ensure Audit Trail integrity$"), 20 | doc.save, 21 | ) 22 | 23 | doc.reload() 24 | 25 | doc.enable_audit_trail = 0 26 | self.assertRaisesRegex( 27 | frappe.ValidationError, 28 | re.compile(r"^(Audit Trail cannot be disabled once enabled*)"), 29 | doc.save, 30 | ) 31 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('GST HSN Code', { 5 | refresh: function(frm) { 6 | if(!frm.doc.__islocal){ 7 | frm.add_custom_button(__('Update Taxes for Items'), function(){ 8 | frappe.confirm( 9 | 'Are you sure? It will overwrite taxes for all items with HSN Code '+frm.doc.name+'.', 10 | function(){ 11 | frappe.call({ 12 | args:{ 13 | taxes: frm.doc.taxes, 14 | hsn_code: frm.doc.name 15 | }, 16 | method: 'india_compliance.gst_india.doctype.gst_hsn_code.gst_hsn_code.update_taxes_in_item_master', 17 | callback: function(r) { 18 | if(r.message){ 19 | frappe.show_alert(__('Items with this HSN code will be updated shortly')); 20 | } 21 | } 22 | }); 23 | } 24 | ); 25 | }); 26 | } 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /india_compliance/gst_india/number_card/pending_e_invoices/pending_e_invoices.json: -------------------------------------------------------------------------------- 1 | { 2 | "aggregate_function_based_on": "", 3 | "color": "#EC864B", 4 | "creation": "2023-07-25 15:10:39.976867", 5 | "docstatus": 0, 6 | "doctype": "Number Card", 7 | "document_type": "Sales Invoice", 8 | "dynamic_filters_json": "[]", 9 | "filters_config": "", 10 | "filters_json": "[[\"Sales Invoice\",\"einvoice_status\",\"=\",\"Pending\"],[\"Sales Invoice\",\"docstatus\",\"=\",\"1\"]]", 11 | "function": "Count", 12 | "idx": 0, 13 | "is_public": 1, 14 | "is_standard": 1, 15 | "label": "Pending e-Invoices", 16 | "method": "", 17 | "modified": "2025-11-21 18:02:31.451315", 18 | "modified_by": "Administrator", 19 | "module": "GST India", 20 | "name": "Pending e-Invoices", 21 | "owner": "Administrator", 22 | "parent_document_type": "", 23 | "report_function": "Sum", 24 | "show_full_number": 0, 25 | "show_percentage_stats": 0, 26 | "stats_time_interval": "Daily", 27 | "type": "Document Type" 28 | } 29 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/add_company_link_to_einvoice_settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import IfNull 3 | 4 | 5 | def execute(): 6 | """ 7 | update Company of E Invoice User from Dyanamic Link Table in Address 8 | """ 9 | 10 | if not frappe.db.table_exists("E Invoice User"): 11 | return 12 | 13 | user = frappe.qb.DocType("E Invoice User", alias="user") 14 | address = frappe.qb.DocType("Address", alias="address") 15 | dynamic_link = frappe.qb.DocType("Dynamic Link", alias="dynamic_link") 16 | ( 17 | frappe.qb.update(user) 18 | .join(address) 19 | .on(address.gstin == user.gstin) 20 | .join(dynamic_link) 21 | .on( 22 | (dynamic_link.parent == address.name) 23 | & (dynamic_link.link_doctype == "Company") 24 | ) 25 | .set(user.company, dynamic_link.link_name) 26 | .where(IfNull(user.company, "") == "") 27 | .run() 28 | ) 29 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/setup_custom_fields_for_gst.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.utils.custom_fields import delete_old_fields 4 | 5 | 6 | def execute(): 7 | delete_tax_id_custom_field() 8 | set_correct_state_number() 9 | remove_shipping_fields_from_purchase_invoice() 10 | 11 | 12 | def delete_tax_id_custom_field(): 13 | # delete custom field tax_id if it exists 14 | # this field was move to core ERPNext 15 | delete_old_fields("tax_id", ("Sales Order", "Sales Invoice", "Delivery Note")) 16 | 17 | 18 | def set_correct_state_number(): 19 | # set correct state number for all states with single digit state number 20 | frappe.db.sql( 21 | """UPDATE tabAddress SET gst_state_number=concat("0", gst_state_number) 22 | WHERE length(gst_state_number) = 1""" 23 | ) 24 | 25 | 26 | def remove_shipping_fields_from_purchase_invoice(): 27 | delete_old_fields( 28 | ("port_code", "shipping_bill_number", "shipping_bill_date"), "Purchase Invoice" 29 | ) 30 | -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_pending_boe_qty.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import IfNull, Sum 3 | 4 | 5 | def execute(): 6 | pi = frappe.qb.DocType("Purchase Invoice") 7 | pi_item = frappe.qb.DocType("Purchase Invoice Item") 8 | boe_item = frappe.qb.DocType("Bill of Entry Item") 9 | 10 | submitted_boe_qty = ( 11 | frappe.qb.from_(boe_item) 12 | .select(boe_item.pi_detail, Sum(boe_item.qty).as_("qty")) 13 | .where(boe_item.docstatus == 1) 14 | .groupby(boe_item.pi_detail) 15 | ) 16 | 17 | ( 18 | frappe.qb.update(pi_item) 19 | .join(pi) 20 | .on(pi_item.parent == pi.name) 21 | .left_join(submitted_boe_qty) 22 | .on(pi_item.name == submitted_boe_qty.pi_detail) 23 | .set( 24 | pi_item.pending_boe_qty, 25 | pi_item.qty - IfNull(submitted_boe_qty.qty, 0), 26 | ) 27 | .where(pi.docstatus == 1) 28 | .where(pi.gst_category == "Overseas") 29 | .run() 30 | ) 31 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/bill_of_entry_summary/bill_of_entry_summary.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.query_reports["Bill of Entry Summary"] = { 5 | "filters": [ 6 | { 7 | "fieldname": "company", 8 | "label": __("Company"), 9 | "fieldtype": "Link", 10 | "options": "Company", 11 | "reqd": 1, 12 | "default": frappe.defaults.get_user_default("Company") 13 | }, 14 | { 15 | "fieldname": "from_date", 16 | "label": __("From Date"), 17 | "fieldtype": "Date", 18 | "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), 19 | "reqd": 1 20 | }, 21 | { 22 | "fieldname": "to_date", 23 | "label": __("To Date"), 24 | "fieldtype": "Date", 25 | "default": frappe.datetime.get_today(), 26 | "reqd": 1 27 | }, 28 | ] 29 | } -------------------------------------------------------------------------------- /india_compliance/patches/post_install/set_gst_tax_type_in_journal_entry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder import Case 3 | 4 | from india_compliance.gst_india.utils import get_gst_account_gst_tax_type_map 5 | 6 | 7 | def execute(): 8 | gst_account_tax_type_map = get_gst_account_gst_tax_type_map() 9 | if not gst_account_tax_type_map: 10 | return 11 | 12 | gst_accounts_by_tax_type = {} 13 | for account, tax_type in gst_account_tax_type_map.items(): 14 | gst_accounts_by_tax_type.setdefault(tax_type, []).append(account) 15 | 16 | taxes_doctype = frappe.qb.DocType("Journal Entry Account") 17 | 18 | update_query = frappe.qb.update(taxes_doctype) 19 | conditions = Case() 20 | 21 | for gst_tax_account, gst_tax_name in gst_accounts_by_tax_type.items(): 22 | conditions = conditions.when( 23 | taxes_doctype.account.isin(gst_tax_name), gst_tax_account 24 | ) 25 | conditions = conditions.else_(None) 26 | update_query.set(taxes_doctype.gst_tax_type, conditions).run() 27 | -------------------------------------------------------------------------------- /india_compliance/public/js/components/number_card.js: -------------------------------------------------------------------------------- 1 | frappe.provide("india_compliance"); 2 | 3 | india_compliance.NumberCardManager = class NumberCardManager { 4 | constructor(opts) { 5 | Object.assign(this, opts); 6 | this.make_cards(); 7 | this.show_summary(); 8 | } 9 | 10 | make_cards() { 11 | this.$wrapper.empty(); 12 | this.$cards = []; 13 | this.$summary = $(`
`) 14 | .hide() 15 | .appendTo(this.$wrapper); 16 | 17 | this.cards.forEach(summary => { 18 | let number_card = frappe.utils.build_summary_item(summary); 19 | this.$cards.push(number_card); 20 | 21 | number_card.appendTo(this.$summary); 22 | }); 23 | 24 | this.$summary.css({ 25 | "border-bottom": "0px", 26 | "margin-left": "0px", 27 | "margin-right": "0px", 28 | }); 29 | } 30 | 31 | show_summary() { 32 | if (this.cards.length) this.$summary.show(); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: "node_modules|.git" 2 | default_stages: [commit] 3 | fail_fast: false 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.5.0 8 | hooks: 9 | - id: trailing-whitespace 10 | files: "india_compliance.*" 11 | exclude: ".*json$|.*txt$|.*csv|.*md" 12 | - id: check-yaml 13 | - id: no-commit-to-branch 14 | args: ["--branch", "develop"] 15 | - id: check-merge-conflict 16 | - id: check-ast 17 | 18 | - repo: https://github.com/psf/black 19 | rev: 25.1.0 20 | hooks: 21 | - id: black 22 | 23 | - repo: https://github.com/PyCQA/isort 24 | rev: 6.0.0 25 | hooks: 26 | - id: isort 27 | 28 | - repo: https://github.com/PyCQA/flake8 29 | rev: 7.1.1 30 | hooks: 31 | - id: flake8 32 | additional_dependencies: [flake8-isort, flake8-bugbear] 33 | 34 | ci: 35 | autoupdate_schedule: weekly 36 | skip: [] 37 | submodules: false 38 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/gst_job_work_stock_movement/gst_job_work_stock_movement.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2024-08-19 14:40:26.929838", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "letterhead": null, 12 | "modified": "2024-08-19 14:40:33.093455", 13 | "modified_by": "Administrator", 14 | "module": "GST India", 15 | "name": "GST Job Work Stock Movement", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "Subcontracting Order", 19 | "report_name": "GST Job Work Stock Movement", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Item Manager" 24 | }, 25 | { 26 | "role": "Stock Manager" 27 | }, 28 | { 29 | "role": "Stock User" 30 | }, 31 | { 32 | "role": "Accounts Manager" 33 | }, 34 | { 35 | "role": "Accounts User" 36 | }, 37 | { 38 | "role": "Manufacturing Manager" 39 | }, 40 | { 41 | "role": "Manufacturing User" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_reconciliation_status_for_manual_match.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | """ 6 | Match status was not being updated when manually matched. This patch will update the reconciliation status. 7 | """ 8 | inward_supply = frappe.qb.DocType("GST Inward Supply") 9 | docs = ( 10 | frappe.qb.from_(inward_supply) 11 | .select(inward_supply.link_doctype, inward_supply.link_name) 12 | .where(inward_supply.link_doctype.isin(("Purchase Invoice", "Bill of Entry"))) 13 | .where(inward_supply.action == "No Action") # status updated on action 14 | .where(inward_supply.match_status == "Manual Match") 15 | .run(as_dict=True) 16 | ) 17 | 18 | docs_to_update = {} 19 | 20 | for doc in docs: 21 | docs_to_update.setdefault(doc.link_doctype, []).append(doc.link_name) 22 | 23 | for doctype, doc_names in docs_to_update.items(): 24 | frappe.db.set_value( 25 | doctype, {"name": ("in", doc_names)}, "reconciliation_status", "Match Found" 26 | ) 27 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/update_return_logs_with_filing_preference.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.gst_india.utils.gstin_info import ( 4 | fetch_filing_preference, 5 | get_filing_preference, 6 | get_fy, 7 | ) 8 | 9 | 10 | def patch_filing_preference(gstin): 11 | logs = frappe.get_all( 12 | "GST Return Log", 13 | filters={ 14 | "filing_preference": ["is", "not set"], 15 | "gstin": gstin, 16 | "return_period": ["!=", "ALL"], 17 | "return_type": ["in", ["GSTR1", "GSTR3B"]], 18 | }, 19 | fields=["name", "return_period", "gstin"], 20 | ) 21 | 22 | if not logs: 23 | return 24 | 25 | gst_return_log = {} 26 | for log in logs: 27 | response = fetch_filing_preference(gstin, get_fy(log.return_period)) 28 | preference = get_filing_preference(log.return_period, response) 29 | gst_return_log[log.name] = {"filing_preference": preference} 30 | 31 | frappe.db.bulk_update("GST Return Log", gst_return_log, update_modified=False) 32 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | B007, 4 | B950, 5 | E101, 6 | E111, 7 | E114, 8 | E116, 9 | E117, 10 | E121, 11 | E122, 12 | E123, 13 | E124, 14 | E125, 15 | E126, 16 | E127, 17 | E128, 18 | E131, 19 | E201, 20 | E202, 21 | E203, 22 | E211, 23 | E221, 24 | E222, 25 | E223, 26 | E224, 27 | E225, 28 | E226, 29 | E228, 30 | E231, 31 | E241, 32 | E242, 33 | E251, 34 | E261, 35 | E262, 36 | E265, 37 | E266, 38 | E271, 39 | E272, 40 | E273, 41 | E274, 42 | E301, 43 | E302, 44 | E303, 45 | E305, 46 | E306, 47 | E401, 48 | E402, 49 | E501, 50 | E502, 51 | E701, 52 | E702, 53 | E703, 54 | E741, 55 | F403, 56 | W191, 57 | W291, 58 | W292, 59 | W293, 60 | W391, 61 | W503, 62 | W504, 63 | 64 | per-file-ignores = 65 | # syntax: [comma-separated path/to/file: comma-separated ERROR CODES] 66 | __init__.py, hooks.py: F401 67 | 68 | max-line-length = 200 69 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/test_customize_form.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import frappe 4 | from frappe.tests import IntegrationTestCase 5 | 6 | 7 | class TestCustomizeFormAuditTrail(IntegrationTestCase): 8 | def test_validate_customize_form(self): 9 | customize_frm = self.get_customize_form() 10 | customize_frm.doc_type = "Purchase Invoice" 11 | customize_frm.save_customization() 12 | 13 | frappe.db.set_single_value("Accounts Settings", "enable_audit_trail", 1) 14 | 15 | customize_frm.track_changes = 0 16 | self.assertRaisesRegex( 17 | frappe.ValidationError, 18 | re.compile(r"^(Cannot disable Track Changes for*)"), 19 | customize_frm.save_customization, 20 | ) 21 | frappe.db.set_single_value("Accounts Settings", "enable_audit_trail", 0) 22 | 23 | def get_customize_form(self, doctype=None): 24 | d = frappe.get_doc("Customize Form") 25 | if doctype: 26 | d.doc_type = doctype 27 | d.run_method("fetch_to_customize") 28 | return d 29 | -------------------------------------------------------------------------------- /india_compliance/gst_india/number_card/invoice_cancelled_but_not_e_invoice/invoice_cancelled_but_not_e_invoice.json: -------------------------------------------------------------------------------- 1 | { 2 | "aggregate_function_based_on": "", 3 | "color": "#EC864B", 4 | "creation": "2023-07-25 18:33:55.452360", 5 | "docstatus": 0, 6 | "doctype": "Number Card", 7 | "document_type": "Sales Invoice", 8 | "dynamic_filters_json": "[]", 9 | "filters_json": "[[\"Sales Invoice\",\"einvoice_status\",\"=\",\"Pending Cancellation\"],[\"Sales Invoice\",\"docstatus\",\"=\",\"2\"],[\"Sales Invoice\",\"irn\",\"is\",\"set\"]]", 10 | "function": "Count", 11 | "idx": 0, 12 | "is_public": 1, 13 | "is_standard": 1, 14 | "label": "Active e-Invoice, Cancelled Invoice", 15 | "method": "", 16 | "modified": "2025-11-21 18:02:11.655081", 17 | "modified_by": "Administrator", 18 | "module": "GST India", 19 | "name": "Invoice Cancelled But Not e-Invoice", 20 | "owner": "Administrator", 21 | "parent_document_type": "", 22 | "report_function": "Sum", 23 | "show_full_number": 0, 24 | "show_percentage_stats": 0, 25 | "stats_time_interval": "Daily", 26 | "type": "Document Type" 27 | } 28 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/router.js: -------------------------------------------------------------------------------- 1 | import AuthPage from "./pages/AuthPage.vue"; 2 | import AccountPage from "./pages/AccountPage.vue"; 3 | import MailSentPage from "./pages/MailSentPage.vue"; 4 | import PurchaseCreditsPage from "./pages/PurchaseCreditsPage.vue"; 5 | import PaymentPage from "./pages/PaymentPage.vue"; 6 | 7 | export const routes = [ 8 | { 9 | name: "auth", 10 | path: "/authentication", 11 | component: AuthPage, 12 | }, 13 | { 14 | name: "mailSent", 15 | path: "/mail-sent", 16 | component: MailSentPage, 17 | }, 18 | { 19 | name: "purchaseCredits", 20 | path: "/purchase-credits", 21 | component: PurchaseCreditsPage, 22 | }, 23 | { 24 | name: "paymentPage", 25 | path: "/payment-page", 26 | component: PaymentPage, 27 | }, 28 | { 29 | name: "home", 30 | path: "/", 31 | component: AccountPage, 32 | alias: "/account", 33 | }, 34 | ]; 35 | 36 | export const AUTH_ROUTES = ["auth", "mailSent"]; 37 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/company_print_options/company_print_options.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2024-05-24 10:38:39.591721", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "print_label", 10 | "print_value" 11 | ], 12 | "fields": [ 13 | { 14 | "allow_in_quick_entry": 1, 15 | "columns": 2, 16 | "fieldname": "print_label", 17 | "fieldtype": "Autocomplete", 18 | "in_list_view": 1, 19 | "label": "Print Label", 20 | "search_index": 1 21 | }, 22 | { 23 | "columns": 2, 24 | "fieldname": "print_value", 25 | "fieldtype": "Data", 26 | "in_list_view": 1, 27 | "label": "Print Value" 28 | } 29 | ], 30 | "index_web_pages_for_search": 1, 31 | "istable": 1, 32 | "links": [], 33 | "modified": "2024-06-26 16:55:21.621320", 34 | "modified_by": "Administrator", 35 | "module": "GST India", 36 | "name": "Company Print Options", 37 | "owner": "Administrator", 38 | "permissions": [], 39 | "sort_field": "creation", 40 | "sort_order": "DESC", 41 | "states": [] 42 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/test_party.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.tests import IntegrationTestCase 3 | 4 | 5 | class TestUtils(IntegrationTestCase): 6 | def test_validate_new_party(self): 7 | party = frappe.new_doc( 8 | "Customer", customer_name="Resilient Tech", gstin="24AUTPV8831F1ZZ" 9 | ) 10 | party.save() 11 | 12 | self.assertEqual(party.gst_category, "Registered Regular") 13 | 14 | def test_validate_deemed_export_party(self): 15 | party = frappe.new_doc( 16 | "Customer", 17 | customer_name="Resilient Tech", 18 | gstin="24AUTPV8831F1ZZ", 19 | gst_category="Deemed Export", 20 | ) 21 | party.save() 22 | 23 | self.assertEqual(party.gst_category, "Deemed Export") 24 | 25 | def test_validate_new_party_with_tcs(self): 26 | # Allow TCS GSTIN 27 | party = frappe.new_doc( 28 | "Customer", 29 | customer_name="Flipkart India Private Limited", 30 | gstin="29AABCF8078M1C8", 31 | ) 32 | 33 | party.insert() 34 | -------------------------------------------------------------------------------- /india_compliance/exceptions.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | class GSPServerError(frappe.ValidationError): 5 | message = "GSP/GST server is down" 6 | title = "GSP/GST Server Error" 7 | 8 | 9 | class GSPLimitExceededError(GSPServerError): 10 | message = "GSP/GST account limit exceeded" 11 | http_status_code = 429 12 | 13 | 14 | class GatewayTimeoutError(GSPServerError): 15 | message = "The server took too long to respond" 16 | http_status_code = 504 17 | 18 | 19 | class OTPRequestedError(Exception): 20 | def __init__(self, message="OTP has been requested", *args, **kwargs): 21 | self.response = kwargs.pop("response", None) 22 | super().__init__(message, *args, **kwargs) 23 | 24 | 25 | class InvalidOTPError(Exception): 26 | def __init__(self, message="Invalid OTP", *args, **kwargs): 27 | self.response = kwargs.pop("response", None) 28 | super().__init__(message, *args, **kwargs) 29 | 30 | 31 | class InvalidAuthTokenError(Exception): 32 | def __init__(self, message="Invalid Auth Token", *args, **kwargs): 33 | super().__init__(message, *args, **kwargs) 34 | -------------------------------------------------------------------------------- /india_compliance/vat_india/doctype/c_form/c_form.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors 2 | // License: GNU General Public License v3. See license.txt 3 | 4 | //c-form js file 5 | // ----------------------------- 6 | 7 | frappe.ui.form.on('C-Form', { 8 | setup(frm) { 9 | frm.fields_dict.invoices.grid.get_field("invoice_no").get_query = function(doc) { 10 | return { 11 | filters: { 12 | "docstatus": 1, 13 | "customer": doc.customer, 14 | "company": doc.company, 15 | "c_form_applicable": 'Yes', 16 | "c_form_no": '' 17 | } 18 | }; 19 | } 20 | 21 | frm.fields_dict.state.get_query = function() { 22 | return { 23 | filters: { 24 | country: "India" 25 | } 26 | }; 27 | } 28 | } 29 | }); 30 | 31 | frappe.ui.form.on('C-Form Invoice Detail', { 32 | invoice_no(frm, cdt, cdn) { 33 | let d = frappe.get_doc(cdt, cdn); 34 | 35 | if (d.invoice_no) { 36 | frm.call('get_invoice_details', { 37 | invoice_no: d.invoice_no 38 | }).then(r => { 39 | frappe.model.set_value(cdt, cdn, r.message); 40 | }); 41 | } 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/accounts_settings.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | 4 | from india_compliance.audit_trail.setup import create_property_setters_for_versioning 5 | 6 | 7 | def validate(doc, method=None): 8 | validate_change_in_enable_audit_trail(doc) 9 | validate_delete_linked_ledger_entries(doc) 10 | 11 | 12 | def validate_change_in_enable_audit_trail(doc): 13 | if not doc.has_value_changed("enable_audit_trail"): 14 | return 15 | 16 | if not doc.enable_audit_trail: 17 | frappe.throw(_("Audit Trail cannot be disabled once enabled")) 18 | 19 | # Enable audit trail 20 | doc.delete_linked_ledger_entries = 0 21 | frappe.enqueue(create_property_setters_for_versioning, queue="short", at_front=True) 22 | 23 | 24 | def validate_delete_linked_ledger_entries(doc): 25 | if doc.enable_audit_trail and doc.delete_linked_ledger_entries: 26 | frappe.throw( 27 | _("{0} cannot be enabled to ensure Audit Trail integrity").format( 28 | frappe.bold(doc.meta.get_label("delete_linked_ledger_entries")) 29 | ) 30 | ) 31 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/e_invoice_applicable_company/e_invoice_applicable_company.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2023-05-09 09:18:21.328069", 5 | "default_view": "List", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": [ 10 | "company", 11 | "applicable_from" 12 | ], 13 | "fields": [ 14 | { 15 | "fieldname": "company", 16 | "fieldtype": "Link", 17 | "in_list_view": 1, 18 | "label": "Company", 19 | "options": "Company", 20 | "reqd": 1 21 | }, 22 | { 23 | "default": "Today", 24 | "fieldname": "applicable_from", 25 | "fieldtype": "Date", 26 | "in_list_view": 1, 27 | "label": "Applicable From", 28 | "reqd": 1 29 | } 30 | ], 31 | "index_web_pages_for_search": 1, 32 | "istable": 1, 33 | "links": [], 34 | "modified": "2024-03-29 11:36:18.788811", 35 | "modified_by": "Administrator", 36 | "module": "GST India", 37 | "name": "e-Invoice Applicable Company", 38 | "owner": "Administrator", 39 | "permissions": [], 40 | "sort_field": "creation", 41 | "sort_order": "DESC", 42 | "states": [] 43 | } -------------------------------------------------------------------------------- /.github/workflows/on_release.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Generate Semantic Release 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - version-14 8 | - version-15 9 | jobs: 10 | release: 11 | name: Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout Entire Repository 15 | uses: actions/checkout@v6 16 | with: 17 | fetch-depth: 0 18 | persist-credentials: false 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v6 22 | with: 23 | node-version: 20 24 | 25 | - name: Setup dependencies 26 | run: | 27 | npm install @semantic-release/git @semantic-release/exec --no-save 28 | - name: Create Release 29 | env: 30 | GH_TOKEN: ${{ secrets.BOT_TOKEN }} 31 | GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} 32 | GIT_AUTHOR_NAME: "India Compliance Bot" 33 | GIT_AUTHOR_EMAIL: "bot@indiacompliance.app" 34 | GIT_COMMITTER_NAME: "India Compliance Bot" 35 | GIT_COMMITTER_EMAIL: "bot@indiacompliance.app" 36 | run: npx semantic-release 37 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/constants/custom_fields.py: -------------------------------------------------------------------------------- 1 | CUSTOM_FIELDS = { 2 | "Accounts Settings": [ 3 | { 4 | "fieldname": "audit_trail_section", 5 | "fieldtype": "Section Break", 6 | "label": "Audit Trail", 7 | "insert_after": "invoice_and_billing_tab", 8 | "collapsible": 1, 9 | "collapsible_depends_on": "eval: !doc.enable_audit_trail", 10 | }, 11 | { 12 | "fieldname": "enable_audit_trail", 13 | "fieldtype": "Check", 14 | "label": "Enable Audit Trail", 15 | "description": ( 16 | "In accordance with MCA Notification dated 24-03-2021, enabling this" 19 | " feature will ensure that each change made to the books of account" 20 | " gets recorded. Once enabled, this feature cannot be disabled." 21 | ), 22 | "insert_after": "audit_trail_section", 23 | }, 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/migrate_fields_for_gstr3b.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | if not frappe.db.has_column("GSTR 3B Report", "month"): 6 | return 7 | 8 | # Update month_or_quarter from month field 9 | gstr3b_report = frappe.qb.DocType("GSTR 3B Report") 10 | 11 | ( 12 | frappe.qb.update(gstr3b_report) 13 | .set(gstr3b_report.month_or_quarter, gstr3b_report.month) 14 | .run() 15 | ) 16 | 17 | # Update company_gstin based on company_address 18 | addresses = frappe.get_all("GSTR 3B Report", pluck="company_address", distinct=True) 19 | 20 | for address in addresses: 21 | frappe.db.set_value( 22 | "GSTR 3B Report", 23 | {"company_address": address}, 24 | "company_gstin", 25 | get_gstin_based_on_address(address), 26 | ) 27 | 28 | 29 | def get_gstin_based_on_address(address): 30 | """Get GSTIN based on company_address or company""" 31 | return ( 32 | frappe.db.get_value( 33 | "Address", 34 | address, 35 | "gstin", 36 | ) 37 | or "" 38 | ) 39 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/journal_entry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | 4 | from india_compliance.gst_india.overrides.transaction import ( 5 | is_indian_registered_company, 6 | ) 7 | from india_compliance.gst_india.utils import get_gst_account_gst_tax_type_map 8 | 9 | 10 | def set_gst_tax_type(doc, method=None): 11 | if not doc.accounts: 12 | return 13 | 14 | gst_tax_account_map = get_gst_account_gst_tax_type_map() 15 | if not gst_tax_account_map: 16 | return 17 | 18 | for tax in doc.accounts: 19 | # Setting as None if not GST Account 20 | tax.gst_tax_type = gst_tax_account_map.get(tax.account) 21 | 22 | 23 | def validate(doc, method=None): 24 | if doc.company_gstin or not is_indian_registered_company(doc): 25 | return 26 | 27 | set_gst_tax_type(doc) 28 | 29 | # validate company_gstin 30 | contains_gst_account = False 31 | for row in doc.accounts: 32 | if row.gst_tax_type: 33 | contains_gst_account = True 34 | break 35 | 36 | if contains_gst_account: 37 | frappe.throw(_("Company GSTIN is mandatory if any GST account is present.")) 38 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_reverse_charge_and_export_type.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.utils.custom_fields import delete_old_fields 4 | 5 | DOCTYPES = ("Purchase Invoice", "Sales Invoice") 6 | 7 | 8 | def get_doctype_columns(doctypes): 9 | return {doctype: frappe.db.get_table_columns(doctype) for doctype in doctypes} 10 | 11 | 12 | def execute(): 13 | update_field_to_check("reverse_charge", "is_reverse_charge", "Y") 14 | update_field_to_check("export_type", "is_export_with_gst", "With Payment of Tax") 15 | 16 | 17 | def update_field_to_check(old_fieldname, new_fieldname, truthy_value): 18 | for doctype, columns in get_doctype_columns(DOCTYPES).items(): 19 | # Check for new fieldname, is_export_with_gst is only applicable for Sales Invoice 20 | if old_fieldname not in columns or new_fieldname not in columns: 21 | continue 22 | 23 | frappe.db.set_value(doctype, {old_fieldname: truthy_value}, new_fieldname, 1) 24 | frappe.db.sql_ddl( 25 | "alter table `tab{0}` drop column {1}".format(doctype, old_fieldname) 26 | ) 27 | 28 | delete_old_fields(old_fieldname, DOCTYPES) 29 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/auth/MarketingInfoCheckIcon.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/purchase_reconciliation_tool/gstr_download_history.html: -------------------------------------------------------------------------------- 1 |
2 | {% if typeof(data) == "string" %} 3 |
{{ data }}
4 | {% else %} 5 | 6 | 7 | 8 | {% for(let i=0; i{{ columns[i] }} 10 | {% } %} 11 | 12 | 13 | 14 | {% for(let [period, list] of data) { %} 15 | 16 | 17 | 18 | 19 | 20 | {% for(let i=1;i < list.length;i++){ %} 21 | 22 | 23 | 24 | {% } %} 25 | 26 | {% } %} 27 | 28 | 29 |
{{period}}{{ list[0] }}
{{ list[i] }}
30 | {% endif %} 31 |
-------------------------------------------------------------------------------- /india_compliance/public/js/regex_constants.js: -------------------------------------------------------------------------------- 1 | // Copied from india_compliance/gst_india/constants 2 | 3 | const NORMAL = "^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[Z1-9ABD-J]{1}[0-9A-Z]{1}$"; 4 | const GOVT_DEPTID = "^[0-9]{2}[A-Z]{4}[0-9]{5}[A-Z]{1}[0-9]{1}[Z]{1}[0-9]{1}$"; 5 | const NRI_ID = "^[0-9]{4}[A-Z]{3}[0-9]{5}[N][R][0-9A-Z]{1}$"; 6 | const OIDAR = "^[9][9][0-9]{2}[A-Z]{3}[0-9]{5}[O][S][0-9A-Z]{1}$"; 7 | const UNBODY = "^[0-9]{4}[A-Z]{3}[0-9]{5}[UO]{1}[N][A-Z0-9]{1}$"; 8 | const TDS = "^[0-9]{2}[A-Z]{4}[A-Z0-9]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[D][0-9A-Z]$"; 9 | const TCS = "^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[C]{1}[0-9A-Z]{1}$"; 10 | 11 | export const REGISTERED_REGEX = new RegExp([NORMAL, GOVT_DEPTID].join("|")); 12 | export const OVERSEAS_REGEX = new RegExp([NRI_ID, OIDAR].join("|")); 13 | export const UNBODY_REGEX = new RegExp(UNBODY); 14 | export const TDS_REGEX = new RegExp(TDS); 15 | export const TCS_REGEX = new RegExp(TCS); 16 | 17 | export const GSTIN_REGEX = new RegExp( 18 | [NORMAL, GOVT_DEPTID, NRI_ID, OIDAR, UNBODY, TDS, TCS].join("|") 19 | ); 20 | 21 | export const GST_INVOICE_NUMBER_FORMAT = new RegExp("^[^\\W_][A-Za-z\\d\\-/]{0,15}$"); 22 | export const PAN_REGEX = new RegExp("^[A-Z]{5}[0-9]{4}[A-Z]{1}$"); -------------------------------------------------------------------------------- /india_compliance/patches/post_install/manual_patch_to_update_gst_details.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.patches.post_install.improve_item_tax_template import ( 4 | TRANSACTION_ITEM_DOCTYPES, 5 | get_indian_companies, 6 | update_gst_details_for_transactions, 7 | update_gst_treatment_for_transactions, 8 | ) 9 | from india_compliance.patches.post_install.set_gst_tax_type import ( 10 | execute as set_gst_tax_type, 11 | ) 12 | from india_compliance.patches.post_install.set_gst_tax_type_in_journal_entry import ( 13 | execute as set_gst_tax_type_in_journal_entry, 14 | ) 15 | 16 | 17 | def execute(): 18 | reset_gst_treatment() 19 | set_gst_tax_type() 20 | set_gst_tax_type_in_journal_entry() 21 | update_gst_treatment_for_transactions() 22 | 23 | companies = get_indian_companies() 24 | update_gst_details_for_transactions(companies) 25 | 26 | 27 | def reset_gst_treatment(): 28 | for item_doctype in TRANSACTION_ITEM_DOCTYPES: 29 | # GST Treatment is not required in Material Request Item 30 | if item_doctype == "Material Request Item": 31 | continue 32 | 33 | table = frappe.qb.DocType(item_doctype) 34 | frappe.qb.update(table).set(table.gst_treatment, "").run() 35 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/remove_old_fields.py: -------------------------------------------------------------------------------- 1 | from india_compliance.utils.custom_fields import delete_old_fields 2 | 3 | 4 | def execute(): 5 | delete_old_fields( 6 | "customer_gstin", 7 | ( 8 | "Quotation", 9 | "Sales Order", 10 | "Delivery Note", 11 | "Sales Invoice", 12 | "POS Invoice", 13 | ), 14 | ) 15 | 16 | delete_old_fields( 17 | ("reason_for_issuing_document", "ecommerce_gstin"), "Purchase Invoice" 18 | ) 19 | delete_old_fields("pan_details", "Company") 20 | delete_old_fields("export_type", ("Customer", "Supplier")) 21 | delete_old_fields("company_address", "Journal Entry") 22 | delete_old_fields("reason_for_issuing_document", "Sales Invoice") 23 | 24 | # Field renamed post release 25 | delete_old_fields( 26 | ("custom_gst_breakup", "custom_gst_breakup_table"), 27 | ( 28 | "Supplier Quotation", 29 | "Purchase Order", 30 | "Purchase Receipt", 31 | "Purchase Invoice", 32 | "Quotation", 33 | "Sales Order", 34 | "Delivery Note", 35 | "Sales Invoice", 36 | "POS Invoice", 37 | ), 38 | ) 39 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/purchase_receipt.js: -------------------------------------------------------------------------------- 1 | const DOCTYPE = "Purchase Receipt"; 2 | setup_e_waybill_actions(DOCTYPE); 3 | 4 | frappe.ui.form.on(DOCTYPE, { 5 | setup(frm) { 6 | frm.set_query("transporter", { 7 | filters: { 8 | is_transporter: 1, 9 | }, 10 | }); 11 | 12 | frm.set_query("driver", doc => { 13 | return { 14 | filters: { 15 | transporter: doc.transporter, 16 | }, 17 | }; 18 | }); 19 | }, 20 | 21 | refresh(frm) { 22 | if (gst_settings.enable_e_waybill && gst_settings.enable_e_waybill_from_pr) 23 | show_sandbox_mode_indicator(); 24 | }, 25 | 26 | after_save(frm) { 27 | if ( 28 | frm.doc.supplier_address || 29 | !(frm.doc.gst_category == "Unregistered" || frm.doc.is_return) || 30 | !is_e_waybill_applicable(frm) || 31 | !has_e_waybill_threshold_met(frm) 32 | ) 33 | return; 34 | 35 | frappe.show_alert( 36 | { 37 | message: __("Supplier Address is required to create e-Waybill"), 38 | indicator: "yellow", 39 | }, 40 | 10 41 | ); 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/test_sales_invoice.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.tests import IntegrationTestCase 3 | 4 | from india_compliance.gst_india.utils import validate_invoice_number 5 | 6 | 7 | class TestSalesInvoice(IntegrationTestCase): 8 | def test_validate_invoice_number(self): 9 | posting_date = "2021-05-01" 10 | 11 | invalid_names = [ 12 | "SI$1231", 13 | "012345678901234567", 14 | "SI 2020 05", 15 | "SI.2020.0001", 16 | "PI2021 - 001", 17 | ] 18 | for name in invalid_names: 19 | doc = frappe._dict( 20 | name=name, posting_date=posting_date, doctype="Sales Invoice" 21 | ) 22 | self.assertRaises(frappe.ValidationError, validate_invoice_number, doc) 23 | 24 | valid_names = [ 25 | "012345678901236", 26 | "SI/2020/0001", 27 | "SI/2020-0001", 28 | "2020-PI-0001", 29 | "PI2020-0001", 30 | ] 31 | for name in valid_names: 32 | doc = frappe._dict(name=name, posting_date=posting_date) 33 | try: 34 | validate_invoice_number(doc) 35 | except frappe.ValidationError: 36 | self.fail("Valid name {} throwing error".format(name)) 37 | -------------------------------------------------------------------------------- /india_compliance/test_patches.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.modules.patch_handler import get_patches_from_app 3 | from frappe.tests import IntegrationTestCase 4 | 5 | from india_compliance.install import POST_INSTALL_PATCHES 6 | 7 | 8 | class TestPatches(IntegrationTestCase): 9 | def test_post_install_patch_exists(self): 10 | for patch in POST_INSTALL_PATCHES: 11 | self.assertTrue( 12 | frappe.get_attr( 13 | f"india_compliance.patches.post_install.{patch}.execute" 14 | ) 15 | ) 16 | 17 | def test_patches_exists(self): 18 | patches = get_patches_from_app("india_compliance") 19 | 20 | for patch in patches: 21 | if patch.startswith("execute:"): 22 | import_path = patch.split("execute:")[1] 23 | 24 | if not import_path.startswith("from"): 25 | continue 26 | 27 | components = import_path.split("from")[1].split() 28 | module = components[0] 29 | function_name = components[2].replace(";", "").replace(",", "") 30 | patch_path = module + "." + function_name 31 | else: 32 | patch_path = f"{patch.split(maxsplit=1)[0]}.execute" 33 | 34 | frappe.get_attr(patch_path) 35 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/Loading.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | -------------------------------------------------------------------------------- /india_compliance/patches/v15/migrate_boe_taxes_to_ic_taxes.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.model.document import bulk_insert 3 | from frappe.model.naming import _generate_random_string 4 | 5 | 6 | def execute(): 7 | if not frappe.db.table_exists("Bill of Entry Taxes"): 8 | return 9 | 10 | boe_taxes = frappe.qb.DocType("Bill of Entry Taxes") 11 | boe_taxes_docs = frappe.qb.from_(boe_taxes).select("*").run(as_dict=True) 12 | 13 | ic_taxes_names = set( 14 | frappe.get_all("India Compliance Taxes and Charges", pluck="name") 15 | ) 16 | ic_taxes = [] 17 | 18 | for doc in boe_taxes_docs: 19 | ic_taxes_doc = frappe.get_doc( 20 | { 21 | **doc, 22 | "doctype": "India Compliance Taxes and Charges", 23 | "name": set_name(doc.name, ic_taxes_names), 24 | "base_total": doc.total, 25 | } 26 | ) 27 | 28 | ic_taxes.append(ic_taxes_doc) 29 | 30 | bulk_insert("India Compliance Taxes and Charges", ic_taxes) 31 | 32 | # Drop the old table 33 | frappe.db.delete("Bill of Entry Taxes") 34 | 35 | 36 | def set_name(name, names): 37 | new_name = name 38 | while new_name in names: 39 | new_name = _generate_random_string(10) 40 | 41 | names.add(new_name) 42 | return new_name 43 | -------------------------------------------------------------------------------- /india_compliance/uninstall.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from india_compliance.gst_india.constants import BUG_REPORT_URL 4 | from india_compliance.gst_india.uninstall import before_uninstall as remove_gst 5 | from india_compliance.gst_india.uninstall import ( 6 | delete_education_custom_fields, 7 | delete_healthcare_custom_fields, 8 | delete_hrms_custom_fields, 9 | ) 10 | from india_compliance.income_tax_india.uninstall import ( 11 | before_uninstall as remove_income_tax, 12 | ) 13 | 14 | 15 | def before_uninstall(): 16 | try: 17 | print("Removing Income Tax customizations...") 18 | remove_income_tax() 19 | 20 | print("Removing GST customizations...") 21 | remove_gst() 22 | 23 | except Exception as e: 24 | click.secho( 25 | ( 26 | "Removing customizations for India Compliance failed due to an error." 27 | " Please try again or" 28 | f" report the issue on {BUG_REPORT_URL} if not resolved." 29 | ), 30 | fg="bright_red", 31 | ) 32 | raise e 33 | 34 | 35 | def before_app_uninstall(app_name): 36 | if app_name == "hrms": 37 | delete_hrms_custom_fields() 38 | 39 | if app_name == "education": 40 | delete_education_custom_fields() 41 | 42 | if app_name == "healthcare": 43 | delete_healthcare_custom_fields() 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 22 | 23 | > Please provide enough information so that others can review your pull request: 24 | 25 | 26 | 27 | > Explain the **details** for making this change. What existing problem does the pull request solve? 28 | 29 | 30 | 31 | > Screenshots/GIFs 32 | 33 | 34 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/unreconcile_payment.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from erpnext.accounts.general_ledger import make_reverse_gl_entries 3 | 4 | 5 | def before_submit(doc, method=None): 6 | if doc.voucher_type != "Payment Entry": 7 | return 8 | 9 | for allocation in doc.allocations: 10 | voucher_detail_nos = frappe.get_all( 11 | "Payment Entry Reference", 12 | { 13 | "parent": doc.voucher_no, 14 | "reference_doctype": allocation.reference_doctype, 15 | "reference_name": allocation.reference_name, 16 | "docstatus": 1, 17 | }, 18 | pluck="name", 19 | ) 20 | 21 | for voucher_detail_no in voucher_detail_nos: 22 | reverse_gst_adjusted_against_payment_entry( 23 | voucher_detail_no, doc.voucher_no 24 | ) 25 | 26 | 27 | def reverse_gst_adjusted_against_payment_entry(voucher_detail_no, payment_name): 28 | filters = { 29 | "voucher_type": "Payment Entry", 30 | "voucher_no": payment_name, 31 | "voucher_detail_no": voucher_detail_no, 32 | } 33 | 34 | gl_entries = frappe.get_all("GL Entry", filters=filters, fields="*") 35 | if not gl_entries: 36 | return 37 | 38 | frappe.db.set_value("GL Entry", filters, "is_cancelled", 1) 39 | make_reverse_gl_entries(gl_entries, partial_cancel=True) 40 | -------------------------------------------------------------------------------- /india_compliance/gst_india/report/india_compliance_api_usage/india_compliance_api_usage.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Resilient Tech and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.query_reports["India Compliance API Usage"] = { 5 | filters: [ 6 | { 7 | "fieldname": "from_date", 8 | "label": __("From"), 9 | "fieldtype": "Date", 10 | "reqd": 1, 11 | "default": frappe.datetime.add_months(frappe.datetime.now_date(), -6) 12 | }, 13 | { 14 | "fieldname": "to_date", 15 | "label": __("To"), 16 | "fieldtype": "Date", 17 | "reqd": 1, 18 | "default": frappe.datetime.now_date() 19 | }, 20 | { 21 | "fieldname": "report_by", 22 | "label": __("Report by"), 23 | "fieldtype": "Select", 24 | "options": [ 25 | "Endpoint", 26 | "Linked Document", 27 | "Date" 28 | ], 29 | "default": "Endpoint" 30 | }, 31 | ], 32 | 33 | onload(query_report) { 34 | query_report.filters.forEach(filter => { 35 | if (filter.fieldtype === "Date") { 36 | filter.datepicker.update( 37 | { maxDate: new Date() } 38 | ) 39 | } 40 | }); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/customize_form.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | from frappe.custom.doctype.customize_form.customize_form import ( 4 | CustomizeForm as _CustomizeForm, 5 | ) 6 | 7 | from india_compliance.audit_trail.utils import ( 8 | get_audit_trail_doctypes, 9 | is_audit_trail_enabled, 10 | ) 11 | 12 | 13 | class CustomizeForm(_CustomizeForm): 14 | @frappe.whitelist() 15 | def fetch_to_customize(self): 16 | self.set_onload( 17 | "audit_trail_enabled", 18 | self.doc_type 19 | and is_audit_trail_enabled() 20 | and self.doc_type in get_audit_trail_doctypes(), 21 | ) 22 | 23 | return super().fetch_to_customize() 24 | 25 | @frappe.whitelist() 26 | def save_customization(self): 27 | self.validate_audit_trail_integrity() 28 | return super().save_customization() 29 | 30 | def validate_audit_trail_integrity(self): 31 | if ( 32 | not self.doc_type 33 | or self.track_changes 34 | or not is_audit_trail_enabled() 35 | or self.doc_type not in get_audit_trail_doctypes() 36 | ): 37 | return 38 | 39 | frappe.throw( 40 | _( 41 | "Cannot disable Track Changes for {0}, since it has been enabled to" 42 | " maintain Audit Trail" 43 | ).format(_(self.doc_type)) 44 | ) 45 | -------------------------------------------------------------------------------- /india_compliance/public/js/new_gst_category_notification.js: -------------------------------------------------------------------------------- 1 | $(document).on("app_ready", async function () { 2 | if (!frappe.boot.needs_new_gst_category_notification) return; 3 | 4 | // let other processes finish 5 | await new Promise(resolve => setTimeout(resolve, 700)); 6 | const d = frappe.msgprint({ 7 | title: __("New GST Category Introduced"), 8 | indicator: "orange", 9 | message: __( 10 | `Dear India Compliance User, 11 |

12 | 13 | We would like to inform you about an important update regarding the GST category for Input Service Distributors (ISD). 14 |

15 | 16 | Previously, ISD was categorized under Registered Regular in our system. However, we have now introduced a dedicated GST category Input Service Distributor specifically for Input Service Distributors. 17 |

18 | 19 | Action Required: 20 |
21 | If you have been using the ISD under the Registered Regular GST category, please update your records to reflect the new Input Service Distributor category. 22 | ` 23 | ), 24 | }); 25 | 26 | d.onhide = () => { 27 | frappe.xcall( 28 | "india_compliance.gst_india.utils.disable_new_gst_category_notification" 29 | ); 30 | }; 31 | }); 32 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_reconciliation_status.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import IfNull 3 | 4 | 5 | def execute(): 6 | PI = frappe.qb.DocType("Purchase Invoice") 7 | PI_ITEM = frappe.qb.DocType("Purchase Invoice Item") 8 | BOE = frappe.qb.DocType("Bill of Entry") 9 | 10 | ( 11 | frappe.qb.update(PI) 12 | .set(PI.reconciliation_status, "Not Applicable") 13 | .join(PI_ITEM) 14 | .on(PI.name == PI_ITEM.parent) 15 | .where(PI.docstatus == 1) 16 | .where( 17 | (IfNull(PI.supplier_gstin, "") == "") 18 | | ( 19 | IfNull(PI.gst_category, "").isin( 20 | ["Registered Composition", "Unregistered", "Overseas"] 21 | ) 22 | ) 23 | | (IfNull(PI.supplier_gstin, "") == PI.company_gstin) 24 | | (IfNull(PI.is_opening, "") == "Yes") 25 | | (PI_ITEM.gst_treatment == "Non-GST") 26 | ) 27 | .run() 28 | ) 29 | 30 | ( 31 | frappe.qb.update(PI) 32 | .set(PI.reconciliation_status, "Unreconciled") 33 | .where(PI.docstatus == 1) 34 | .where(IfNull(PI.reconciliation_status, "") == "") 35 | .run() 36 | ) 37 | 38 | ( 39 | frappe.qb.update(BOE) 40 | .set(BOE.reconciliation_status, "Unreconciled") 41 | .where(BOE.docstatus == 1) 42 | .run() 43 | ) 44 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/invoice_history_table.html: -------------------------------------------------------------------------------- 1 | {% if data_array %} 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for data in data_array %} 15 | 16 | 17 | 18 | 19 | 20 | 28 | 29 | {% endfor %} 30 | 31 |
NamePosting DateCreditsGrand Total
{{ data.name }}{{ data.posting_date }}{{ data.credits }}{{ data.grand_total }} 21 | 27 |
32 |
33 | {% else %} 34 |

35 | No invoices found! 36 |

37 | {% endif %} 38 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/merge_utgst_account_into_sgst_account.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | import frappe 4 | 5 | DOCTYPE = "GST Account" 6 | 7 | 8 | def execute(): 9 | if not frappe.db.has_column(DOCTYPE, "utgst_account"): 10 | return 11 | 12 | gst_accounts = frappe.get_all( 13 | DOCTYPE, 14 | filters={"parent": "GST Settings", "utgst_account": ("is", "set")}, 15 | fields=( 16 | "name", 17 | "sgst_account", 18 | "utgst_account", 19 | ), 20 | ) 21 | 22 | if not gst_accounts: 23 | return 24 | 25 | values_to_set = {"utgst_account": ""} 26 | 27 | for row in gst_accounts: 28 | if not row.sgst_account: 29 | # SGST account was set as not mandatory by user? 30 | frappe.db.set_value( 31 | DOCTYPE, 32 | row.name, 33 | {"sgst_account": row.utgst_account, **values_to_set}, 34 | ) 35 | return 36 | 37 | frappe.rename_doc( 38 | "Account", 39 | row.utgst_account, 40 | row.sgst_account, 41 | merge=1, 42 | force=1, 43 | ) 44 | 45 | frappe.db.set_value(DOCTYPE, row.name, values_to_set) 46 | 47 | click.secho( 48 | "The UTGST Accounts set in your GST Settings have been merged into the" 49 | " corresponding SGST / UTGST Accounts.\n", 50 | fg="yellow", 51 | ) 52 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/item.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.gst_india.doctype.gst_hsn_code.gst_hsn_code import ( 4 | validate_hsn_code as _validate_hsn_code, 5 | ) 6 | 7 | 8 | def validate(doc, method=None): 9 | update_hsn_code(doc) 10 | validate_hsn_code(doc) 11 | set_taxes_from_hsn_code(doc) 12 | 13 | 14 | def update_hsn_code(doc): 15 | """ 16 | Update HSN Code from Fee Category (education), Healthcare Services (healthcare) 17 | """ 18 | if not frappe.flags.cross_app_hsn_code: 19 | return 20 | 21 | doc.gst_hsn_code = frappe.flags.cross_app_hsn_code 22 | del frappe.flags.cross_app_hsn_code 23 | 24 | 25 | def validate_hsn_code(doc): 26 | # HSN Code is being validated only for sales items 27 | if not doc.is_sales_item: 28 | return 29 | 30 | _validate_hsn_code(doc.gst_hsn_code) 31 | 32 | 33 | def set_taxes_from_hsn_code(doc): 34 | if doc.taxes or not doc.gst_hsn_code: 35 | return 36 | 37 | hsn_doc = frappe.get_doc("GST HSN Code", doc.gst_hsn_code) 38 | 39 | for tax in hsn_doc.taxes: 40 | doc.append( 41 | "taxes", 42 | { 43 | "item_tax_template": tax.item_tax_template, 44 | "tax_category": tax.tax_category, 45 | "valid_from": tax.valid_from, 46 | "minimum_net_rate": tax.minimum_net_rate, 47 | "maximum_net_rate": tax.maximum_net_rate, 48 | }, 49 | ) 50 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/setup.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.audit_trail.constants.custom_fields import CUSTOM_FIELDS 4 | from india_compliance.audit_trail.utils import ( 5 | get_audit_trail_doctypes, 6 | is_audit_trail_enabled, 7 | ) 8 | from india_compliance.utils.custom_fields import get_custom_fields_creator 9 | 10 | _create_custom_fields = get_custom_fields_creator("Audit Trail") 11 | 12 | 13 | # Hooks 14 | 15 | 16 | def setup_fixtures(): 17 | create_custom_fields() 18 | create_property_setters_for_versioning() 19 | 20 | 21 | def create_custom_fields(): 22 | _create_custom_fields(CUSTOM_FIELDS) 23 | 24 | 25 | def create_property_setters_for_versioning(): 26 | for doctype in get_audit_trail_doctypes(): 27 | property_setter_data = { 28 | "doctype_or_field": "DocType", 29 | "doc_type": doctype, 30 | "property": "track_changes", 31 | "value": "1", 32 | "property_type": "Check", 33 | "is_system_generated": 1, 34 | } 35 | 36 | if frappe.db.exists("Property Setter", property_setter_data): 37 | continue 38 | 39 | property_setter = frappe.new_doc("Property Setter") 40 | property_setter.update(property_setter_data) 41 | property_setter.flags.ignore_permissions = True 42 | property_setter.insert() 43 | 44 | 45 | def after_migrate(): 46 | if is_audit_trail_enabled(): 47 | create_property_setters_for_versioning() 48 | -------------------------------------------------------------------------------- /india_compliance/public/js/quick_info_popover.js: -------------------------------------------------------------------------------- 1 | frappe.provide("india_compliance"); 2 | 3 | india_compliance.quick_info_popover = class QuickInfoPopover { 4 | constructor(frm, field_dict) { 5 | /** 6 | * Setup tooltip for fields to show details 7 | * @param {Object} frm Form object 8 | * @param {Object} field_dict Dictionary of fields with info to show 9 | */ 10 | this.frm = frm; 11 | this.field_dict = field_dict; 12 | this.make(); 13 | } 14 | make() { 15 | this.create_info_popover(); 16 | } 17 | create_info_popover() { 18 | if (!this.field_dict) return; 19 | 20 | for (const [field, info] of Object.entries(this.field_dict)) { 21 | this.create_info_icon(field); 22 | 23 | if (!this.info_btn) return; 24 | 25 | this.info_btn.popover({ 26 | trigger: "hover", 27 | placement: "top", 28 | content: () => this.get_content_html(field, info), 29 | html: true, 30 | }); 31 | } 32 | } 33 | create_info_icon(field) { 34 | let field_area = this.frm.get_field(field).$wrapper.find(".clearfix"); 35 | this.info_btn = $(``).appendTo(field_area); 36 | } 37 | get_content_html(field, info) { 38 | let field_lable = frappe.meta.get_label(this.frm.doctype, field); 39 | 40 | return ` 41 |
42 |
43 |
${__(field_lable)}
44 |
45 |
${info}
46 |
47 | `; 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/set_gst_tax_type.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder import Case 3 | 4 | from india_compliance.gst_india.utils import get_gst_account_gst_tax_type_map 5 | 6 | TAX_DOCTYPES = [ 7 | "Sales Taxes and Charges", 8 | "Purchase Taxes and Charges", 9 | "Advance Taxes and Charges", 10 | ] 11 | 12 | 13 | def execute(): 14 | gst_account_tax_type_map = get_gst_account_gst_tax_type_map() 15 | 16 | if not gst_account_tax_type_map: 17 | return 18 | 19 | gst_accounts_by_tax_type = {} 20 | for account, tax_type in gst_account_tax_type_map.items(): 21 | gst_accounts_by_tax_type.setdefault(tax_type, []).append(account) 22 | 23 | for tax_doctype in TAX_DOCTYPES: 24 | update_documents(tax_doctype, gst_accounts_by_tax_type) 25 | 26 | 27 | def update_documents(taxes_doctype, gst_accounts_by_tax_type): 28 | taxes_doctype = frappe.qb.DocType(taxes_doctype) 29 | 30 | update_query = frappe.qb.update(taxes_doctype).where( 31 | taxes_doctype.parenttype.notin( 32 | ["Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"] 33 | ) 34 | ) 35 | 36 | conditions = Case() 37 | 38 | for gst_tax_account, gst_tax_name in gst_accounts_by_tax_type.items(): 39 | conditions = conditions.when( 40 | taxes_doctype.account_head.isin(gst_tax_name), gst_tax_account 41 | ) 42 | 43 | conditions = conditions.else_(None) 44 | 45 | update_query.set(taxes_doctype.gst_tax_type, conditions).run() 46 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/delivery_note.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.gst_india.overrides.sales_invoice import ( 4 | is_e_waybill_applicable, 5 | is_shipping_address_in_india, 6 | update_dashboard_with_gst_logs, 7 | validate_port_address, 8 | ) 9 | from india_compliance.gst_india.overrides.transaction import ( 10 | validate_transaction, 11 | ) 12 | from india_compliance.gst_india.utils import is_api_enabled 13 | from india_compliance.gst_india.utils.e_waybill import get_e_waybill_info 14 | 15 | 16 | def onload(doc, method=None): 17 | if not doc.get("ewaybill"): 18 | if doc.gst_category == "Overseas" and is_e_waybill_applicable(doc): 19 | 20 | doc.set_onload( 21 | "shipping_address_in_india", is_shipping_address_in_india(doc) 22 | ) 23 | return 24 | 25 | gst_settings = frappe.get_cached_doc("GST Settings") 26 | 27 | if ( 28 | is_api_enabled(gst_settings) 29 | and gst_settings.enable_e_waybill 30 | and ( 31 | gst_settings.enable_e_waybill_from_dn or gst_settings.auto_cancel_e_waybill 32 | ) 33 | and (e_waybill_info := get_e_waybill_info(doc)) 34 | ): 35 | doc.set_onload("e_waybill_info", e_waybill_info) 36 | 37 | 38 | def validate(doc, method=None): 39 | if validate_transaction(doc) is False: 40 | return 41 | 42 | validate_port_address(doc) 43 | 44 | 45 | def get_dashboard_data(data): 46 | return update_dashboard_with_gst_logs( 47 | "Delivery Note", data, "e-Waybill Log", "Integration Request" 48 | ) 49 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/services/AccountService.js: -------------------------------------------------------------------------------- 1 | export async function get_details(type) { 2 | return india_compliance.gst_api.call(`account.get_${type}_details`, { 3 | method: "GET", 4 | with_api_secret: true, 5 | }); 6 | } 7 | 8 | export async function update_billing_details(new_billing_details) { 9 | return india_compliance.gst_api.call("account.update_billing_details", { 10 | method: "POST", 11 | body: { new_billing_details }, 12 | with_api_secret: true, 13 | }); 14 | } 15 | 16 | export async function create_order(credits, amount) { 17 | return india_compliance.gst_api.call("account.create_order", { 18 | method: "POST", 19 | body: { credits, amount }, 20 | with_api_secret: true, 21 | }); 22 | } 23 | 24 | export async function verify_payment(orderId) { 25 | return india_compliance.gst_api.call("account.verify_payment", { 26 | method: "POST", 27 | body: { order_id: orderId }, 28 | with_api_secret: true, 29 | }); 30 | } 31 | 32 | export async function get_invoice_history(from_date, to_date) { 33 | return india_compliance.gst_api.call("account.get_invoice_history", { 34 | method: "POST", 35 | body: { from_date, to_date }, 36 | with_api_secret: true, 37 | }); 38 | } 39 | 40 | export async function send_invoice_email(invoice_name, email) { 41 | return india_compliance.gst_api.call("account.send_invoice_email", { 42 | method: "POST", 43 | body: { invoice_name, email }, 44 | with_api_secret: true, 45 | }); 46 | } -------------------------------------------------------------------------------- /india_compliance/patches/post_install/set_gst_category.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.utils.custom_fields import delete_old_fields 4 | 5 | 6 | def execute(): 7 | invoice_type_gst_category_map = { 8 | "Regular": "Registered Regular", 9 | "Export": "Overseas", 10 | "SEZ": "SEZ", 11 | "Deemed Export": "Deemed Export", 12 | } 13 | 14 | doctypes = ("Sales Invoice", "Purchase Invoice") 15 | for doctype in doctypes: 16 | if not frappe.db.exists( 17 | "Custom Field", {"dt": doctype, "fieldname": "invoice_type"} 18 | ): 19 | continue 20 | 21 | for invoice_type, gst_category in invoice_type_gst_category_map.items(): 22 | frappe.db.set_value( 23 | doctype, 24 | {"gst_category": ("in", (None, "")), "invoice_type": invoice_type}, 25 | "gst_category", 26 | gst_category, 27 | ) 28 | 29 | delete_old_fields("invoice_type", doctypes) 30 | 31 | if "eligibility_for_itc" not in frappe.db.get_table_columns("Purchase Invoice"): 32 | return 33 | 34 | # update eligibility_for_itc with new options 35 | for old_value, new_value in { 36 | "ineligible": "Ineligible", 37 | "input service": "Input Service Distributor", 38 | "capital goods": "Import Of Goods", 39 | "input": "All Other ITC", 40 | }.items(): 41 | frappe.db.set_value( 42 | "Purchase Invoice", 43 | {"eligibility_for_itc": old_value}, 44 | "eligibility_for_itc", 45 | new_value, 46 | ) 47 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_inward_supply_item/gst_inward_supply_item.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2022-04-12 17:40:41.914298", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "item_number", 9 | "taxable_value", 10 | "rate", 11 | "igst", 12 | "cgst", 13 | "sgst", 14 | "cess" 15 | ], 16 | "fields": [ 17 | { 18 | "fieldname": "item_number", 19 | "fieldtype": "Int", 20 | "label": "Item Number" 21 | }, 22 | { 23 | "fieldname": "taxable_value", 24 | "fieldtype": "Float", 25 | "in_list_view": 1, 26 | "label": "Taxable Value" 27 | }, 28 | { 29 | "fieldname": "rate", 30 | "fieldtype": "Float", 31 | "in_list_view": 1, 32 | "label": "Tax Rate" 33 | }, 34 | { 35 | "fieldname": "igst", 36 | "fieldtype": "Float", 37 | "in_list_view": 1, 38 | "label": "IGST Amount" 39 | }, 40 | { 41 | "fieldname": "cgst", 42 | "fieldtype": "Float", 43 | "in_list_view": 1, 44 | "label": "CGST Amount" 45 | }, 46 | { 47 | "fieldname": "sgst", 48 | "fieldtype": "Float", 49 | "in_list_view": 1, 50 | "label": "SGST Amount" 51 | }, 52 | { 53 | "fieldname": "cess", 54 | "fieldtype": "Float", 55 | "label": "CESS Amount" 56 | } 57 | ], 58 | "index_web_pages_for_search": 1, 59 | "istable": 1, 60 | "links": [], 61 | "modified": "2024-03-29 11:54:42.857545", 62 | "modified_by": "Administrator", 63 | "module": "GST India", 64 | "name": "GST Inward Supply Item", 65 | "owner": "Administrator", 66 | "permissions": [], 67 | "sort_field": "creation", 68 | "sort_order": "DESC", 69 | "states": [] 70 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstr_action/gstr_action.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2024-09-09 17:43:22.979394", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "request_type", 10 | "request_id", 11 | "token", 12 | "status", 13 | "creation_time", 14 | "integration_request" 15 | ], 16 | "fields": [ 17 | { 18 | "fieldname": "request_type", 19 | "fieldtype": "Data", 20 | "in_list_view": 1, 21 | "label": "Request Type" 22 | }, 23 | { 24 | "fieldname": "token", 25 | "fieldtype": "Data", 26 | "hidden": 1, 27 | "label": "Token" 28 | }, 29 | { 30 | "fieldname": "status", 31 | "fieldtype": "Data", 32 | "in_list_view": 1, 33 | "label": "Status" 34 | }, 35 | { 36 | "fieldname": "creation_time", 37 | "fieldtype": "Datetime", 38 | "in_list_view": 1, 39 | "label": "Creation Time" 40 | }, 41 | { 42 | "fieldname": "integration_request", 43 | "fieldtype": "Link", 44 | "in_list_view": 1, 45 | "label": "Integration Request", 46 | "options": "Integration Request", 47 | "read_only": 1 48 | }, 49 | { 50 | "fieldname": "request_id", 51 | "fieldtype": "Data", 52 | "hidden": 1, 53 | "label": "Request id" 54 | } 55 | ], 56 | "index_web_pages_for_search": 1, 57 | "istable": 1, 58 | "links": [], 59 | "modified": "2025-01-22 16:33:57.014368", 60 | "modified_by": "Administrator", 61 | "module": "GST India", 62 | "name": "GSTR Action", 63 | "owner": "Administrator", 64 | "permissions": [], 65 | "sort_field": "creation", 66 | "sort_order": "DESC", 67 | "states": [] 68 | } -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gstin/test_gstin.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Resilient Tech and Contributors 2 | # See license.txt 3 | import responses 4 | from responses import matchers 5 | 6 | from frappe.tests import IntegrationTestCase, change_settings 7 | 8 | from india_compliance.gst_india.doctype.gstin.gstin import validate_gst_transporter_id 9 | 10 | TEST_GSTIN = "24AANFA2641L1ZK" 11 | 12 | TRANSPORTER_ID_API_RESPONSE = { 13 | "success": True, 14 | "message": "Transporter details are fetched successfully", 15 | "result": { 16 | "transin": TEST_GSTIN, 17 | "tradeName": "_Test Transporter ID Comapany", 18 | "legalName": "_Test Transporter ID Comapany", 19 | "address1": "address 1", 20 | "address2": "address 2", 21 | "stateCode": "24", 22 | "pinCode": "390020", 23 | }, 24 | } 25 | 26 | 27 | class TestGSTIN(IntegrationTestCase): 28 | @classmethod 29 | def setUpClass(cls): 30 | super().setUpClass() 31 | 32 | @responses.activate 33 | @change_settings("GST Settings", {"validate_gstin_status": 1, "sandbox_mode": 0}) 34 | def test_validate_gst_transporter_id(self): 35 | self.mock_get_transporter_details_response() 36 | 37 | validate_gst_transporter_id(TEST_GSTIN) 38 | 39 | def mock_get_transporter_details_response(self): 40 | url = "https://asp.resilient.tech/ewb/Master/GetTransporterDetails" 41 | 42 | responses.add( 43 | responses.GET, 44 | url, 45 | json=TRANSPORTER_ID_API_RESPONSE, 46 | match=[matchers.query_param_matcher({"trn_no": TEST_GSTIN})], 47 | status=200, 48 | ) 49 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/pan/pan.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "field:pan", 4 | "creation": "2024-07-31 13:10:35.277697", 5 | "doctype": "DocType", 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "pan", 9 | "pan_status", 10 | "column_break_hzzp", 11 | "last_updated_on" 12 | ], 13 | "fields": [ 14 | { 15 | "fieldname": "pan", 16 | "fieldtype": "Data", 17 | "in_list_view": 1, 18 | "label": "Pan", 19 | "reqd": 1, 20 | "unique": 1 21 | }, 22 | { 23 | "fieldname": "pan_status", 24 | "fieldtype": "Data", 25 | "in_list_view": 1, 26 | "in_standard_filter": 1, 27 | "label": "Pan Status" 28 | }, 29 | { 30 | "fieldname": "last_updated_on", 31 | "fieldtype": "Datetime", 32 | "label": "Last Updated On" 33 | }, 34 | { 35 | "fieldname": "column_break_hzzp", 36 | "fieldtype": "Column Break" 37 | } 38 | ], 39 | "in_create": 1, 40 | "index_web_pages_for_search": 1, 41 | "links": [], 42 | "modified": "2024-08-08 14:08:25.490761", 43 | "modified_by": "Administrator", 44 | "module": "GST India", 45 | "name": "PAN", 46 | "naming_rule": "By fieldname", 47 | "owner": "Administrator", 48 | "permissions": [ 49 | { 50 | "delete": 1, 51 | "export": 1, 52 | "read": 1, 53 | "report": 1, 54 | "role": "System Manager", 55 | "share": 1 56 | }, 57 | { 58 | "export": 1, 59 | "read": 1, 60 | "report": 1, 61 | "role": "Accounts Manager", 62 | "share": 1 63 | }, 64 | { 65 | "export": 1, 66 | "read": 1, 67 | "report": 1, 68 | "role": "Accounts User", 69 | "share": 1 70 | } 71 | ], 72 | "sort_field": "creation", 73 | "sort_order": "DESC", 74 | "states": [] 75 | } -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/property_setter.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | from frappe.utils import cint 4 | 5 | from india_compliance.audit_trail.utils import ( 6 | get_audit_trail_doctypes, 7 | is_audit_trail_enabled, 8 | ) 9 | 10 | 11 | def validate(doc, method=None): 12 | flags = frappe.local.flags 13 | 14 | if flags.in_install or flags.in_migrate or not is_audit_trail_enabled(): 15 | return 16 | 17 | is_protected = is_protected_property_setter(doc) 18 | if doc.is_new() and (not is_protected or cint(doc.value) == 1): 19 | return 20 | 21 | if is_protected: 22 | throw_cannot_change_property_error(doc) 23 | 24 | old_doc = doc.get_doc_before_save() 25 | if is_protected_property_setter(old_doc): 26 | throw_cannot_change_property_error(old_doc) 27 | 28 | 29 | def on_trash(doc, method=None): 30 | flags = frappe.local.flags 31 | 32 | if ( 33 | flags.in_install 34 | or flags.in_migrate 35 | or not is_audit_trail_enabled() 36 | or not is_protected_property_setter(doc) 37 | ): 38 | return 39 | 40 | throw_cannot_change_property_error(doc) 41 | 42 | 43 | def throw_cannot_change_property_error(doc): 44 | frappe.throw( 45 | _( 46 | "Cannot change the Track Changes property for {0}, since it has been" 47 | " enabled to maintain Audit Trail" 48 | ).format(_(doc.doc_type)) 49 | ) 50 | 51 | 52 | def is_protected_property_setter(doc): 53 | return ( 54 | doc.doctype_or_field == "DocType" 55 | and doc.property == "track_changes" 56 | and doc.doc_type in get_audit_trail_doctypes() 57 | ) 58 | -------------------------------------------------------------------------------- /india_compliance/gst_india/utils/api.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def enqueue_integration_request(**kwargs): 5 | frappe.enqueue( 6 | "india_compliance.gst_india.utils.api.create_integration_request", 7 | **kwargs, 8 | ) 9 | 10 | 11 | def create_integration_request( 12 | url=None, 13 | request_id=None, 14 | request_headers=None, 15 | data=None, 16 | output=None, 17 | error=None, 18 | reference_doctype=None, 19 | reference_name=None, 20 | update_gstr_action=False, 21 | ): 22 | doc = frappe.get_doc( 23 | { 24 | "doctype": "Integration Request", 25 | "integration_request_service": "India Compliance API", 26 | "request_id": request_id, 27 | "url": url, 28 | "request_headers": pretty_json(request_headers), 29 | "data": pretty_json(data), 30 | "output": pretty_json(output), 31 | "error": pretty_json(error), 32 | "status": "Failed" if error else "Completed", 33 | "reference_doctype": reference_doctype, 34 | "reference_docname": reference_name, 35 | } 36 | ) 37 | doc.insert(ignore_permissions=True, ignore_links=True) 38 | 39 | if update_gstr_action: 40 | link_integration_request(request_id, doc.name) 41 | 42 | 43 | def link_integration_request(request_id, doc_name): 44 | frappe.db.set_value( 45 | "GSTR Action", {"request_id": request_id}, {"integration_request": doc_name} 46 | ) 47 | 48 | 49 | def pretty_json(obj): 50 | if not obj: 51 | return "" 52 | 53 | if isinstance(obj, str): 54 | return obj 55 | 56 | return frappe.as_json(obj, indent=4) 57 | -------------------------------------------------------------------------------- /india_compliance/gst_india/client_scripts/journal_entry.js: -------------------------------------------------------------------------------- 1 | frappe.ui.form.on("Journal Entry", { 2 | onload: (frm) => set_gstin_options(frm, frm.is_new()), 3 | company: (frm) => set_gstin_options(frm, true), 4 | }); 5 | 6 | async function set_gstin_options(frm, set_value) { 7 | const company = frm.doc.company; 8 | if (!company) return; 9 | 10 | const options = await india_compliance.get_gstin_options(company); 11 | frm.get_field("company_gstin").set_data(options); 12 | 13 | if (set_value) 14 | frm.set_value("company_gstin", options.length === 1 ? options[0] : ""); 15 | } 16 | 17 | frappe.ui.form.on("Journal Entry Account", { 18 | account: toggle_gstin_for_journal_entry, 19 | accounts_remove: toggle_gstin_for_journal_entry, 20 | }); 21 | 22 | function toggle_gstin_for_journal_entry(frm) { 23 | toggle_company_gstin(frm, "accounts", "account"); 24 | } 25 | 26 | async function toggle_company_gstin(frm, taxes_table, account_head) { 27 | _toggle_company_gstin(frm, await contains_gst_account(frm, taxes_table, account_head)); 28 | } 29 | 30 | async function contains_gst_account(frm, taxes_table, account_field) { 31 | if (!frm.gst_accounts || frm.company !== frm.doc.company) { 32 | frm.gst_accounts = await india_compliance.get_account_options(frm.doc.company); 33 | frm.company = frm.doc.company; 34 | } 35 | 36 | return frm.doc[taxes_table].some(row => frm.gst_accounts.includes(row[account_field])); 37 | } 38 | 39 | function _toggle_company_gstin(frm, reqd) { 40 | if (frm.get_field("company_gstin").df.reqd !== reqd) { 41 | frm.set_df_property("company_gstin", "reqd", reqd); 42 | frm.refresh_field("company_gstin"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_uom_map/gst_uom_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2022-11-15 19:15:06.551557", 4 | "default_view": "List", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "uom", 10 | "gst_uom" 11 | ], 12 | "fields": [ 13 | { 14 | "fieldname": "uom", 15 | "fieldtype": "Link", 16 | "in_list_view": 1, 17 | "label": "UOM", 18 | "options": "UOM", 19 | "reqd": 1, 20 | "unique": 1 21 | }, 22 | { 23 | "fieldname": "gst_uom", 24 | "fieldtype": "Autocomplete", 25 | "in_list_view": 1, 26 | "label": "GST UOM", 27 | "options": "\nBAG (Bags)\nBAL (Bale)\nBDL (Bundles)\nBKL (Buckles)\nBOU (Billion of Units)\nBOX (Box)\nBTL (Bottles)\nBUN (Bunches)\nCAN (Cans)\nCBM (Cubic Meters)\nCCM (Cubic Centimeters)\nCMS (Centimeters)\nCTN (Cartons)\nDOZ (Dozens)\nDRM (Drums)\nGGK (Great Gross)\nGMS (Grammes)\nGRS (Gross)\nGYD (Gross Yards)\nKGS (Kilograms)\nKLR (Kilolitre)\nKME (Kilometre)\nLTR (Litres)\nMLT (Mililitre)\nMTR (Meters)\nMTS (Metric Ton)\nNOS (Numbers)\nOTH (Others)\nPAC (Packs)\nPCS (Pieces)\nPRS (Pairs)\nQTL (Quintal)\nROL (Rolls)\nSET (Sets)\nSQF (Square Feet)\nSQM (Square Meters)\nSQY (Square Yards)\nTBS (Tablets)\nTGM (Ten Gross)\nTHD (Thousands)\nTON (Tonnes)\nTUB (Tubes)\nUGS (US Gallons)\nUNT (Units)\nYDS (Yards)", 28 | "reqd": 1 29 | } 30 | ], 31 | "index_web_pages_for_search": 1, 32 | "istable": 1, 33 | "links": [], 34 | "modified": "2024-03-29 11:54:43.163793", 35 | "modified_by": "Administrator", 36 | "module": "GST India", 37 | "name": "GST UOM Map", 38 | "owner": "Administrator", 39 | "permissions": [], 40 | "sort_field": "creation", 41 | "sort_order": "DESC", 42 | "states": [] 43 | } -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_gst_treatment_for_taxable_nil_transaction_item.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | DOCTYPES = ("Sales Invoice Item", "Purchase Invoice Item") 4 | 5 | 6 | def execute(): 7 | update_taxable_items() 8 | update_nil_rated_items() 9 | 10 | 11 | def update_taxable_items(): 12 | # Patch invoices where gst_treatment is Nil-Rated but tax is applied. 13 | # Cases where is_nil_exempt was checked but Item tax template was selected. 14 | for dt in DOCTYPES: 15 | doctype = frappe.qb.DocType(dt) 16 | 17 | ( 18 | frappe.qb.update(doctype) 19 | .set(doctype.gst_treatment, "Taxable") 20 | .where(doctype.gst_treatment.notin(("Zero-Rated", "Taxable"))) 21 | .where( 22 | ( 23 | doctype.cgst_amount 24 | + doctype.igst_amount 25 | + doctype.sgst_amount 26 | + doctype.cess_amount 27 | ) 28 | != 0 29 | ) 30 | .run() 31 | ) 32 | 33 | 34 | def update_nil_rated_items(): 35 | # Patch invoices where Item Tax Template is Nil-Rated. 36 | 37 | nil_rated_templates = frappe.get_all( 38 | "Item Tax Template", filters={"gst_treatment": "Nil-Rated"}, pluck="name" 39 | ) 40 | 41 | if not nil_rated_templates: 42 | return 43 | 44 | for dt in DOCTYPES: 45 | doctype = frappe.qb.DocType(dt) 46 | 47 | ( 48 | frappe.qb.update(doctype) 49 | .set(doctype.gst_treatment, "Nil-Rated") 50 | .where(doctype.item_tax_template.isin(nil_rated_templates)) 51 | .where(doctype.gst_treatment == "Taxable") 52 | .run() 53 | ) 54 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/test_version.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import frappe 4 | from frappe.tests import IntegrationTestCase 5 | 6 | from india_compliance.gst_india.utils.tests import create_sales_invoice 7 | 8 | 9 | class TestVersion(IntegrationTestCase): 10 | def test_validate_version_where_audit_trail_enabled(self): 11 | # enable audit trail 12 | frappe.db.set_single_value("Accounts Settings", "enable_audit_trail", 1) 13 | 14 | doc = create_sales_invoice(do_not_submit=True) 15 | doc.items[0].qty = 2 16 | doc.save(ignore_version=False) 17 | doc.submit() 18 | 19 | version = frappe.get_doc( 20 | "Version", {"ref_doctype": doc.doctype, "docname": doc.name} 21 | ) 22 | 23 | version.ref_doctype = "Address" 24 | 25 | self.assertRaisesRegex( 26 | frappe.ValidationError, 27 | re.compile(r"^(Cannot alter Versions of.*)"), 28 | version.save, 29 | ) 30 | 31 | self.assertRaisesRegex( 32 | frappe.ValidationError, 33 | re.compile(r"^(Cannot alter Versions of.*)"), 34 | version.delete, 35 | ) 36 | 37 | # disable audit trail 38 | frappe.db.set_single_value("Accounts Settings", "enable_audit_trail", 0) 39 | 40 | def test_validate_version_where_audit_trail_disabled(self): 41 | doc = create_sales_invoice(do_not_submit=True) 42 | doc.items[0].qty = 2 43 | doc.save(ignore_version=False) 44 | doc.submit() 45 | 46 | version = frappe.get_doc( 47 | "Version", {"ref_doctype": doc.doctype, "docname": doc.name} 48 | ) 49 | 50 | version.ref_doctype = "Address" 51 | 52 | version.save() 53 | version.delete() 54 | -------------------------------------------------------------------------------- /india_compliance/audit_trail/overrides/test_property_setter.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import frappe 4 | from frappe.tests import IntegrationTestCase 5 | 6 | 7 | class TestPropertySetter(IntegrationTestCase): 8 | def test_validate_property_setter_where_audit_trail_enabled_and_doc_is_protected( 9 | self, 10 | ): 11 | frappe.db.set_single_value("Accounts Settings", "enable_audit_trail", 1) 12 | frappe.db.delete( 13 | "Property Setter", 14 | { 15 | "doctype_or_field": "DocType", 16 | "doc_type": "Purchase Invoice", 17 | "property": "track_changes", 18 | }, 19 | ) 20 | 21 | doc = frappe.get_doc( 22 | { 23 | "doctype": "Property Setter", 24 | "doctype_or_field": "DocType", 25 | "doc_type": "Purchase Invoice", 26 | "property": "track_changes", 27 | "value": 1, 28 | }, 29 | ) 30 | doc.save() 31 | doc.value = 0 32 | 33 | self.assertRaisesRegex( 34 | frappe.ValidationError, 35 | re.compile(r"^(Cannot change the Track Changes property for*)"), 36 | doc.save, 37 | ) 38 | 39 | self.assertRaisesRegex( 40 | frappe.ValidationError, 41 | re.compile(r"^(Cannot change the Track Changes property for*)"), 42 | doc.delete, 43 | ) 44 | doc.reload() 45 | doc.doc_type = "Address" 46 | self.assertRaisesRegex( 47 | frappe.ValidationError, 48 | re.compile(r"^(Cannot change the Track Changes property for*)"), 49 | doc.save, 50 | ) 51 | frappe.db.set_single_value("Accounts Settings", "enable_audit_trail", 0) 52 | doc.delete() 53 | -------------------------------------------------------------------------------- /india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "field:hsn_code", 4 | "creation": "2017-06-21 10:48:56.422086", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "hsn_code", 10 | "description", 11 | "taxes" 12 | ], 13 | "fields": [ 14 | { 15 | "fieldname": "hsn_code", 16 | "fieldtype": "Data", 17 | "in_list_view": 1, 18 | "label": "HSN Code", 19 | "reqd": 1, 20 | "unique": 1 21 | }, 22 | { 23 | "fieldname": "description", 24 | "fieldtype": "Small Text", 25 | "in_list_view": 1, 26 | "label": "Description" 27 | }, 28 | { 29 | "fieldname": "taxes", 30 | "fieldtype": "Table", 31 | "label": "Taxes", 32 | "options": "Item Tax" 33 | } 34 | ], 35 | "links": [], 36 | "modified": "2024-03-29 11:54:42.576237", 37 | "modified_by": "Administrator", 38 | "module": "GST India", 39 | "name": "GST HSN Code", 40 | "naming_rule": "By fieldname", 41 | "owner": "Administrator", 42 | "permissions": [ 43 | { 44 | "read": 1, 45 | "role": "All" 46 | }, 47 | { 48 | "create": 1, 49 | "read": 1, 50 | "role": "Accounts Manager", 51 | "write": 1 52 | }, 53 | { 54 | "create": 1, 55 | "read": 1, 56 | "role": "Accounts User", 57 | "write": 1 58 | }, 59 | { 60 | "create": 1, 61 | "read": 1, 62 | "role": "System Manager", 63 | "write": 1 64 | }, 65 | { 66 | "create": 1, 67 | "read": 1, 68 | "role": "Item Manager", 69 | "write": 1 70 | }, 71 | { 72 | "create": 1, 73 | "read": 1, 74 | "role": "Stock Manager", 75 | "write": 1 76 | } 77 | ], 78 | "quick_entry": 1, 79 | "search_fields": "hsn_code, description", 80 | "sort_field": "creation", 81 | "sort_order": "DESC", 82 | "states": [], 83 | "title_field": "hsn_code", 84 | "track_changes": 1 85 | } -------------------------------------------------------------------------------- /india_compliance/patches/v14/set_correct_root_account_for_rcm.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.gst_india.constants import GST_ACCOUNT_FIELDS 4 | 5 | 6 | def execute(): 7 | settings = frappe.get_doc("GST Settings") 8 | company_accounts = get_company_accounts(settings) 9 | 10 | for accounts in company_accounts.values(): 11 | if not accounts.get("Output") or not accounts.get("Purchase Reverse Charge"): 12 | continue 13 | 14 | rcm_accounts = get_asset_rcm_accounts(accounts) 15 | if not rcm_accounts: 16 | continue 17 | 18 | output_account = frappe.db.get_value( 19 | "Account", 20 | accounts["Output"].cgst_account, 21 | ["parent_account", "root_type"], 22 | as_dict=True, 23 | ) 24 | 25 | if not output_account: 26 | continue 27 | 28 | # update reverse charge accounts 29 | frappe.db.set_value( 30 | "Account", 31 | {"name": ("in", rcm_accounts)}, 32 | output_account, 33 | update_modified=False, 34 | ) 35 | 36 | 37 | def get_company_accounts(settings): 38 | company_accounts = {} 39 | for row in settings.gst_accounts: 40 | company_accounts.setdefault(row.company, {}) 41 | company_accounts[row.company][row.account_type] = row 42 | 43 | return company_accounts 44 | 45 | 46 | def get_asset_rcm_accounts(accounts): 47 | return frappe.get_all( 48 | "Account", 49 | filters={ 50 | "root_type": "Asset", 51 | "name": ( 52 | "in", 53 | [ 54 | accounts["Purchase Reverse Charge"].get(field) 55 | for field in GST_ACCOUNT_FIELDS 56 | ], 57 | ), 58 | }, 59 | pluck="name", 60 | ) 61 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/components/TheFooter.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 77 | -------------------------------------------------------------------------------- /india_compliance/gst_india/overrides/purchase_receipt.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from india_compliance.gst_india.overrides.purchase_invoice import ( 4 | set_ineligibility_reason, 5 | ) 6 | from india_compliance.gst_india.overrides.sales_invoice import ( 7 | update_dashboard_with_gst_logs, 8 | ) 9 | from india_compliance.gst_india.overrides.transaction import ( 10 | ignore_gst_validations, 11 | validate_mandatory_fields, 12 | validate_transaction, 13 | ) 14 | from india_compliance.gst_india.utils import is_api_enabled 15 | from india_compliance.gst_india.utils.e_waybill import get_e_waybill_info 16 | 17 | 18 | def get_dashboard_data(data): 19 | return update_dashboard_with_gst_logs( 20 | "Purchase Receipt", 21 | data, 22 | "e-Waybill Log", 23 | "Integration Request", 24 | ) 25 | 26 | 27 | def onload(doc, method=None): 28 | if ignore_gst_validations(doc): 29 | return 30 | 31 | if ( 32 | validate_mandatory_fields( 33 | doc, ("company_gstin", "place_of_supply", "gst_category"), throw=False 34 | ) 35 | is False 36 | ): 37 | return 38 | 39 | set_ineligibility_reason(doc, show_alert=False) 40 | 41 | # Load e-waybill info if applicable 42 | if not doc.get("ewaybill"): 43 | return 44 | 45 | gst_settings = frappe.get_cached_doc("GST Settings") 46 | 47 | if ( 48 | is_api_enabled(gst_settings) 49 | and gst_settings.enable_e_waybill 50 | and ( 51 | gst_settings.enable_e_waybill_from_pr or gst_settings.auto_cancel_e_waybill 52 | ) 53 | and (e_waybill_info := get_e_waybill_info(doc)) 54 | ): 55 | doc.set_onload("e_waybill_info", e_waybill_info) 56 | 57 | 58 | def validate(doc, method=None): 59 | if validate_transaction(doc) is False: 60 | return 61 | 62 | set_ineligibility_reason(doc) 63 | -------------------------------------------------------------------------------- /india_compliance/patches/post_install/update_itc_classification_field.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder.functions import IfNull 3 | 4 | from india_compliance.utils.custom_fields import delete_old_fields 5 | 6 | 7 | def execute(): 8 | patch_field_in_purchase_invoice() 9 | patch_journal_entry() 10 | 11 | delete_old_fields("eligibility_for_itc", "Purchase Invoice") 12 | delete_old_fields("reversal_type", "Journal Entry") 13 | 14 | 15 | def patch_field_in_purchase_invoice(): 16 | if "eligibility_for_itc" not in frappe.db.get_table_columns("Purchase Invoice"): 17 | return 18 | 19 | doctype = frappe.qb.DocType("Purchase Invoice") 20 | depricated_eligibility_for_itc = ( 21 | "Ineligible As Per Section 17(5)", 22 | "Ineligible Others", 23 | ) 24 | ( 25 | frappe.qb.update(doctype) 26 | .set(doctype.itc_classification, doctype.eligibility_for_itc) 27 | .where(doctype.eligibility_for_itc.notin(depricated_eligibility_for_itc)) 28 | .run() 29 | ) 30 | ( 31 | frappe.qb.update(doctype) 32 | .set(doctype.itc_classification, "All Other ITC") 33 | .where(IfNull(doctype.itc_classification, "") == "") 34 | .run() 35 | ) 36 | ( 37 | frappe.qb.update(doctype) 38 | .set(doctype.ineligibility_reason, "Ineligible As Per Section 17(5)") 39 | .where(doctype.eligibility_for_itc.isin(depricated_eligibility_for_itc)) 40 | .run() 41 | ) 42 | 43 | 44 | def patch_journal_entry(): 45 | if "reversal_type" not in frappe.db.get_table_columns("Journal Entry"): 46 | return 47 | 48 | doctype = frappe.qb.DocType("Journal Entry") 49 | ( 50 | frappe.qb.update(doctype) 51 | .set(doctype.reversal_type, doctype.ineligibility_reason) 52 | .where(IfNull(doctype.reversal_type, "") != "") 53 | .run() 54 | ) 55 | -------------------------------------------------------------------------------- /india_compliance/public/js/india_compliance_account/services/AuthService.js: -------------------------------------------------------------------------------- 1 | export async function get_api_secret() { 2 | return call_server_method( 3 | "india_compliance.gst_india.page.india_compliance_account.get_api_secret" 4 | ); 5 | } 6 | 7 | export async function set_api_secret(api_secret) { 8 | return call_server_method( 9 | "india_compliance.gst_india.page.india_compliance_account.set_api_secret", 10 | { api_secret } 11 | ); 12 | } 13 | 14 | export function login(email) { 15 | return india_compliance.gst_api.call("auth/login", { 16 | body: { email }, 17 | fail_silently: true, 18 | }); 19 | } 20 | 21 | export function signup(email, gstin) { 22 | return india_compliance.gst_api.call("auth/signup", { 23 | body: { email, gstin }, 24 | fail_silently: true, 25 | }); 26 | } 27 | 28 | export function check_free_trial_eligibility(gstin) { 29 | return india_compliance.gst_api.call("auth/is_eligible_for_free_trial", { 30 | body: { gstin }, 31 | fail_silently: true, 32 | }); 33 | } 34 | 35 | export function get_session() { 36 | return call_server_method("india_compliance.gst_india.page.india_compliance_account.get_auth_session"); 37 | } 38 | 39 | export function set_session(session) { 40 | call_server_method("india_compliance.gst_india.page.india_compliance_account.set_auth_session", { 41 | session, 42 | }); 43 | } 44 | 45 | export function validate_session(session_id) { 46 | return india_compliance.gst_api.call("auth/validate_session", { 47 | body: { session_id }, 48 | }); 49 | } 50 | 51 | function call_server_method(method, args) { 52 | return frappe 53 | .call({ 54 | method: method, 55 | args: args, 56 | silent: true, 57 | }) 58 | .then((response) => response.message || null) 59 | .catch(() => null); 60 | } 61 | --------------------------------------------------------------------------------