Skip to content

Commit

Permalink
fix: Suggest Invoices that Should be Marked as Pending (#3009)
Browse files Browse the repository at this point in the history
Co-authored-by: Smit Vora <smitvora203@gmail.com>
(cherry picked from commit 95db03d)
  • Loading branch information
Ninad1306 authored and mergify[bot] committed Feb 3, 2025
1 parent 5193b9d commit 0e7377f
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ frappe.ui.form.on(DOCTYPE, {
render_empty_state(frm);
if (!frm.doc.company) return;
const options = await india_compliance.set_gstin_options(frm);

frm.set_value("company_gstin", options[0]);

set_period_options(frm);
},

company_gstin(frm) {
render_empty_state(frm);
set_period_options(frm);
},

company_gstin: render_empty_state,
period: render_empty_state,

refresh(frm) {
show_download_invoices_message(frm);
Expand All @@ -70,7 +76,13 @@ frappe.ui.form.on(DOCTYPE, {
});

class IMS extends reconciliation.reconciliation_tabs {
render_data(data) {
this.process_data(data);
super.render_data(data);
}

refresh(data) {
this.process_data(data);
super.refresh(data);
this.set_actions_summary();
}
Expand Down Expand Up @@ -136,6 +148,7 @@ class IMS extends reconciliation.reconciliation_tabs {
"Mismatch",
"Manual Match",
"Missing in PI",
"Suggested Mark as Pending",
],
},
{
Expand Down Expand Up @@ -562,6 +575,28 @@ class IMS extends reconciliation.reconciliation_tabs {
${frappe.datetime.str_to_user(row.bill_date) || ""}
`;
}

process_data(data = this.frm.__invoice_data) {
if (!data || !this.frm?.doc?.period) return;

const { period } = this.frm.doc;
const month = period.slice(0, 2);
const year = period.slice(2);
const reference_date = new Date(year, month, 0);

// Change match status of invoices in which purchase is booked in next period
// but supplier has filed return in current period
for (const row of data) {
if (!row._purchase_invoice?.posting_date) continue;

const bill_date = str_to_obj(row._inward_supply.bill_date);
const posting_date = str_to_obj(row._purchase_invoice.posting_date);

if (posting_date > reference_date && bill_date <= reference_date) {
row.match_status = "Suggested Mark as Pending";
}
}
}
}

class IMSAction {
Expand Down Expand Up @@ -1016,3 +1051,19 @@ function show_download_invoices_message(frm) {
frm.ims_actions.download_ims_data();
});
}

async function set_period_options(frm) {
if (!(frm.doc.company && frm.doc.company_gstin)) return;

const { message: period_options } = await frappe.call({
method: "india_compliance.gst_india.doctype.gst_invoice_management_system.gst_invoice_management_system.get_period_options",
args: { company: frm.doc.company, company_gstin: frm.doc.company_gstin },
});

frm.get_field("period").set_data(period_options);
frm.set_value("period", period_options[0]);
}

function str_to_obj(d) {
return frappe.datetime.user_to_obj(frappe.datetime.str_to_user(d));
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"company",
"column_break_2",
"company_gstin",
"column_break_mrvm",
"period",
"data_section",
"invoice_html",
"invoice_empty_state",
Expand Down Expand Up @@ -49,13 +51,22 @@
{
"fieldname": "data_section",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_mrvm",
"fieldtype": "Column Break"
},
{
"fieldname": "period",
"fieldtype": "Autocomplete",
"label": "Period"
}
],
"hide_toolbar": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-01-17 12:19:24.001794",
"modified": "2025-01-27 10:36:10.098739",
"modified_by": "Administrator",
"module": "GST India",
"name": "GST Invoice Management System",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import format_date
from frappe.utils import add_to_date, format_date

from india_compliance.gst_india.api_classes.taxpayer_base import (
TaxpayerBaseAPI,
Expand Down Expand Up @@ -37,6 +37,10 @@
unlink_documents as _unlink_documents,
)
from india_compliance.gst_india.utils.exporter import ExcelExporter
from india_compliance.gst_india.utils.gstin_info import (
get_latest_3b_filed_period,
update_gstr_returns_info,
)
from india_compliance.gst_india.utils.gstr_2 import (
GSTRCategory,
ReturnType,
Expand Down Expand Up @@ -109,7 +113,7 @@ def get_invoice_data(self, inward_supply=None, purchase=None, filters=None):
"is_supplier_return_filed": doc.is_supplier_return_filed,
"doc_type": doc.doc_type,
"posting_date": format_date(
_purchase_invoice.pop("posting_date", None)
_purchase_invoice.get("posting_date")
),
"_inward_supply": doc,
"_purchase_invoice": _purchase_invoice,
Expand Down Expand Up @@ -275,6 +279,41 @@ def download_excel_report(data, doc):
build_data.export_data()


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

# Calculate six months ago as fallback
six_months_ago = add_to_date(None, months=-7).strftime("%m%Y")
latest_3b_filed_period = get_latest_3b_filed_period(company, company_gstin) or (
six_months_ago,
)

# Fetch latest GSTR3B filing or default to six months ago
latest_3b_filed_period = format_period(latest_3b_filed_period[0])
six_months_ago = format_period(six_months_ago)

if latest_3b_filed_period <= six_months_ago:
update_gstr_returns_info(company, company_gstin)

# Generate last six months of valid periods
periods = []
date = add_to_date(None, months=-1)

while True:
period = date.strftime("%m%Y")
formatted_period = format_period(period)

if formatted_period <= latest_3b_filed_period or formatted_period < "201707":
break

periods.append(period)
date = add_to_date(date, months=-1)

return periods


def download_and_upload_ims_invoices(company_gstin):
"""
1. This function will download invoices from GST Portal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,16 @@ def download_file():
frappe.response["type"] = "download"


def process_gstr_1_returns_info(company, gstin, response):
def process_gstr_returns_info(company, gstin, e_filed_list):
process_gstr_1_returns_info(company, gstin, e_filed_list)
process_gstr_3b_returns_info(company, gstin, e_filed_list)


def process_gstr_1_returns_info(company, gstin, e_filed_list):
return_info = {}

# compile gstr-1 returns info
for info in response.get("EFiledlist"):
for info in e_filed_list:
if info["rtntype"] == "GSTR1":
return_info[f"GSTR1-{info['ret_prd']}-{gstin}"] = info

Expand Down Expand Up @@ -287,6 +292,28 @@ def _update_gstr_1_filed_upto(filing_date):
_update_gstr_1_filed_upto(filed_upto)


def process_gstr_3b_returns_info(company, gstin, e_filed_list):
for info in e_filed_list:
if info["status"] != "Filed":
continue

if frappe.db.exists(
"GST Return Log",
f'GSTR3B-{info["ret_prd"]}-{gstin}',
):
continue

gstr3b_log = frappe.new_doc("GST Return Log")
gstr3b_log.return_period = info["ret_prd"]
gstr3b_log.company = company
gstr3b_log.gstin = gstin
gstr3b_log.return_type = "GSTR3B"
gstr3b_log.filing_status = "Filed"
gstr3b_log.acknowledgement_number = info["arn"]
gstr3b_log.filing_date = datetime.strptime(info["dof"], "%d-%m-%Y").date()
gstr3b_log.insert()


def get_gst_return_log(posting_date, company_gstin):
period = getdate(posting_date).strftime("%m%Y")
if name := frappe.db.exists(DOCTYPE, f"GSTR1-{period}-{company_gstin}"):
Expand Down
76 changes: 54 additions & 22 deletions india_compliance/gst_india/utils/gstin_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
from datetime import timedelta
from string import whitespace

from pypika import Order

import frappe
from frappe import _
from frappe.query_builder.functions import Concat, Substring
from frappe.utils import getdate

from india_compliance.exceptions import GSPServerError
Expand All @@ -12,7 +15,7 @@
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.doctype.gst_return_log.gst_return_log import (
process_gstr_1_returns_info,
process_gstr_returns_info,
)
from india_compliance.gst_india.utils import parse_datetime, titlecase, validate_gstin

Expand Down Expand Up @@ -304,38 +307,67 @@ def fetch_transporter_id_status(transporter_id, throw=True):
####################################################################################################


def get_gstr_1_return_status(
company, gstin, period, process_info=True, year_increment=0
):
"""Returns Returns info for the given period"""
def get_gstr_1_return_status(company, gstin, period, year_increment=0):
"""Returns Returns-info for the given period"""
fy = get_fy(period, year_increment=year_increment)
e_filed_list = update_gstr_returns_info(company, gstin, fy)

response = PublicAPI().get_returns_info(gstin, fy)
if not response:
return

if process_info:
frappe.enqueue(
process_gstr_1_returns_info,
company=company,
gstin=gstin,
response=response,
enqueue_after_commit=True,
)

for info in response.get("EFiledlist"):
for info in e_filed_list:
if info["rtntype"] == "GSTR1" and info["ret_prd"] == period:
return info["status"]

# late filing possibility (limitation: only checks for the next FY: good enough)
if not year_increment and get_current_fy() != fy:
get_gstr_1_return_status(
company, gstin, period, process_info=process_info, year_increment=1
)
get_gstr_1_return_status(company, gstin, period, year_increment=1)

return "Not Filed"


def update_gstr_returns_info(company, gstin, fy=None):
if not fy:
fy = get_current_fy()

response = PublicAPI().get_returns_info(gstin, fy)
if not response:
return

e_filed_list = response.get("EFiledlist")

# If api call is made then update logs for GSTR1 AND GSTR3B
frappe.enqueue(
process_gstr_returns_info,
company=company,
gstin=gstin,
e_filed_list=e_filed_list,
enqueue_after_commit=True,
)

return e_filed_list


def get_latest_3b_filed_period(company, company_gstin):
log = frappe.qb.DocType("GST Return Log")

return (
frappe.qb.from_(log)
.select(log.return_period)
.where(log.company == company)
.where(log.gstin == company_gstin)
.where(log.return_type == "GSTR3B")
.where(log.filing_status == "Filed")
.orderby(
# eg: 202411
Concat(
Substring(log.return_period, 3, 4), # year
Substring(log.return_period, 1, 2), # month
),
order=Order.desc,
)
.limit(1)
.run(pluck=True)
)


def get_fy(period, year_increment=0):
month, year = period[:2], period[2:]
year = str(int(year) + year_increment)
Expand Down

0 comments on commit 0e7377f

Please sign in to comment.