from flask import ( render_template, request, send_file, jsonify ) from werkzeug.utils import secure_filename import pandas as pd import os import io from AppCode.Config import DBConfig from AppCode.FileHandler import FileHandler from AppCode.YearGet import YearGet class DocumentHandler: def __init__(self): self.years = [] self.documents = [] self.isSuccess = False self.resultMessage = "" # ========================= # Utility: Parse Year # ========================= def parse_year(self, year_value): """ Accepts: - '2026' - 'AY 2026-2027' Returns: - 2026 (int) """ 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 # ========================= def View(self, request): year_raw = request.args.get('year', '') stage = request.args.get('stage', '') year = self.parse_year(year_raw) dbconfig = DBConfig() connection = dbconfig.get_db_connection() if not connection: self.isSuccess = False return cursor = connection.cursor(dictionary=True) cursor.callproc("GetDocuments", [year, stage]) for result in cursor.stored_results(): self.documents = result.fetchall() break cursor.callproc("GetYear") for result in cursor.stored_results(): year_rows = result.fetchall() break self.years = [row['year'] for row in year_rows] cursor.close() connection.close() self.isSuccess = True # ========================= # Upload Documents # ========================= def Upload(self, request): dbconfig = DBConfig() connection = dbconfig.get_db_connection() 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) cursor.callproc( 'InsertDocument', [filename, filepath, extension, year, stage] ) connection.commit() cursor.close() connection.close() # ========================= # Summary Preview (JSON) # ========================= def Summary_preview(self, request): """ Returns JSON preview of summary report for selected year. """ 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 = {} 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() columns = [ 'gross_total_income', 'disallowance_14a', 'disallowance_37', '-', 'deduction_80ia_business', 'deduction_80ia_misc', 'deduction_80ia_other', 'deduction_sec37_disallowance', 'deduction_80g', '-', 'net_taxable_income', 'tax_30_percent', 'tax_book_profit_18_5', 'tax_payable', 'surcharge', 'edu_cess', 'total_tax_payable', 'mat_credit_created', 'mat_credit_utilized', 'interest_234c', 'total_tax', '-', 'advance_tax', 'tds', 'tcs', 'sat', 'tax_on_assessment', 'refund', 'Remarks' ] 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", "Less: Deduction u/s 80G", " ", "Net Taxable Income", "Tax @ 30%", "Tax @ 18.5% on Book Profit", "Tax Payable", "Surcharge @ %", "Education Cess @ %", "Total Tax Payable", "Add: MAT Credit Created", "Less: MAT Credit Utilized", "Add: Interest u/s 234C", "Total Tax", " ", "Advance Tax", "TDS", "TCS", "SAT", "Tax on Regular Assessment", "Refund", "Remarks" ] 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]), }) return jsonify(preview) finally: connection.close() def Summary_report(self, request): dbconfig = DBConfig() connection = dbconfig.get_db_connection() year_raw = request.args.get('year') # Safely parse year to int try: year = int(year_raw) except (TypeError, ValueError): year = None if not year: yearGetter = YearGet() allYears = yearGetter.get_year_by_model("AllYearsInAllModel") yearGetter.close() return render_template( 'summary_reports.html', years=allYears, message="Please select a valid year to download." ) try: stages = { "ITR": "itr", "AO": "ao", "CIT": "cit", "ITAT": "itat", } stage_data = {} 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() def safe_get(df, col): return df[col].values[0] if col in df.columns and not df.empty else "-" 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", "Less: Deduction u/s 80G", " ", "Net Taxable Income", "Tax @ 30%", "Tax @ 18.5% on Book Profit", "Tax Payable", "Surcharge @ %", "Education Cess @ %", "Total Tax Payable", "Add: MAT Credit Created", "Less: MAT Credit Utilized", "Add: Interest u/s 234C", "Total Tax", " ", "Advance Tax", "TDS", "TCS", "SAT", "Tax on Regular Assessment", "Refund", "Remarks" ] columns = [ 'gross_total_income', 'disallowance_14a', 'disallowance_37', '-', 'deduction_80ia_business', 'deduction_80ia_misc', 'deduction_80ia_other', 'deduction_sec37_disallowance', 'deduction_80g', '-', 'net_taxable_income', 'tax_30_percent', 'tax_book_profit_18_5', 'tax_payable', 'surcharge', 'edu_cess', 'total_tax_payable', 'mat_credit_created', 'mat_credit_utilized', 'interest_234c', 'total_tax', '-', 'advance_tax', 'tds', 'tcs', 'sat', 'tax_on_assessment', 'refund', 'Remarks' ] 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() with pd.ExcelWriter(output, engine='xlsxwriter') as writer: sheet_name = f"AY {year}-{year + 1}" df.to_excel(writer, index=False, sheet_name=sheet_name, startrow=2) workbook = writer.book worksheet = writer.sheets[sheet_name] 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 }) cell = workbook.add_format({'border': 1}) for col_num, col_name in enumerate(df.columns): worksheet.write(2, col_num, col_name, header) worksheet.set_column(col_num, col_num, 25) for row in range(len(df)): for col in range(len(df.columns)): worksheet.write(row + 3, col, df.iloc[row, col], cell) worksheet.freeze_panes(3, 1) output.seek(0) return send_file( output, download_name=f"AY{year}-{year + 1}_Summary_Report.xlsx", as_attachment=True, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) finally: connection.close()