new fom of MAT credit add in table formate and update show

This commit is contained in:
2026-01-07 16:26:59 +05:30
parent 9c2486fdd6
commit 68511b7558
6 changed files with 595 additions and 6 deletions

123
AppCode/MatCreditHandler.py Normal file
View File

@@ -0,0 +1,123 @@
from AppCode.Config import DBConfig
import mysql.connector
class MatCreditHandler:
def __init__(self):
db = DBConfig()
self.conn = db.get_db_connection()
self.cursor = self.conn.cursor(dictionary=True)
@staticmethod
def fetch_all():
conn = DBConfig.get_db_connection()
cur = conn.cursor(dictionary=True)
try:
# Stored Procedure returns TWO result sets
cur.callproc("GetMatCedit")
result_sets = cur.stored_results()
mat_rows = next(result_sets).fetchall()
utilization_rows = next(result_sets).fetchall()
return mat_rows, utilization_rows
finally:
cur.close()
conn.close()
@staticmethod
def save_single(data):
conn = DBConfig.get_db_connection()
cur = conn.cursor()
try:
cur.execute(
"SELECT id FROM mat_credit WHERE financial_year=%s",
(data["financial_year"],)
)
row = cur.fetchone()
if row:
mat_id = row[0]
cur.execute("""
UPDATE mat_credit
SET mat_credit=%s, balance=%s
WHERE id=%s
""", (data["mat_credit"], data["balance"], mat_id))
cur.execute(
"DELETE FROM mat_utilization WHERE mat_credit_id=%s",
(mat_id,)
)
else:
cur.execute("""
INSERT INTO mat_credit (financial_year, mat_credit, balance)
VALUES (%s,%s,%s)
""", (data["financial_year"], data["mat_credit"], data["balance"]))
mat_id = cur.lastrowid
for u in data["utilization"]:
cur.execute("""
INSERT INTO mat_utilization
(mat_credit_id, utilized_year, utilized_amount)
VALUES (%s,%s,%s)
""", (mat_id, u["year"], u["amount"]))
conn.commit()
except Exception:
conn.rollback()
raise
finally:
cur.close()
conn.close()
@staticmethod
def save_bulk(rows):
conn = DBConfig.get_db_connection()
cur = conn.cursor()
skipped = []
try:
for row in rows:
cur.execute(
"SELECT id FROM mat_credit WHERE financial_year=%s",
(row["financial_year"],)
)
if cur.fetchone():
skipped.append(row["financial_year"])
continue
cur.execute("""
INSERT INTO mat_credit (financial_year, mat_credit, balance)
VALUES (%s,%s,%s)
""", (row["financial_year"], row["mat_credit"], row["balance"]))
mat_id = cur.lastrowid
for u in row["utilization"]:
cur.execute("""
INSERT INTO mat_utilization
(mat_credit_id, utilized_year, utilized_amount)
VALUES (%s,%s,%s)
""", (mat_id, u["year"], u["amount"]))
conn.commit()
return skipped
except Exception:
conn.rollback()
raise
finally:
cur.close()
conn.close()

58
main.py
View File

@@ -1,11 +1,11 @@
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, abort, flash,send_file
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, abort, flash,send_file ,jsonify
import os
import pandas as pd
import pymysql
import io
import mysql.connector
from werkzeug.utils import secure_filename
from datetime import datetime
from AppCode.Config import DBConfig
from AppCode.FileHandler import FileHandler
@@ -16,6 +16,8 @@ from AppCode.CITHandler import CITHandler
from AppCode.ITATHandler import ITATHandler
from AppCode.YearGet import YearGet
from AppCode.LoginAuth import LoginAuth
from AppCode.MatCreditHandler import MatCreditHandler
# Server
@@ -433,8 +435,8 @@ def check_year():
conn = DBConfig.get_db_connection()
cursor = conn.cursor()
query = f"SELECT COUNT(*) FROM {table_name} WHERE year = %s"
cursor.execute(query, (year,))
sqlstr = f"SELECT COUNT(*) FROM {table_name} WHERE year = %s"
cursor.execute(sqlstr, (year,))
result = cursor.fetchone()[0]
cursor.close()
@@ -442,6 +444,54 @@ def check_year():
return {"exists": result > 0}
# new new
# Mat credit from
@app.route("/mat_credit", methods=["GET"])
def mat_credit():
mat= MatCreditHandler()
mat_rows, utilization_rows = mat.fetch_all()
utilization_map = {}
all_years = set()
for u in utilization_rows:
all_years.add(u["utilized_year"])
utilization_map.setdefault(
u["mat_credit_id"], {}
)[u["utilized_year"]] = u["utilized_amount"]
return render_template(
"mat_credit.html",
mat_rows=mat_rows,
utilization_map=utilization_map,
added_years=sorted(all_years)
)
# save mat credit row data
@app.route("/save_mat_row", methods=["POST"])
def save_mat_row():
mat= MatCreditHandler()
try:
mat.save_single(request.json)
return jsonify({"message": "Row saved successfully"})
except Exception as e:
return jsonify({"error": str(e)}), 500
# save mat credit bulk data
@app.route("/save_mat_all", methods=["POST"])
def save_mat_all():
mat= MatCreditHandler()
try:
skipped = mat.save_bulk(request.json)
return jsonify({"message": "Saved successfully", "skipped": skipped})
except Exception as e:
return jsonify({"error": str(e)}), 500
# run
if __name__ == '__main__':

