2026-02-17 16:54:43 +05:30
|
|
|
from flask import render_template, request, send_file, jsonify
|
2026-01-24 02:11:19 +05:30
|
|
|
from werkzeug.utils import secure_filename
|
2025-12-02 15:38:19 +05:30
|
|
|
import pandas as pd
|
2026-01-24 02:11:19 +05:30
|
|
|
import os
|
2025-12-02 15:38:19 +05:30
|
|
|
import io
|
2026-01-24 02:11:19 +05:30
|
|
|
|
|
|
|
|
from AppCode.Config import DBConfig
|
|
|
|
|
from AppCode.FileHandler import FileHandler
|
2025-12-02 15:38:19 +05:30
|
|
|
from AppCode.YearGet import YearGet
|
|
|
|
|
|
2025-11-30 16:24:49 +05:30
|
|
|
|
|
|
|
|
class DocumentHandler:
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.years = []
|
|
|
|
|
self.documents = []
|
|
|
|
|
self.isSuccess = False
|
|
|
|
|
self.resultMessage = ""
|
|
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
# =========================
|
|
|
|
|
# Utility: Parse Year
|
|
|
|
|
# =========================
|
|
|
|
|
def parse_year(self, year_value):
|
|
|
|
|
if not year_value:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
year_value = year_value.strip()
|
|
|
|
|
|
|
|
|
|
if year_value.isdigit():
|
|
|
|
|
return int(year_value)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# "AY 2026-2027" → 2026
|
|
|
|
|
return int(year_value.split()[1].split('-')[0])
|
|
|
|
|
except Exception:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# =========================
|
|
|
|
|
# View Documents
|
|
|
|
|
# =========================
|
2025-11-30 16:24:49 +05:30
|
|
|
def View(self, request):
|
2026-02-06 15:11:58 +05:30
|
|
|
year_raw = request.args.get('year', '')
|
2025-11-30 16:24:49 +05:30
|
|
|
stage = request.args.get('stage', '')
|
|
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
year = self.parse_year(year_raw)
|
|
|
|
|
|
2025-11-30 16:24:49 +05:30
|
|
|
dbconfig = DBConfig()
|
|
|
|
|
connection = dbconfig.get_db_connection()
|
|
|
|
|
|
|
|
|
|
if not connection:
|
|
|
|
|
self.isSuccess = False
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
cursor = connection.cursor(dictionary=True)
|
2026-02-06 15:11:58 +05:30
|
|
|
|
2025-12-03 19:08:28 +05:30
|
|
|
cursor.callproc("GetDocuments", [year, stage])
|
2025-11-30 16:24:49 +05:30
|
|
|
|
2025-12-03 19:08:28 +05:30
|
|
|
for result in cursor.stored_results():
|
|
|
|
|
self.documents = result.fetchall()
|
|
|
|
|
break
|
2025-11-30 16:24:49 +05:30
|
|
|
|
|
|
|
|
cursor.callproc("GetYear")
|
|
|
|
|
|
|
|
|
|
for result in cursor.stored_results():
|
|
|
|
|
year_rows = result.fetchall()
|
2025-12-03 19:08:28 +05:30
|
|
|
break
|
2025-11-30 16:24:49 +05:30
|
|
|
|
|
|
|
|
self.years = [row['year'] for row in year_rows]
|
|
|
|
|
|
|
|
|
|
cursor.close()
|
|
|
|
|
connection.close()
|
2026-02-06 15:11:58 +05:30
|
|
|
|
2025-11-30 16:24:49 +05:30
|
|
|
self.isSuccess = True
|
|
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
# =========================
|
2025-12-03 19:08:28 +05:30
|
|
|
# Upload Documents
|
2026-02-06 15:11:58 +05:30
|
|
|
# =========================
|
2025-11-30 16:24:49 +05:30
|
|
|
def Upload(self, request):
|
|
|
|
|
dbconfig = DBConfig()
|
|
|
|
|
connection = dbconfig.get_db_connection()
|
|
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
if not connection:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
cursor = connection.cursor()
|
|
|
|
|
|
|
|
|
|
files = request.files.getlist('documents')
|
|
|
|
|
year_raw = request.form.get('year')
|
|
|
|
|
stage = request.form.get('stage')
|
|
|
|
|
|
|
|
|
|
year = self.parse_year(year_raw)
|
|
|
|
|
|
|
|
|
|
if not year:
|
|
|
|
|
self.resultMessage = "Invalid year selected."
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
for file in files:
|
|
|
|
|
if '.' not in file.filename:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
extension = file.filename.rsplit('.', 1)[1].lower()
|
|
|
|
|
|
|
|
|
|
if extension not in FileHandler.ALLOWED_EXTENSIONS:
|
|
|
|
|
print("Skipping invalid file:", extension)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
filename = secure_filename(file.filename)
|
|
|
|
|
filepath = os.path.join(FileHandler.UPLOAD_FOLDER, filename)
|
|
|
|
|
|
|
|
|
|
file.save(filepath)
|
|
|
|
|
|
2026-02-17 16:54:43 +05:30
|
|
|
cursor.callproc('InsertDocument',[filename, filepath, extension, year, stage])
|
2026-02-06 15:11:58 +05:30
|
|
|
|
|
|
|
|
connection.commit()
|
|
|
|
|
cursor.close()
|
|
|
|
|
connection.close()
|
|
|
|
|
|
|
|
|
|
# =========================
|
|
|
|
|
# Summary Preview (JSON)
|
|
|
|
|
# =========================
|
|
|
|
|
def Summary_preview(self, request):
|
2026-02-17 16:54:43 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
year_raw = request.args.get("year")
|
|
|
|
|
year = self.parse_year(year_raw)
|
|
|
|
|
|
|
|
|
|
if not year:
|
|
|
|
|
return jsonify([])
|
|
|
|
|
|
|
|
|
|
dbconfig = DBConfig()
|
|
|
|
|
connection = dbconfig.get_db_connection()
|
|
|
|
|
if not connection:
|
|
|
|
|
return jsonify([])
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
stages = {
|
|
|
|
|
"ITR": "itr",
|
|
|
|
|
"AO": "ao",
|
|
|
|
|
"CIT": "cit",
|
|
|
|
|
"ITAT": "itat",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stage_data = {}
|
2025-12-03 19:08:28 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
for stage_name, table_name in stages.items():
|
|
|
|
|
cursor = connection.cursor(dictionary=True)
|
|
|
|
|
cursor.callproc("sp_get_stage_data", [table_name, year])
|
|
|
|
|
rows = []
|
|
|
|
|
for result in cursor.stored_results():
|
|
|
|
|
rows = result.fetchall()
|
|
|
|
|
stage_data[stage_name] = pd.DataFrame(rows) if rows else pd.DataFrame()
|
|
|
|
|
cursor.close()
|
2025-11-30 16:24:49 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
columns = [
|
|
|
|
|
'gross_total_income', 'disallowance_14a',
|
2026-02-14 17:32:30 +05:30
|
|
|
'disallowance_37',
|
|
|
|
|
'gross_total_income'+'disallowance_14a'+'disallowance_37',
|
2026-02-06 15:11:58 +05:30
|
|
|
'deduction_80ia_business',
|
|
|
|
|
'deduction_80ia_misc',
|
|
|
|
|
'deduction_80ia_other',
|
|
|
|
|
'deduction_sec37_disallowance',
|
|
|
|
|
'deduction_80g', '-',
|
|
|
|
|
'net_taxable_income',
|
2026-02-14 17:32:30 +05:30
|
|
|
'-',
|
|
|
|
|
'per_tax_a',
|
|
|
|
|
'tax_a_cal',
|
|
|
|
|
'per_surcharge_a',
|
|
|
|
|
'surcharge_a_cal',
|
|
|
|
|
'per_cess_a',
|
|
|
|
|
'edu_cess_a_cal',
|
|
|
|
|
'sum_of_a',
|
|
|
|
|
'-',
|
|
|
|
|
'per_tax_b',
|
|
|
|
|
'tax_b_cal',
|
|
|
|
|
'per_surcharge_b',
|
|
|
|
|
'surcharge_b_cal',
|
|
|
|
|
'per_cess_b',
|
|
|
|
|
'edu_cess_b_cal',
|
|
|
|
|
'sum_of_b',
|
|
|
|
|
'-',
|
|
|
|
|
'tax_payable',
|
|
|
|
|
'total_tax_payable',
|
|
|
|
|
'opening_balance',
|
2026-02-06 15:11:58 +05:30
|
|
|
'mat_credit_created',
|
|
|
|
|
'mat_credit_utilized',
|
2026-02-14 17:32:30 +05:30
|
|
|
'closing_balance',
|
2026-02-06 15:11:58 +05:30
|
|
|
'interest_234c', 'total_tax',
|
|
|
|
|
'-', 'advance_tax', 'tds',
|
|
|
|
|
'tcs', 'sat',
|
|
|
|
|
'tax_on_assessment',
|
2026-02-14 17:32:30 +05:30
|
|
|
'refund',
|
|
|
|
|
'interest_244a_per143',
|
|
|
|
|
'refund_received',
|
|
|
|
|
'balance_receivable',
|
|
|
|
|
'Remarks'
|
2026-02-06 15:11:58 +05:30
|
|
|
]
|
2025-11-30 16:24:49 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
particulars = [
|
|
|
|
|
"Gross Total Income", "Add: Disallowance u/s 14A",
|
|
|
|
|
"Add: Disallowance u/s 37", "GTI as per",
|
|
|
|
|
"Less: Deduction u/s 80IA - On Business Income",
|
|
|
|
|
"- On Misc Receipts", "- On Other",
|
|
|
|
|
"- On Sec 37 Disallowance",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Less: Deduction u/s 80G", "-",
|
|
|
|
|
"Net Taxable Income", "-",
|
|
|
|
|
"Per% Tax @(A)",
|
|
|
|
|
"Tax cal(A)",
|
|
|
|
|
"Per% surcharge @(A)",
|
|
|
|
|
"Surcharge cal (A)",
|
|
|
|
|
"Per% cess(A)",
|
|
|
|
|
"Edu cess cal(A)",
|
|
|
|
|
"Sum of tax_cal(A)",
|
|
|
|
|
"-",
|
|
|
|
|
"Per% Tax @(B)",
|
|
|
|
|
"Tax cal(B)",
|
|
|
|
|
"Per% surcharge @(B)",
|
|
|
|
|
"Surcharge cal (B)",
|
|
|
|
|
"Per% cess(B)",
|
|
|
|
|
"Edu cess cal(B)",
|
|
|
|
|
"Sum of tax_cal(B)",
|
|
|
|
|
"-",
|
|
|
|
|
"Tax Payable",
|
|
|
|
|
"Total Tax Payable",
|
|
|
|
|
"Opening Balance:",
|
2026-02-06 15:11:58 +05:30
|
|
|
"Add: MAT Credit Created",
|
|
|
|
|
"Less: MAT Credit Utilized",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Closing Balance",
|
2026-02-06 15:11:58 +05:30
|
|
|
"Add: Interest u/s 234C",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Total Tax", "-",
|
2026-02-06 15:11:58 +05:30
|
|
|
"Advance Tax", "TDS", "TCS", "SAT",
|
|
|
|
|
"Tax on Regular Assessment",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Refund", "Add : Interest u/s 244A as per 143",
|
|
|
|
|
"Less : Refund Received on:","Balance Receivable","Remarks"
|
2026-02-06 15:11:58 +05:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def safe_get(df, col):
|
|
|
|
|
return df[col].values[0] if col in df.columns and not df.empty else 0
|
|
|
|
|
|
|
|
|
|
preview = []
|
|
|
|
|
for i, part in enumerate(particulars):
|
|
|
|
|
preview.append({
|
|
|
|
|
"Particular": part,
|
|
|
|
|
"ITR": safe_get(stage_data['ITR'], columns[i]),
|
|
|
|
|
"AO": safe_get(stage_data['AO'], columns[i]),
|
|
|
|
|
"CIT": safe_get(stage_data['CIT'], columns[i]),
|
|
|
|
|
"ITAT": safe_get(stage_data['ITAT'], columns[i]),
|
|
|
|
|
})
|
2025-11-30 16:24:49 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
return jsonify(preview)
|
|
|
|
|
|
|
|
|
|
finally:
|
2025-11-30 16:24:49 +05:30
|
|
|
connection.close()
|
2025-12-02 15:38:19 +05:30
|
|
|
|
|
|
|
|
def Summary_report(self, request):
|
|
|
|
|
dbconfig = DBConfig()
|
|
|
|
|
connection = dbconfig.get_db_connection()
|
|
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
year_raw = request.args.get('year')
|
|
|
|
|
|
|
|
|
|
# Safely parse year to int
|
|
|
|
|
try:
|
|
|
|
|
year = int(year_raw)
|
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
|
year = None
|
2026-01-07 00:49:42 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
if not year:
|
2025-12-02 15:38:19 +05:30
|
|
|
yearGetter = YearGet()
|
|
|
|
|
allYears = yearGetter.get_year_by_model("AllYearsInAllModel")
|
|
|
|
|
yearGetter.close()
|
2026-02-06 15:11:58 +05:30
|
|
|
|
2026-01-07 00:49:42 +05:30
|
|
|
return render_template(
|
|
|
|
|
'summary_reports.html',
|
|
|
|
|
years=allYears,
|
|
|
|
|
message="Please select a valid year to download."
|
|
|
|
|
)
|
2025-12-02 15:38:19 +05:30
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
stages = {
|
|
|
|
|
"ITR": "itr",
|
|
|
|
|
"AO": "ao",
|
|
|
|
|
"CIT": "cit",
|
|
|
|
|
"ITAT": "itat",
|
|
|
|
|
}
|
2026-01-07 00:49:42 +05:30
|
|
|
|
2025-12-02 15:38:19 +05:30
|
|
|
stage_data = {}
|
|
|
|
|
|
|
|
|
|
for stage_name, table_name in stages.items():
|
|
|
|
|
cursor = connection.cursor(dictionary=True)
|
|
|
|
|
cursor.callproc("sp_get_stage_data", [table_name, year])
|
2026-01-07 00:49:42 +05:30
|
|
|
rows = []
|
2025-12-02 15:38:19 +05:30
|
|
|
for result in cursor.stored_results():
|
|
|
|
|
rows = result.fetchall()
|
2026-01-07 00:49:42 +05:30
|
|
|
stage_data[stage_name] = pd.DataFrame(rows) if rows else pd.DataFrame()
|
2025-12-02 15:38:19 +05:30
|
|
|
cursor.close()
|
|
|
|
|
|
|
|
|
|
def safe_get(df, col):
|
|
|
|
|
return df[col].values[0] if col in df.columns and not df.empty else "-"
|
|
|
|
|
|
|
|
|
|
particulars = [
|
2026-02-06 15:11:58 +05:30
|
|
|
"Gross Total Income", "Add: Disallowance u/s 14A",
|
|
|
|
|
"Add: Disallowance u/s 37", "GTI as per",
|
|
|
|
|
"Less: Deduction u/s 80IA - On Business Income",
|
|
|
|
|
"- On Misc Receipts", "- On Other",
|
|
|
|
|
"- On Sec 37 Disallowance",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Less: Deduction u/s 80G", "-",
|
|
|
|
|
"Net Taxable Income", "-",
|
|
|
|
|
"Per% Tax @(A)",
|
|
|
|
|
"Tax cal(A)",
|
|
|
|
|
"Per% surcharge @(A)",
|
|
|
|
|
"Surcharge cal (A)",
|
|
|
|
|
"Per% cess(A)",
|
|
|
|
|
"Edu cess cal(A)",
|
|
|
|
|
"Sum of tax_cal(A)",
|
|
|
|
|
"-",
|
|
|
|
|
"Per% Tax @(B)",
|
|
|
|
|
"Tax cal(B)",
|
|
|
|
|
"Per% surcharge @(B)",
|
|
|
|
|
"Surcharge cal (B)",
|
|
|
|
|
"Per% cess(B)",
|
|
|
|
|
"Edu cess cal(B)",
|
|
|
|
|
"Sum of tax_cal(B)",
|
|
|
|
|
"-",
|
|
|
|
|
"Tax Payable",
|
|
|
|
|
"Total Tax Payable",
|
|
|
|
|
"Opening Balance",
|
2026-02-06 15:11:58 +05:30
|
|
|
"Add: MAT Credit Created",
|
|
|
|
|
"Less: MAT Credit Utilized",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Closing Balance",
|
2026-02-06 15:11:58 +05:30
|
|
|
"Add: Interest u/s 234C",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Total Tax", "-",
|
2026-01-07 00:49:42 +05:30
|
|
|
"Advance Tax", "TDS", "TCS", "SAT",
|
2026-02-06 15:11:58 +05:30
|
|
|
"Tax on Regular Assessment",
|
2026-02-14 17:32:30 +05:30
|
|
|
"Refund", "Add : Interest u/s 244A as per 143",
|
|
|
|
|
"Less : Refund Received on","Balance Receivable","Remarks"
|
2025-12-02 15:38:19 +05:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
columns = [
|
2026-02-06 15:11:58 +05:30
|
|
|
'gross_total_income', 'disallowance_14a',
|
2026-02-14 17:32:30 +05:30
|
|
|
'disallowance_37',
|
|
|
|
|
'gross_total_income'+'disallowance_14a'+'disallowance_37',
|
2026-02-06 15:11:58 +05:30
|
|
|
'deduction_80ia_business',
|
|
|
|
|
'deduction_80ia_misc',
|
|
|
|
|
'deduction_80ia_other',
|
|
|
|
|
'deduction_sec37_disallowance',
|
|
|
|
|
'deduction_80g', '-',
|
|
|
|
|
'net_taxable_income',
|
2026-02-14 17:32:30 +05:30
|
|
|
'-',
|
|
|
|
|
'per_tax_a',
|
|
|
|
|
'tax_a_cal',
|
|
|
|
|
'per_surcharge_a',
|
|
|
|
|
'surcharge_a_cal',
|
|
|
|
|
'per_cess_a',
|
|
|
|
|
'edu_cess_a_cal',
|
|
|
|
|
'sum_of_a',
|
|
|
|
|
'-',
|
|
|
|
|
'per_tax_b',
|
|
|
|
|
'tax_b_cal',
|
|
|
|
|
'per_surcharge_b',
|
|
|
|
|
'surcharge_b_cal',
|
|
|
|
|
'per_cess_b',
|
|
|
|
|
'edu_cess_b_cal',
|
|
|
|
|
'sum_of_b',
|
|
|
|
|
'-',
|
|
|
|
|
'tax_payable',
|
|
|
|
|
'total_tax_payable',
|
|
|
|
|
'opening_balance',
|
2026-02-06 15:11:58 +05:30
|
|
|
'mat_credit_created',
|
|
|
|
|
'mat_credit_utilized',
|
2026-02-14 17:32:30 +05:30
|
|
|
'closing_balance',
|
2026-02-06 15:11:58 +05:30
|
|
|
'interest_234c', 'total_tax',
|
|
|
|
|
'-', 'advance_tax', 'tds',
|
|
|
|
|
'tcs', 'sat',
|
|
|
|
|
'tax_on_assessment',
|
2026-02-14 17:32:30 +05:30
|
|
|
'refund',
|
|
|
|
|
'interest_244a_per143',
|
|
|
|
|
'refund_received',
|
|
|
|
|
'balance_receivable',
|
|
|
|
|
'Remarks'
|
2025-12-02 15:38:19 +05:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
"Particulars": particulars,
|
|
|
|
|
"ITR": [safe_get(stage_data['ITR'], col) for col in columns],
|
|
|
|
|
"AO": [safe_get(stage_data['AO'], col) for col in columns],
|
|
|
|
|
"CIT": [safe_get(stage_data['CIT'], col) for col in columns],
|
|
|
|
|
"ITAT": [safe_get(stage_data['ITAT'], col) for col in columns],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
df = pd.DataFrame(data)
|
|
|
|
|
|
|
|
|
|
output = io.BytesIO()
|
2026-02-06 15:11:58 +05:30
|
|
|
|
2025-12-02 15:38:19 +05:30
|
|
|
with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
|
2026-02-06 15:11:58 +05:30
|
|
|
sheet_name = f"AY {year}-{year + 1}"
|
2026-01-07 00:49:42 +05:30
|
|
|
df.to_excel(writer, index=False, sheet_name=sheet_name, startrow=2)
|
|
|
|
|
|
2025-12-02 15:38:19 +05:30
|
|
|
workbook = writer.book
|
2026-01-07 00:49:42 +05:30
|
|
|
worksheet = writer.sheets[sheet_name]
|
|
|
|
|
|
2026-02-17 16:54:43 +05:30
|
|
|
title = workbook.add_format({'bold': True,'font_size': 14,'align': 'center'})
|
|
|
|
|
worksheet.merge_range(0, 0, 0, len(df.columns) - 1,"Laxmi Civil Engineering Services Pvt Ltd",title)
|
|
|
|
|
header = workbook.add_format({'bold': True,'align': 'center', 'bg_color': '#007bff','font_color': 'white','border': 1 })
|
2026-02-06 15:11:58 +05:30
|
|
|
cell = workbook.add_format({'border': 1})
|
2025-12-02 15:38:19 +05:30
|
|
|
|
|
|
|
|
for col_num, col_name in enumerate(df.columns):
|
2026-01-07 00:49:42 +05:30
|
|
|
worksheet.write(2, col_num, col_name, header)
|
2026-02-06 15:11:58 +05:30
|
|
|
worksheet.set_column(col_num, col_num, 25)
|
2025-12-02 15:38:19 +05:30
|
|
|
|
2026-02-06 15:11:58 +05:30
|
|
|
for row in range(len(df)):
|
2025-12-02 15:38:19 +05:30
|
|
|
for col in range(len(df.columns)):
|
2026-02-06 15:11:58 +05:30
|
|
|
worksheet.write(row + 3, col, df.iloc[row, col], cell)
|
2026-01-07 00:49:42 +05:30
|
|
|
|
|
|
|
|
worksheet.freeze_panes(3, 1)
|
2025-12-02 15:38:19 +05:30
|
|
|
|
|
|
|
|
output.seek(0)
|
2026-01-07 00:49:42 +05:30
|
|
|
|
2025-12-02 15:38:19 +05:30
|
|
|
return send_file(
|
|
|
|
|
output,
|
2026-01-07 00:49:42 +05:30
|
|
|
download_name=f"AY{year}-{year + 1}_Summary_Report.xlsx",
|
2025-12-02 15:38:19 +05:30
|
|
|
as_attachment=True,
|
|
|
|
|
mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
finally:
|
2026-02-14 17:32:30 +05:30
|
|
|
connection.close()
|