diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index 1e92ffee0f..2533063180 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -7,7 +7,7 @@ import frappe import frappe.utils -from frappe import _ +from frappe import _, request_cache from frappe.utils import add_to_date, cint, now_datetime from india_compliance.exceptions import ( @@ -465,16 +465,18 @@ def validate_auth_token(self): return - def get_filing_preference(self): + @request_cache + def get_filing_preference(self, date=None): return self.get( - action="GETPREF", params={"fy": self.get_fy()}, endpoint="returns" + action="GETPREF", params={"fy": self.get_fy(date)}, endpoint="returns" ) @staticmethod - def get_fy(): - date = frappe.utils.getdate() - + def get_fy(date=None): # Standard for India as per GST + if not date: + date = frappe.utils.getdate() + if date.month < 4: return f"{date.year - 1}-{str(date.year)[2:]}" diff --git a/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py b/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py index 75ea1ca070..3b23b2b4d3 100644 --- a/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py +++ b/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py @@ -6,6 +6,7 @@ from frappe import unscrub from frappe.utils import flt +from india_compliance.gst_india.utils.__init__ import get_month_or_quarter_dict from india_compliance.gst_india.utils.gstr_1 import GSTR1_SubCategory from india_compliance.gst_india.utils.gstr_1.__init__ import ( CATEGORY_SUB_CATEGORY_MAPPING, @@ -21,6 +22,9 @@ summarize_retsum_data, ) +MONTH = list(get_month_or_quarter_dict().keys())[4:] +QUARTER = ["Jan-Mar", "Apr-Jun", "Jul-Sep", "Oct-Dec"] + class SummarizeGSTR1: AMOUNT_FIELDS = { @@ -504,6 +508,14 @@ def generate_gstr1_data(self, filters, callback=None): # APIs Enabled status = self.get_return_status() + from india_compliance.gst_india.utils.gstin_info import get_filing_preference + + filing_preference = get_filing_preference(self.gstin, self.return_period) + + if filing_preference and self.filing_preference is None: + self.db_set({"filing_preference": filing_preference}) + update_filters(filters, filing_preference) + if status == "Filed": gov_data_field = "filed" else: @@ -577,7 +589,7 @@ def get_books_gstr1_data(self, filters, aggregate=False): return books_data from_date, to_date = get_gstr_1_from_and_to_date( - filters.month_or_quarter, filters.year + filters.month_or_quarter, filters.year, filters.is_quarterly ) _filters = frappe._dict( @@ -659,3 +671,18 @@ def normalize_data(data): data[subcategory] = [*subcategory_data.values()] return data + + +def update_filters(filters, filing_preference): + is_quarterly = 1 if filing_preference == "Quarterly" else 0 + + if filters.is_quarterly == is_quarterly: + return + + filters.is_quarterly = is_quarterly + if filters.is_quarterly == 1: + quarter = MONTH.index(filters.month_or_quarter) // 3 + filters.month_or_quarter = QUARTER[quarter] + else: + quarter_idx = QUARTER.index(filters.month_or_quarter) + filters.month_or_quarter = MONTH[(quarter_idx * 3) + 2] diff --git a/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.js b/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.js index 0c7e31968a..6c5b7b5bf4 100644 --- a/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.js +++ b/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.js @@ -4,7 +4,7 @@ frappe.ui.form.on("GST Return Log", { refresh(frm) { const [month_or_quarter, year] = india_compliance.get_month_year_from_period( - frm.doc.return_period + frm.doc.return_period, frm.doc.filing_preference === "Monthly" ? 0 : 1 ); frm.add_custom_button(__("View GSTR-1"), () => { @@ -17,13 +17,14 @@ frappe.ui.form.on("GST Return Log", { clearInterval(interval); resolve(); } - }, 100); + }, 200); }).then(async () => { await cur_frm.set_value({ company: frm.doc.company, company_gstin: frm.doc.gstin, year: year, month_or_quarter: month_or_quarter, + is_quarterly : frm.doc.filing_preference == "Monthly" ? 0 : 1, }); cur_frm.save(); }); diff --git a/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.json b/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.json index 78857c16f5..95f1bb77df 100644 --- a/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.json +++ b/india_compliance/gst_india/doctype/gst_return_log/gst_return_log.json @@ -12,6 +12,7 @@ "return_period", "company", "return_type", + "filing_preference", "column_break_sqwh", "filing_date", "acknowledgement_number", @@ -187,6 +188,12 @@ "label": "Return Type", "read_only": 1, "reqd": 1 + }, + { + "fieldname": "filing_preference", + "fieldtype": "Data", + "label": "Filing Preference", + "read_only": 1 } ], "in_create": 1, @@ -197,7 +204,7 @@ "link_fieldname": "reference_docname" } ], - "modified": "2024-08-14 17:53:03.509293", + "modified": "2024-11-07 23:12:55.966658", "modified_by": "Administrator", "module": "GST India", "name": "GST Return Log", diff --git a/india_compliance/gst_india/doctype/gst_settings/gst_settings.json b/india_compliance/gst_india/doctype/gst_settings/gst_settings.json index 49b8f8a9d5..d1edc45c7a 100644 --- a/india_compliance/gst_india/doctype/gst_settings/gst_settings.json +++ b/india_compliance/gst_india/doctype/gst_settings/gst_settings.json @@ -45,7 +45,6 @@ "e_invoice_applicable_companies", "gstr_1_section_break", "compare_gstr_1_data", - "filing_frequency", "column_break_cxmn", "restrict_changes_after_gstr_1", "role_allowed_to_modify", @@ -585,12 +584,6 @@ "fieldname": "column_break_cxmn", "fieldtype": "Column Break" }, - { - "fieldname": "filing_frequency", - "fieldtype": "Select", - "label": "Filing Frequency", - "options": "Monthly\nQuarterly" - }, { "depends_on": "eval: doc.restrict_changes_after_gstr_1", "fieldname": "role_allowed_to_modify", @@ -633,7 +626,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-07-25 19:16:25.036677", + "modified": "2024-10-17 17:30:39.135632", "modified_by": "Administrator", "module": "GST India", "name": "GST Settings", diff --git a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js index 2df782df56..37c3c385b2 100644 --- a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js +++ b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js @@ -108,8 +108,6 @@ frappe.ui.form.on(DOCTYPE, { frm.trigger("company"); }); - frm.filing_frequency = gst_settings.filing_frequency; - // Set Default Values set_default_company_gstin(frm); set_options_for_year(frm); @@ -122,7 +120,7 @@ frappe.ui.form.on(DOCTYPE, { const { filters } = message; const [month_or_quarter, year] = - india_compliance.get_month_year_from_period(filters.period); + india_compliance.get_month_year_from_period(filters.period, frm.doc.is_quarterly); if ( frm.doc.company_gstin !== filters.company_gstin || @@ -159,11 +157,15 @@ frappe.ui.form.on(DOCTYPE, { if ( frm.doc.company_gstin !== filters.company_gstin || - frm.doc.month_or_quarter != filters.month_or_quarter || frm.doc.year != filters.year ) return; + if(frm.doc.is_quarterly != filters.is_quarterly){ + frm.set_value("month_or_quarter", filters.month_or_quarter) + frm.set_value("is_quarterly", filters.is_quarterly) + } + frappe.after_ajax(() => { frm.doc.__gst_data = data ; frm.trigger("load_gstr1_data"); @@ -180,10 +182,14 @@ frappe.ui.form.on(DOCTYPE, { frm.set_value("company_gstin", options[0]); }, - company_gstin: render_empty_state, + company_gstin(frm){ + render_empty_state(frm); + update_fields_based_on_filing_preference(frm); + }, month_or_quarter(frm) { render_empty_state(frm); + update_fields_based_on_filing_preference(frm); }, year(frm) { @@ -191,6 +197,11 @@ frappe.ui.form.on(DOCTYPE, { set_options_for_month_or_quarter(frm); }, + is_quarterly(frm){ + render_empty_state(frm); + set_options_for_month_or_quarter(frm); + }, + refresh(frm) { // Primary Action frm.disable_save(); @@ -2117,7 +2128,46 @@ function set_options_for_year(frm) { frm.set_value("year", current_year.toString()); } -function set_options_for_month_or_quarter(frm) { +async function update_fields_based_on_filing_preference(frm){ + let {message : preference } = await frappe.call({ + method: "india_compliance.gst_india.doctype.gstr_1_beta.gstr_1_beta.get_filing_preference_from_log", + args: {month_or_quarter: frm.doc.month_or_quarter, year: frm.doc.year, company_gstin: frm.doc.company_gstin}, + }) + + if(preference === undefined){ + frm.set_df_property("is_quarterly", "read_only", 0); + return + } + + preference = cint(preference) + + if(preference === frm.doc.is_quarterly){ + frm.set_df_property("is_quarterly", "read_only", 1); + return + } + + update_preference(frm, preference) +} + +function update_preference(frm, preference){ + frm.doc.is_quarterly = preference + frm.set_df_property("is_quarterly", "read_only", 1) + set_options_for_month_or_quarter(frm, set_only_options=true) + + if(preference === 1){ + const month_index = india_compliance.MONTH.indexOf(frm.doc.month_or_quarter) + const quarter = Math.floor(month_index / 3) + frm.doc.month_or_quarter = india_compliance.QUARTER[quarter] + }else{ + const quarter_index = india_compliance.QUARTER.indexOf(frm.doc.month_or_quarter) * 3 + // added 2 to set month_or_quarter as last month of that quarter + frm.doc.month_or_quarter = india_compliance.MONTH[quarter_index+2] + } + frm.refresh_field("month_or_quarter") + frm.refresh_field("is_quarterly") +} + +function set_options_for_month_or_quarter(frm, set_only_options=false) { /** * Set options for Month or Quarter based on the year and current date * 1. If the year is current year, then options are till current month @@ -2136,7 +2186,7 @@ function set_options_for_month_or_quarter(frm) { if (frm.doc.year === current_year) { // Options for current year till current month - if (frm.filing_frequency === "Monthly") + if (frm.doc.is_quarterly === 0) options = india_compliance.MONTH.slice(0, current_month_idx + 1); else { let quarter_idx; @@ -2149,15 +2199,17 @@ function set_options_for_month_or_quarter(frm) { } } else if (frm.doc.year === "2017") { // Options for 2017 from July to December - if (frm.filing_frequency === "Monthly") + if (frm.doc.is_quarterly === 0) options = india_compliance.MONTH.slice(6); else options = india_compliance.QUARTER.slice(2); } else { - if (frm.filing_frequency === "Monthly") options = india_compliance.MONTH; + if (frm.doc.is_quarterly === 0) options = india_compliance.MONTH; else options = india_compliance.QUARTER; } set_field_options("month_or_quarter", options); + if (set_only_options) return + if (frm.doc.year === current_year) // set second last option as default frm.set_value("month_or_quarter", options[options.length - 2]); @@ -2181,6 +2233,7 @@ async function get_net_gst_liability(frm) { year: frm.doc.year, company_gstin: frm.doc.company_gstin, company: frm.doc.company, + is_quarterly: frm.doc.is_quarterly, }, }); diff --git a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.json b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.json index ee26f09558..d141d7b16d 100644 --- a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.json +++ b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.json @@ -13,6 +13,8 @@ "year", "column_break_qcor", "month_or_quarter", + "column_break_auof", + "is_quarterly", "data_section", "tabs_html", "tabs_empty_state", @@ -81,13 +83,23 @@ "fieldtype": "Select", "label": "Month/Quarter", "reqd": 1 + }, + { + "fieldname": "column_break_auof", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_quarterly", + "fieldtype": "Check", + "label": "Is Quarterly" } ], "hide_toolbar": 1, "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-05-27 19:30:01.074149", + "modified": "2024-10-17 18:50:23.515301", "modified_by": "Administrator", "module": "GST India", "name": "GSTR-1 Beta", diff --git a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py index d0f7f00280..02143be138 100644 --- a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py +++ b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py @@ -7,7 +7,7 @@ from frappe import _ from frappe.model.document import Document from frappe.query_builder.functions import Date, Sum -from frappe.utils import get_last_day, getdate +from frappe.utils import cint, get_last_day, getdate from india_compliance.gst_india.api_classes.taxpayer_base import ( TaxpayerBaseAPI, @@ -125,6 +125,7 @@ def _generate_gstr1(self): company_gstin=self.company_gstin, month_or_quarter=self.month_or_quarter, year=self.year, + is_quarterly=self.is_quarterly, ) try: @@ -166,14 +167,16 @@ def on_generate(self, data, filters=None): @frappe.whitelist() -def get_net_gst_liability(company, company_gstin, month_or_quarter, year): +def get_net_gst_liability(company, company_gstin, month_or_quarter, year, is_quarterly): """ Returns the net output balance for the given return period as per ledger entries """ frappe.has_permission("GSTR-1 Beta", throw=True) - from_date, to_date = get_gstr_1_from_and_to_date(month_or_quarter, year) + from_date, to_date = get_gstr_1_from_and_to_date( + month_or_quarter, year, is_quarterly + ) filters = frappe._dict( { @@ -229,15 +232,14 @@ def get_period(month_or_quarter: str, year: str) -> str: return f"{month_number}{year}" -def get_gstr_1_from_and_to_date(month_or_quarter: str, year: str) -> tuple: +def get_gstr_1_from_and_to_date( + month_or_quarter: str, year: str, is_quarterly: str +) -> tuple: """ Returns the from and to date for the given month or quarter and year This is used to filter the data for the given period in Books """ - - filing_frequency = frappe.get_cached_value("GST Settings", None, "filing_frequency") - - if filing_frequency == "Quarterly": + if cint(is_quarterly): start_month, end_month = month_or_quarter.split("-") from_date = getdate(f"{year}-{start_month}-01") to_date = get_last_day(f"{year}-{end_month}-01") @@ -247,3 +249,16 @@ def get_gstr_1_from_and_to_date(month_or_quarter: str, year: str) -> tuple: to_date = get_last_day(from_date) return from_date, to_date + + +@frappe.whitelist() +def get_filing_preference_from_log(month_or_quarter: str, year: str, company_gstin): + period = get_period(month_or_quarter, year) + filing_preference = frappe.db.get_value( + "GST Return Log", f"GSTR1-{period}-{company_gstin}", "filing_preference" + ) + + if not filing_preference: + return None + + return 1 if filing_preference == "Quarterly" else 0 diff --git a/india_compliance/gst_india/setup/__init__.py b/india_compliance/gst_india/setup/__init__.py index f18073a90b..e71562ddcb 100644 --- a/india_compliance/gst_india/setup/__init__.py +++ b/india_compliance/gst_india/setup/__init__.py @@ -220,7 +220,6 @@ def set_default_gst_settings(): # GSTR-1 "compare_gstr_1_data": 1, "freeze_transactions": 1, - "filing_frequency": "Monthly", } if frappe.conf.developer_mode: diff --git a/india_compliance/gst_india/utils/gstin_info.py b/india_compliance/gst_india/utils/gstin_info.py index 01e02611d6..15152e0ada 100644 --- a/india_compliance/gst_india/utils/gstin_info.py +++ b/india_compliance/gst_india/utils/gstin_info.py @@ -4,13 +4,15 @@ import frappe from frappe import _ -from frappe.utils import getdate +from frappe.query_builder import Case +from frappe.utils import cint, get_last_day, getdate from india_compliance.exceptions import GSPServerError from india_compliance.gst_india.api_classes.base import BASE_URL from india_compliance.gst_india.api_classes.e_invoice import EInvoiceAPI from india_compliance.gst_india.api_classes.e_waybill import EWaybillAPI from india_compliance.gst_india.api_classes.public import PublicAPI +from india_compliance.gst_india.api_classes.taxpayer_returns import GSTR1API from india_compliance.gst_india.doctype.gst_return_log.gst_return_log import ( process_gstr_1_returns_info, ) @@ -335,6 +337,124 @@ def get_gstr_1_return_status( return "Not Filed" +def get_filing_preference(gstin, period): + month = cint(period[:2]) + year = cint(period[2:]) + + start_month = (month - 1) // 3 * 3 + 1 + quarter = (month - 1) // 3 + + start_date = getdate(f"{year}-{start_month}-01") + months_of_quarter = [start_month + i for i in range(3)] + log_names = [f"GSTR1-{month:02d}{year}-{gstin}" for month in months_of_quarter] + + if filing_preference := frappe.get_list( + "GST Return Log", + filters={"name": ["in", log_names]}, + fields=["filing_preference"], + ): + for record in filing_preference: + if not record.get("filing_preference"): + continue + + return record.get("filing_preference") + + if frappe.db.get_value( + "GST Return Log", log_names[0], "filing_status" + ) != "Filed" and getdate() < get_last_day(start_date): + return + + api = GSTR1API(company_gstin=gstin) + response = api.get_filing_preference(date=start_date).response + + filing_preference = ( + "Quarterly" if response[quarter].get("preference") == "Q" else "Monthly" + ) + frappe.enqueue( + create_gst_return_log_for_quarter, + gstin=gstin, + log_names=log_names, + filing_preference=filing_preference, + ) + + return filing_preference + + +def create_gst_return_log_for_quarter(gstin, log_names, filing_preference): + existing_log = frappe.get_all( + "GST Return Log", filters={"name": ["in", log_names]}, pluck="name" + ) + + for log_name in log_names: + if log_name in existing_log: + frappe.db.set_value( + "GST Return Log", log_name, "filing_preference", filing_preference + ) + continue + + frappe.get_doc( + { + "doctype": "GST Return Log", + "name": log_name, + "return_type": "GSTR1", + "filing_preference": filing_preference, + "return_period": log_name.split("-")[1], + "gstin": gstin, + } + ).insert() + + update_logs_without_filing_preference() + + +def update_logs_without_filing_preference(): + gst_return_logs = frappe.get_all( + "GST Return Log", + filters={"filing_preference": ["is", "not set"]}, + fields=["name", "return_period", "gstin"], + ) + + if not gst_return_logs: + return + + filing_preferences = {} + logs_to_update = {} + + for log in gst_return_logs: + return_period = log.get("return_period") + gstin = log.get("gstin") + financial_year = get_fy(return_period) + key = f"{financial_year}:{gstin}" + + if key not in filing_preferences: + start_date = getdate(f"{return_period[2:]}-{return_period[:2]}-01") + api = GSTR1API(company_gstin=gstin) + filing_preferences[key] = api.get_filing_preference( + date=start_date + ).response + + month = cint(return_period[:2]) + quarter = (month - 1) // 3 + preference_data = filing_preferences.get(key) + logs_to_update[log.get("name")] = ( + "Quarterly" + if preference_data[quarter].get("preference") == "Q" + else "Monthly" + ) + + gst_return_log = frappe.qb.DocType("GST Return Log") + case_conditions = Case() + + for name, preference_data in logs_to_update.items(): + case_conditions.when(gst_return_log.name == name, preference_data) + + ( + frappe.qb.update(gst_return_log) + .set(gst_return_log.filing_preference, case_conditions) + .where(gst_return_log.name.isin(list(logs_to_update.keys()))) + .run() + ) + + def get_fy(period, year_increment=0): month, year = period[:2], period[2:] year = str(int(year) + year_increment) diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index a889da558e..1d8cb79120 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -30,7 +30,7 @@ Object.assign(india_compliance, { QUARTER: ["Jan-Mar", "Apr-Jun", "Jul-Sep", "Oct-Dec"], - get_month_year_from_period(period) { + get_month_year_from_period(period, is_quarterly) { /** * Returns month or quarter and year from the period * Month or quarter depends on the filing frequency set in GST Settings @@ -39,12 +39,11 @@ Object.assign(india_compliance, { * @returns {Array} - [month_or_quarter, year] */ - const { filing_frequency } = gst_settings; const month_number = period.slice(0, 2); const year = period.slice(2); - if (filing_frequency === "Monthly") return [this.MONTH[month_number - 1], year]; - else return [this.QUARTER[Math.floor(month_number / 3)], year]; + if (is_quarterly === 0) return [this.MONTH[month_number - 1], year]; + else return [this.QUARTER[Math.floor(month_number / 3) - 1], year]; }, get_gstin_query(party, party_type = "Company") {