146
static/css/mat_credit.css Normal file
View File

@@ -0,0 +1,146 @@
/* ===== CONTAINER ===== */
.container {
max-width: 1200px;
margin: 30px auto;
padding: 25px;
background: #ffffff;
border-radius: 10px;
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
}
/* ===== TABLE ===== */
#matTable {
width: 100%;
border-collapse: collapse;
font-size: 14px;
text-align: center;
}
/* ===== HEADER ===== */
#matTable thead th {
background: linear-gradient(135deg, #0d6efd, #0a58ca);
color: #ffffff;
padding: 12px 8px;
border: 1px solid #0a58ca;
font-weight: 600;
white-space: nowrap;
}
/* ===== BODY CELLS ===== */
#matTable tbody td {
padding: 8px;
border: 1px solid #dcdcdc;
background-color: #ffffff;
}
/* ===== FY COLUMN ===== */
#matTable tbody td:first-child {
font-weight: 600;
background-color: #f5f8ff;
}
/* ===== INPUT FIELDS ===== */
#matTable input {
width: 100%;
padding: 6px 6px;
border: 1px solid #cfd8dc;
border-radius: 4px;
font-size: 13px;
text-align: right;
box-sizing: border-box;
}
/* TEXT INPUT (Unutilized) */
#matTable input[type="text"] {
text-align: center;
}
/* INPUT FOCUS */
#matTable input:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.15);
outline: none;
}
/* ===== ACTION BUTTON ===== */
#matTable button {
background-color: #198754;
border: none;
color: white;
padding: 6px 14px;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: 0.2s;
}
#matTable button:hover {
background-color: #157347;
}
/* ===== SAVE ALL BUTTON ===== */
button[onclick="saveAll()"] {
display: block;
width: 300px;
margin: 25px auto 0;
background-color: #28a745;
color: #fff;
font-size: 16px;
font-weight: 600;
padding: 12px;
border-radius: 8px;
border: none;
cursor: pointer;
}
button[onclick="saveAll()"]:hover {
background-color: #218838;
}
/* ===== ROW HOVER ===== */
#matTable tbody tr:hover {
background-color: #f1f6ff;
}
/* ===== ERROR HIGHLIGHT ===== */
.input-error {
border-color: #dc3545 !important;
background-color: #fff5f5;
}
/* ===== SUCCESS HIGHLIGHT ===== */
.row-saved {
background-color: #e9f7ef !important;
}
/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
#matTable {
font-size: 12px;
}
#matTable thead {
display: none;
}
#matTable tbody tr {
display: block;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 6px;
padding: 10px;
}
#matTable tbody td {
display: flex;
justify-content: space-between;
padding: 6px 8px;
border: none;
}
#matTable tbody td::before {
content: attr(data-label);
font-weight: 600;
color: #0d6efd;
}
}

184
static/js/mat_credit.js Normal file
View File

@@ -0,0 +1,184 @@
/* =====================================================
GLOBAL STATE
===================================================== */
let addedYears = [];
/* =====================================================
INITIALIZE YEARS FROM DATABASE HEADERS
===================================================== */
document.addEventListener("DOMContentLoaded", () => {
const headers = document.querySelectorAll("#tableHeader th");
headers.forEach(th => {
if (th.innerText.startsWith("Utilized")) {
const year = th.innerText.replace("Utilized", "").trim();
if (!addedYears.includes(year)) {
addedYears.push(year);
}
}
});
});
/* =====================================================
ADD YEAR COLUMN (DYNAMIC)
===================================================== */
function addYearColumn() {
const yearSelect = document.getElementById("yearSelect");
const year = yearSelect.value;
if (!year) {
alert("Please select AY");
return;
}
if (addedYears.includes(year)) {
alert("This AY is already added");
return;
}
addedYears.push(year);
// Add header column
const headerRow = document.getElementById("tableHeader");
const th = document.createElement("th");
th.innerText = "Utilized " + year;
// Insert before Balance column
headerRow.insertBefore(th, headerRow.children[headerRow.children.length - 2]);
// Add input cells to all existing rows
document.querySelectorAll("#matTable tbody tr").forEach(tr => {
const td = document.createElement("td");
td.innerHTML = `<input type="number">`;
tr.insertBefore(td, tr.children[tr.children.length - 2]);
});
}
/* =====================================================
ADD NEW ROW
===================================================== */
function addRow() {
const tbody = document.querySelector("#matTable tbody");
const tr = document.createElement("tr");
let utilizedCols = "";
addedYears.forEach(() => {
utilizedCols += `<td><input type="number"></td>`;
});
tr.innerHTML = `
<td contenteditable="true"></td>
<td><input></td>
${utilizedCols}
<td><input></td>
<td>
<button onclick="saveRow(this)">Save</button>
</td>
`;
tbody.appendChild(tr);
}
/* =====================================================
SAVE SINGLE ROW
===================================================== */
function saveRow(btn) {
const tr = btn.closest("tr");
const inputs = tr.querySelectorAll("input");
const financialYear = tr.children[0].innerText.trim();
if (!financialYear) {
alert("AY is required");
return;
}
let payload = {
financial_year: financialYear,
mat_credit: inputs[0].value || 0,
balance: inputs[inputs.length - 1].value || 0,
utilization: []
};
addedYears.forEach((year, i) => {
const value = inputs[i + 1].value;
if (value) {
payload.utilization.push({
year: year,
amount: value
});
}
});
fetch("/save_mat_row", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
.then(res => res.json().then(j => ({ ok: res.ok, data: j })))
.then(res => {
if (res.ok) {
alert("✅ " + res.data.message);
} else {
alert("⚠️ " + res.data.error);
}
})
.catch(() => {
alert("Server error");
});
}
/* =====================================================
SAVE ALL ROWS
===================================================== */
function saveAll() {
let rows = [];
document.querySelectorAll("#matTable tbody tr").forEach(tr => {
const inputs = tr.querySelectorAll("input");
const financialYear = tr.children[0].innerText.trim();
if (!financialYear) return;
let row = {
financial_year: financialYear,
mat_credit: inputs[0].value || 0,
balance: inputs[inputs.length - 1].value || 0,
utilization: []
};
addedYears.forEach((year, i) => {
const value = inputs[i + 1].value;
if (value) {
row.utilization.push({
year: year,
amount: value
});
}
});
rows.push(row);
});
if (!rows.length) {
alert("No data to save");
return;
}
fetch("/save_mat_all", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(rows)
})
.then(res => res.json())
.then(res => {
let msg = "✅ " + res.message;
if (res.skipped && res.skipped.length) {
msg += "\n\nSkipped AY:\n" + res.skipped.join(", ");
}
alert(msg);
})
.catch(() => {
alert("Server error");
});
}

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{% block title %}Income Tax Utilities{% endblock %}</title>
@@ -23,7 +24,9 @@
<!-- SIDEBAR -->
<div class="sidebar hide" id="sidebar">
<a href="{{ url_for('index') }}"><h2>Dashboard</h2></a>
<a href="{{ url_for('index') }}">
<h2>Dashboard</h2>
</a>
<div class="menu-btn" onclick="toggleMenu('itrMenu')">ITR ▼</div>
<div class="submenu" id="itrMenu">
@@ -57,6 +60,14 @@
<a href="{{ url_for('summary_report') }}">📝 Summary Report</a>
</div>
<div class="menu-btn" onclick="toggleMenu('matCredit')">Mat Credit ▼</div>
<div class="submenu" id="matCredit">
<a href="{{ url_for('mat_credit') }}">📄 Mat Credit from</a>
<!-- <a href="{{ url_for('view_documents') }}">📄 Documents</a> -->
</div>
<!-- Logout at bottom -->
<a href="{{ url_for('auth.logout') }}" class="sidebar-logout">
<img src="{{ url_for('static', filename='images/logout_icon.png') }}" class="logout-icon" alt="Logout" />
@@ -74,4 +85,5 @@
<!-- Extra JS for child pages -->
{% block extra_js %}{% endblock %}
</body>
</html>
</html>

74
templates/mat_credit.html Normal file
View File

@@ -0,0 +1,74 @@
{% extends "base.html" %}
{% block title %}MAT Credit{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/mat_credit.css') }}">
{% endblock %}
{% block content %}
<div class="container">
<div class="container">
<!-- YEAR ADD CONTROLS -->
<div class="year-controls">
<select id="yearSelect">
<option value="">-- Select AY --</option>
{% for y in range(2001, 2026) %}
<option value="{{ y }}-{{ y+1 }}">{{ y }}-{{ y+1 }}</option>
{% endfor %}
</select>
<button onclick="addYearColumn()"> Add Year</button>
</div>
<!-- SCROLL WRAPPER -->
<div class="table-wrapper">
<table id="matTable">
<thead>
<tr id="tableHeader">
<th>AY</th>
<th>MAT Credit</th>
{% for y in added_years %}
<th>Utilized {{ y }}</th>
{% endfor %}
<th>Balance</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for row in mat_rows %}
<tr>
<td contenteditable="true">{{ row.financial_year }}</td>
<td><input value="{{ row.mat_credit }}"></td>
{% for y in added_years %}
<td>
<input value="{{ utilization_map.get(row.id, {}).get(y, '') }}">
</td>
{% endfor %}
<td><input value="{{ row.balance }}"></td>
<td><button onclick="saveRow(this)">Save</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<br>
<button onclick="addRow()"> Add Row</button>
<button onclick="saveAll()">💾 Save All</button>
</div>
</div>
{% block extra_js %}
<script src="{{ url_for('static', filename='js/mat_credit.js') }}"></script>
{% endblock %}
{% endblock %}