29 Commits

Author SHA1 Message Date
Swapnil9693
d84ba520cf GST Release Add, Edit, Delete Working 2026-03-24 18:46:07 +05:30
0aeaf775dd Merge pull request 'pankaj-dev - all done' (#18) from pankaj-dev into main
Reviewed-on: #18
2026-03-24 11:27:55 +00:00
88e8771b51 add log file function 2026-03-24 16:56:23 +05:30
6c74b5d3bf Merge branch 'main' of https://gitea.lcepl.org/pjpatil12/Payment_Reconciliation into pankaj-dev 2026-03-24 16:29:32 +05:30
47ba78d72c search on table 2026-03-24 16:29:18 +05:30
1946a98d59 Merge pull request 'Invoice Add Edit and Delete Done' (#17) from swapnil-dev into main
Reviewed-on: #17
2026-03-24 10:51:43 +00:00
8f35e08429 Merge pull request 'GST Release Display Chages' (#16) from laxmi-dev into main
Reviewed-on: #16
Reviewed-by: Pankaj J Patil <pankajjpatil2001@gmail.com>
2026-03-24 10:51:05 +00:00
82bedc3117 Update activity.log 2026-03-24 10:49:06 +00:00
cb68e454bc Update .env 2026-03-24 10:47:05 +00:00
Swapnil9693
91b078a0c3 edit_invoice html removed 2026-03-24 16:16:43 +05:30
Swapnil9693
eb46929893 New invoice commit 2026-03-24 16:13:04 +05:30
675301df7f GST Release Display Chages 2026-03-24 16:08:37 +05:30
Swapnil9693
14e799a1d4 Inoive add update delete messages shown properly 2026-03-24 15:51:55 +05:30
8ab1b69033 Merge pull request 'hold type updated , delete done by prajkta' (#13) from pankaj-dev into main
Reviewed-on: #13
2026-03-24 09:51:42 +00:00
dbeec9962d hold type updated , delete done by prajkta 2026-03-24 15:19:38 +05:30
8750f268db Merge pull request 'Village Add, Edit and Delete Messages done' (#12) from swapnil-dev into main
Reviewed-on: #12
2026-03-24 09:47:16 +00:00
Swapnil9693
b78526ad9f Village Add, Edit and Delete Messages done 2026-03-24 13:41:00 +05:30
7146391c18 Merge pull request 'delete payment code updated by prajakta' (#11) from pankaj-dev into main
Reviewed-on: #11
2026-03-24 07:21:43 +00:00
94b5563d15 delete payment code updated by prajakta 2026-03-24 12:50:44 +05:30
937018dc16 Merge pull request 'add item crude' (#10) from pankaj-dev into main
Reviewed-on: #10
2026-03-24 07:07:31 +00:00
eda238c235 add itemcrude 2026-03-24 12:36:15 +05:30
f184d6cecc Merge pull request 'update block redirect added' (#9) from pankaj-dev into main
Reviewed-on: #9
2026-03-24 06:26:39 +00:00
e7646eee76 Merge pull request 'Village module delete issue resolved and also message shown in alert' (#8) from swapnil-dev into main
Reviewed-on: #8
2026-03-24 06:25:19 +00:00
64ca39944b update block redirect added 2026-03-24 11:47:57 +05:30
Swapnil9693
6b4beb5af8 Village module delete issue resolved and also message shown on alert 2026-03-24 11:43:16 +05:30
Swapnil9693
d092eb0527 Sync .env and activity.log with main 2026-03-24 11:39:08 +05:30
Swapnil9693
f9e9612df5 Re-add .env and activity.log 2026-03-24 11:37:08 +05:30
Swapnil9693
46ec2c0276 Remove sensitive files (.env, activity.log) from repo 2026-03-24 11:32:00 +05:30
Swapnil9693
630ee1744f Backup Before Changes village Task 2026-03-24 11:28:14 +05:30
61 changed files with 1808 additions and 1279 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ __pycache__/
static/downloads/ static/downloads/
static/uploads/ static/uploads/

Binary file not shown.

View File

View File

View File

@@ -1,5 +1,5 @@
import config import config
from flask import Blueprint, render_template, request, redirect, url_for, jsonify from flask import Blueprint, render_template, request, redirect, url_for, jsonify, flash
from flask_login import login_required from flask_login import login_required
from model.State import State from model.State import State
@@ -74,7 +74,14 @@ def edit_block(block_id):
if request.method == 'POST': if request.method == 'POST':
block.EditBlock(request, block_id) block.EditBlock(request, block_id)
return block.resultMessage block.resultMessage
if block.resultMessage:
flash("Block updated successfully!", "success")
return redirect(url_for('block.add_block'))
else:
flash(block.resultMessage, "error")
connection = config.get_db_connection() connection = config.get_db_connection()
cursor = connection.cursor() cursor = connection.cursor()

View File

@@ -1,8 +1,8 @@
from flask import Blueprint, render_template, request, redirect, url_for # routes/gst_release_routes.py
from flask import Blueprint, render_template, request, redirect, url_for, flash
from flask_login import login_required from flask_login import login_required
from model.gst_release import GSTRelease from model.gst_release import GSTRelease
from model.Log import LogHelper from model.Log import LogHelper
from flask import flash, current_app
gst_release_bp = Blueprint('gst_release_bp', __name__) gst_release_bp = Blueprint('gst_release_bp', __name__)
gst_service = GSTRelease() gst_service = GSTRelease()
@@ -13,7 +13,7 @@ gst_service = GSTRelease()
def add_gst_release(): def add_gst_release():
if request.method == 'POST': if request.method == 'POST':
gst_service.AddGSTRelease(request) gst_service.AddGSTRelease(request)
LogHelper.log_action("Add GST Release", f"User added GST release") LogHelper.log_action("Add GST Release", "User added GST release")
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error') flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
return redirect(url_for('gst_release_bp.add_gst_release')) return redirect(url_for('gst_release_bp.add_gst_release'))
@@ -30,7 +30,7 @@ def edit_gst_release(gst_release_id):
if request.method == 'POST': if request.method == 'POST':
gst_service.EditGSTRelease(request, gst_release_id) gst_service.EditGSTRelease(request, gst_release_id)
LogHelper.log_action("Edit GST Release", f"User edited GST release") LogHelper.log_action("Edit GST Release", "User edited GST release")
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error') flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
return redirect(url_for('gst_release_bp.add_gst_release')) return redirect(url_for('gst_release_bp.add_gst_release'))
@@ -40,7 +40,7 @@ def edit_gst_release(gst_release_id):
@gst_release_bp.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST']) @gst_release_bp.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
@login_required @login_required
def delete_gst_release(gst_release_id): def delete_gst_release(gst_release_id):
gst_service.DeleteGSTRelease(gst_release_id) # remove request gst_service.DeleteGSTRelease(gst_release_id)
LogHelper.log_action("Delete GST Release", f"User deleted GST release") LogHelper.log_action("Delete GST Release", "User deleted GST release")
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error') flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
return redirect(url_for('gst_release_bp.add_gst_release')) return redirect(url_for('gst_release_bp.add_gst_release'))

View File

@@ -10,21 +10,21 @@ hold_bp = Blueprint("hold_types", __name__)
@hold_bp.route('/add_hold_type', methods=['GET','POST']) @hold_bp.route('/add_hold_type', methods=['GET','POST'])
@login_required @login_required
def add_hold_type(): def add_hold_type():
hold = HoldTypes() hold = HoldTypes()
if request.method == 'POST': if request.method == 'POST':
hold.AddHoldType(request) # ✅ hold.AddHoldType(request)
return hold.resultMessage # ✅ Always redirect to same page (NO JSON)
return redirect(url_for("hold_types.add_hold_type"))
hold_types = hold.GetAllHoldTypes() # ✅ # GET request → show data
hold_types = hold.GetAllHoldTypes()
return render_template( return render_template(
"add_hold_type.html", "add_hold_type.html",
Hold_Types_data=hold_types Hold_Types_data=hold_types
) )
# ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ---------------- # ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ----------------
@hold_bp.route('/check_hold_type', methods=['POST']) @hold_bp.route('/check_hold_type', methods=['POST'])
@login_required @login_required

View File

@@ -21,7 +21,6 @@ def activity_log():
end_date, end_date,
user_name user_name
) )
return render_template( return render_template(
"activity_log.html", "activity_log.html",
logs=filtered_logs, logs=filtered_logs,

View File

@@ -86,12 +86,12 @@ def edit_payment(payment_id):
return render_template('edit_payment.html', payment_data=payment_data) return render_template('edit_payment.html', payment_data=payment_data)
# ------------------- Delete Payment ------------------- # ------------------- Delete Payment -------------------
@payment_bp.route('/delete_payment/<int:payment_id>', methods=['POST']) @payment_bp.route('/delete_payment/<int:payment_id>', methods=['GET'])
@login_required @login_required
def delete_payment(payment_id): def delete_payment(payment_id):
success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id) success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id)
if not success: if not success:
flash("Payment not found or failed to delete", "error") flash("Payment not found or failed to delete", "error")
else: else:

View File

@@ -83,17 +83,29 @@ def check_village():
return village.CheckVillage(request=request) return village.CheckVillage(request=request)
# ------------------------- Delete Village -------------------------
@village_bp.route('/delete_village/<int:village_id>') @village_bp.route('/delete_village/<int:village_id>')
@login_required @login_required
def delete_village(village_id): def delete_village(village_id):
village = Village() village = Village()
village.DeleteVillage(request=request, village_id=village_id) village.DeleteVillage(request=request, village_id=village_id)
flash(village.resultMessage, "success" if village.isSuccess else "error") # ✅ Convert resultMessage to string if it's a Response or tuple
return redirect(url_for('village.add_village')) raw_msg = village.resultMessage
if isinstance(raw_msg, tuple):
# e.g., (<Response ...>, 200)
msg = "Village deleted successfully!"
elif hasattr(raw_msg, 'get_data'):
# Flask Response object
msg = raw_msg.get_data(as_text=True) # get raw text
else:
# fallback
msg = str(raw_msg) if raw_msg else "Village deleted successfully!"
return jsonify({
"status": "success" if village.isSuccess else "error",
"message": msg
})
# ------------------------- Edit Village ------------------------- # ------------------------- Edit Village -------------------------
@village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST']) @village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])

View File

@@ -1,4 +1,4 @@
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogData, LogHelper from model.Log import LogData, LogHelper
@@ -147,7 +147,7 @@ class Block:
self.isSuccess = block.isSuccess self.isSuccess = block.isSuccess
self.resultMessage = block.resultMessage self.resultMessage = block.resultMessage
return render_template('add_block.html') return
# ---------------------------------------------------------- # ----------------------------------------------------------
# Delete Block # Delete Block

View File

@@ -27,13 +27,27 @@ class FolderAndFile:
os.makedirs(folder, exist_ok=True) os.makedirs(folder, exist_ok=True)
return folder return folder
# ----------------------------- @staticmethod
# FILE PATH METHODS def get_logs_folder():
# ----------------------------- folder = os.path.join(current_app.root_path, "logs")
if not os.path.exists(folder):
os.makedirs(folder)
os.makedirs(folder, exist_ok=True)
return folder
# FILE PATH METHODS - download
@staticmethod @staticmethod
def get_download_path(filename): def get_download_path(filename):
return os.path.join(FolderAndFile.get_download_folder(), filename) return os.path.join(FolderAndFile.get_download_folder(), filename)
# FILE PATH METHODS - upload file
@staticmethod @staticmethod
def get_upload_path(filename): def get_upload_path(filename):
return os.path.join(FolderAndFile.get_upload_folder(), filename) return os.path.join(FolderAndFile.get_upload_folder(), filename)
# FILE PATH METHODS - activity log file
@staticmethod
def get_activity_log_path(filename):
return os.path.join(FolderAndFile.get_logs_folder(), filename)

View File

@@ -6,7 +6,6 @@ import config
import re import re
import mysql.connector import mysql.connector
# ---------------------------------------------------------- # ----------------------------------------------------------
# Mapping Class # Mapping Class
# ---------------------------------------------------------- # ----------------------------------------------------------
@@ -23,10 +22,11 @@ class itemCRUDMapping:
self.name = "Hold Type" self.name = "Hold Type"
elif itemType is ItemCRUDType.Subcontractor: elif itemType is ItemCRUDType.Subcontractor:
self.name = "Subcontractor" self.name = "Subcontractor"
elif itemType.name == "GSTRelease":
self.name = "GSTRelease"
else: else:
self.name = "Item" self.name = "Item"
# ---------------------------------------------------------- # ----------------------------------------------------------
# Generic CRUD Class # Generic CRUD Class
# ---------------------------------------------------------- # ----------------------------------------------------------
@@ -93,13 +93,47 @@ class ItemCRUD:
try: try:
# ====================================================== # ======================================================
# SUBCONTRACTOR (MULTI-FIELD) # GSTRelease MULTI-FIELD
# ====================================================== # ======================================================
if data: if self.itemCRUDType.name == "GSTRelease" and data:
# Duplicate check (PMC_No + Invoice_No)
if storedprocfetch:
cursor.callproc(storedprocfetch, (data['PMC_No'], data['Invoice_No']))
existing_item = None
for rs in cursor.stored_results():
existing_item = rs.fetchone()
if existing_item:
self.isSuccess = False
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.already_exists(self.itemCRUDMapping.name), 409
)
return
# Insert GSTRelease
cursor.callproc(storedprocadd, (
data['PMC_No'],
data['Invoice_No'],
data['Basic_Amount'],
data['Final_Amount'],
data['Total_Amount'],
data['UTR'],
data['Contractor_ID']
))
connection.commit()
self.isSuccess = True
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
)
return
# ======================================================
# SUBCONTRACTOR MULTI-FIELD
# ======================================================
if self.itemCRUDType.name == "Subcontractor" and data:
# Duplicate check
cursor.callproc(storedprocfetch, (data['Contractor_Name'],)) cursor.callproc(storedprocfetch, (data['Contractor_Name'],))
existing_item = None existing_item = None
for rs in cursor.stored_results(): for rs in cursor.stored_results():
existing_item = rs.fetchone() existing_item = rs.fetchone()
@@ -111,7 +145,6 @@ class ItemCRUD:
) )
return return
# Insert
cursor.callproc(storedprocadd, ( cursor.callproc(storedprocadd, (
data['Contractor_Name'], data['Contractor_Name'],
data['Address'], data['Address'],
@@ -123,26 +156,24 @@ class ItemCRUD:
data['GST_No'], data['GST_No'],
data['Contractor_password'] data['Contractor_password']
)) ))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200 ResponseHandler.add_success(self.itemCRUDMapping.name), 200
) )
return return
# ====================================================== # ======================================================
# NORMAL (Village / Block / State) # NORMAL SINGLE-FIELD (Village / Block / State)
# ====================================================== # ======================================================
if not re.match(RegEx.patternAlphabetOnly, childname): if not re.match(RegEx.allPattern, childname):
self.isSuccess = False self.isSuccess = False
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400 ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
) )
return return
# Duplicate check
if parentid is None: if parentid is None:
cursor.callproc(storedprocfetch, (childname,)) cursor.callproc(storedprocfetch, (childname,))
else: else:
@@ -159,17 +190,14 @@ class ItemCRUD:
) )
return return
# Insert
if parentid is None: if parentid is None:
cursor.callproc(storedprocadd, (childname,)) cursor.callproc(storedprocadd, (childname,))
else: else:
cursor.callproc(storedprocadd, (childname, parentid)) cursor.callproc(storedprocadd, (childname, parentid))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200 ResponseHandler.add_success(self.itemCRUDMapping.name), 200
) )
@@ -199,9 +227,32 @@ class ItemCRUD:
try: try:
# ====================================================== # ======================================================
# SUBCONTRACTOR (MULTI-FIELD) # GSTRelease MULTI-FIELD
# ====================================================== # ======================================================
if data: if self.itemCRUDType.name == "GSTRelease" and data:
cursor.callproc(storedprocupdate, (
data['p_pmc_no'], # PMC_No
data['p_invoice_no'], # Invoice_No
data['p_basic_amount'], # Basic_Amount
data['p_final_amount'], # Final_Amount
data['p_total_amount'], # Total_Amount
data['p_utr'], # UTR
data['p_gst_release_id']# GST_Release_Id
))
connection.commit()
self.isSuccess = True
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
)
return
# ======================================================
# SUBCONTRACTOR MULTI-FIELD
# ======================================================
if self.itemCRUDType.name == "Subcontractor" and data:
cursor.callproc(storedprocupdate, ( cursor.callproc(storedprocupdate, (
childid, childid,
data['Contractor_Name'], data['Contractor_Name'],
@@ -214,9 +265,7 @@ class ItemCRUD:
data['GST_No'], data['GST_No'],
data['Contractor_password'] data['Contractor_password']
)) ))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.update_success(self.itemCRUDMapping.name), 200 ResponseHandler.update_success(self.itemCRUDMapping.name), 200
@@ -224,9 +273,9 @@ class ItemCRUD:
return return
# ====================================================== # ======================================================
# NORMAL # NORMAL SINGLE-FIELD
# ====================================================== # ======================================================
if not re.match(RegEx.patternAlphabetOnly, childname): if not re.match(RegEx.allPattern, childname):
self.isSuccess = False self.isSuccess = False
self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message'] self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message']
return return
@@ -237,7 +286,6 @@ class ItemCRUD:
cursor.callproc(storedprocupdate, (childid, parentid, childname)) cursor.callproc(storedprocupdate, (childid, parentid, childname))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message'] self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message']
@@ -259,20 +307,15 @@ class ItemCRUD:
data = [] data = []
connection = config.get_db_connection() connection = config.get_db_connection()
if not connection: if not connection:
return [] return []
cursor = connection.cursor() cursor = connection.cursor()
try: try:
cursor.callproc(storedproc) cursor.callproc(storedproc)
for result in cursor.stored_results(): for result in cursor.stored_results():
data = result.fetchall() data = result.fetchall()
self.isSuccess = True self.isSuccess = True
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error fetching {self.itemCRUDMapping.name}: {e}") print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
self.isSuccess = False self.isSuccess = False
@@ -280,7 +323,6 @@ class ItemCRUD:
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500 ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
) )
return [] return []
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()
@@ -298,13 +340,10 @@ class ItemCRUD:
try: try:
cursor.callproc(storedproc, (id,)) cursor.callproc(storedproc, (id,))
for rs in cursor.stored_results(): for rs in cursor.stored_results():
data = rs.fetchone() data = rs.fetchone()
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error fetching {self.itemCRUDMapping.name}: {e}") print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()
@@ -324,7 +363,7 @@ class ItemCRUD:
f"User {current_user.id} checked '{childname}'" f"User {current_user.id} checked '{childname}'"
) )
if not re.match(RegEx.patternAlphabetOnly, childname): if not re.match(RegEx.allPattern, childname):
return HtmlHelper.json_response( return HtmlHelper.json_response(
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400 ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
) )
@@ -353,7 +392,6 @@ class ItemCRUD:
return HtmlHelper.json_response( return HtmlHelper.json_response(
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500 ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
) )
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()

View File

@@ -1,29 +1,41 @@
import os import os
from datetime import datetime
from flask import current_app from flask import current_app
from flask_login import current_user from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from datetime import datetime
from model.FolderAndFile import FolderAndFile
class LogHelper: class LogHelper:
@staticmethod @staticmethod
def log_action(action, details=""): def log_action(action, details=""):
"""Add a log entry.""" """Log user actions with timestamp, user, action, and details."""
log_data = LogData() logData = LogData()
log_data.add_log(action, details) logData.WriteLog(action, details="")
class LogData: class LogData:
filepath = ""
timestamp = None
def __init__(self): def __init__(self):
self.filepath = os.path.join(current_app.root_path, 'activity.log') self.filepath = FolderAndFile.get_activity_log_path('activity.log')
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.user = getattr(current_user, "cn", None) \ self.user = LogData.get_current_user()
or getattr(current_user, "username", None) \
or getattr(current_user, "sAMAccountName", "Unknown")
def add_log(self, action, details=""): @staticmethod
"""Create/Add a log entry.""" def get_current_user():
if hasattr(current_user, "cn") and current_user.cn:
return current_user.cn
elif hasattr(current_user, "username") and current_user.username:
return current_user.username
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
return current_user.sAMAccountName
return "Unknown"
def WriteLog(self, action, details=""):
"""Log user actions with timestamp, user, action, and details."""
with open(self.filepath, "a", encoding="utf-8") as f: with open(self.filepath, "a", encoding="utf-8") as f:
f.write( f.write(
f"Timestamp: {self.timestamp} | " f"Timestamp: {self.timestamp} | "
@@ -32,73 +44,41 @@ class LogData:
f"Details: {details}\n" f"Details: {details}\n"
) )
def get_all_logs(self): def GetActivitiesLog(self):
"""Read all logs."""
logs = [] logs = []
if os.path.exists(self.filepath): if os.path.exists(self.filepath):
with open(self.filepath, 'r', encoding="utf-8") as f: with open(self.filepath, 'r') as f:
for line in f: for line in f:
parts = line.strip().split(" | ") parts = line.strip().split(" | ")
if len(parts) == 4: if len(parts) == 4:
logs.append({ logs.append({
"timestamp": parts[0].split(":", 1)[1].strip(), "timestamp": parts[0].replace("Timestamp:", "").strip(),
"user": parts[1].split(":", 1)[1].strip(), "user": parts[1].replace("User:", "").strip(),
"action": parts[2].split(":", 1)[1].strip(), "action": parts[2].replace("Action:", "").strip(),
"details": parts[3].split(":", 1)[1].strip() "details": parts[3].replace("Details:", "").strip()
}) })
return logs return logs
def get_filtered_logs(self, start_date=None, end_date=None, user_name=None): def GetFilteredActivitiesLog(self, startDate, endDate, userName):
"""Filter logs by date and/or user.""" filtered_logs = self.GetActivitiesLog()
logs = self.get_all_logs()
# Filter by date # Date filter
if start_date or end_date: if startDate or endDate:
start_dt = datetime.strptime(start_date, "%Y-%m-%d") if start_date else datetime.min try:
end_dt = datetime.strptime(end_date, "%Y-%m-%d") if end_date else datetime.max start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
logs = [ end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
log for log in logs
filtered_logs = [
log for log in filtered_logs
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
] ]
# Filter by username except Exception as e:
if user_name: print("Date filter error:", e)
logs = [log for log in logs if user_name.lower() in log.get("user", "").lower()]
return logs
def update_log(self, index, action=None, details=None):
"""Update a specific log entry by index (0-based)."""
logs = self.get_all_logs()
if 0 <= index < len(logs):
if action:
logs[index]["action"] = action
if details:
logs[index]["details"] = details
self._rewrite_logs_file(logs)
return True
return False
def delete_log(self, index):
"""Delete a specific log entry by index (0-based)."""
logs = self.get_all_logs()
if 0 <= index < len(logs):
logs.pop(index)
self._rewrite_logs_file(logs)
return True
return False
# ------------------- INTERNAL HELPER -------------------
def _rewrite_logs_file(self, logs):
"""Overwrite the log file with current logs."""
with open(self.filepath, "w", encoding="utf-8") as f:
for log in logs:
f.write(
f"Timestamp: {log['timestamp']} | "
f"User: {log['user']} | "
f"Action: {log['action']} | "
f"Details: {log['details']}\n"
)
# Username filter
if userName:
filtered_logs = [log for log in filtered_logs if userName.lower() in log["user"].lower()]
return filtered_logs

View File

@@ -8,10 +8,11 @@ class ItemCRUDType(Enum):
State = 4 State = 4
HoldType = 5 HoldType = 5
Subcontractor = 6 Subcontractor = 6
GSTRelease = 7
class RegEx: class RegEx:
patternAlphabetOnly = "^[A-Za-z ]+$" patternAlphabetOnly = "^[A-Za-z ]+$"
allPattern = "^(?!\s*$).+"
class ResponseHandler: class ResponseHandler:

View File

@@ -1,3 +1,5 @@
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
import config import config
import mysql.connector import mysql.connector
@@ -11,11 +13,18 @@ class Village:
def __init__(self): def __init__(self):
self.isSuccess = False self.isSuccess = False
self.resultMessage = "" self.resultMessage = ""
self.response = {} # ✅ ADDED
self.village = ItemCRUD(itemType=ItemCRUDType.Village) self.village = ItemCRUD(itemType=ItemCRUDType.Village)
# 🔹 Helper: sync status # 🔹 Helper: sync status
def _set_status(self, village): def _set_status(self, village):
self.isSuccess = village.isSuccess self.isSuccess = village.isSuccess
# ✅ UPDATED (safe handling)
if hasattr(village, "response"):
self.response = village.response
self.resultMessage = village.response.get("message", "")
else:
self.resultMessage = village.resultMessage self.resultMessage = village.resultMessage
# 🔹 Helper: get request data # 🔹 Helper: get request data
@@ -28,8 +37,9 @@ class Village:
block_id, village_name = self._get_form_data(request) block_id, village_name = self._get_form_data(request)
if not village_name: if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.resultMessage = self.response["message"]
self.isSuccess = False self.isSuccess = False
self.resultMessage = "Village name cannot be empty"
return return
try: try:
@@ -65,8 +75,9 @@ class Village:
block_id, village_name = self._get_form_data(request) block_id, village_name = self._get_form_data(request)
if not village_name: if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.resultMessage = self.response["message"]
self.isSuccess = False self.isSuccess = False
self.resultMessage = "Village name cannot be empty"
return None return None
try: try:
@@ -102,8 +113,9 @@ class Village:
block_id, village_name = self._get_form_data(request) block_id, village_name = self._get_form_data(request)
if not village_name: if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.resultMessage = self.response["message"]
self.isSuccess = False self.isSuccess = False
self.resultMessage = "Village name cannot be empty"
return return
try: try:
@@ -163,9 +175,11 @@ class Village:
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error fetching blocks: {e}") print(f"Error fetching blocks: {e}")
self.isSuccess = False self.isSuccess = False
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.fetch_failure("block"), 500 # ✅ FIXED (removed jsonify response)
) self.response = ResponseHandler.fetch_failure("block")
self.resultMessage = self.response["message"]
return [] return []
finally: finally:

View File

@@ -1,109 +1,8 @@
# from flask import request # model/gst_release.py
# from model.ItemCRUD import ItemCRUD
# from model.Utilities import ItemCRUDType
# class GSTRelease:
# """CRUD operations for GST Release using ItemCRUD"""
# def __init__(self):
# self.isSuccess = False
# self.resultMessage = ""
# # ------------------- Add GST Release -------------------
# def AddGSTRelease(self, request):
# pmc_no = request.form.get('PMC_No', '').strip()
# invoice_no = request.form.get('invoice_No', '').strip()
# gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# gst.AddItem(
# request=request,
# parentid=None,
# childname=f"{pmc_no}-{invoice_no}",
# storedprocfetch="CheckGSTReleaseExists",
# storedprocadd="AddGSTReleaseFromExcel" # your stored procedure handles extra fields
# )
# self.isSuccess = gst.isSuccess
# self.resultMessage = str(gst.resultMessage)
# # ------------------- Get All GST Releases -------------------
# def GetAllGSTReleases(self):
# gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# # Pass request=None for fetch
# rows = gst.GetAllData(request=None, storedproc="GetAllGSTReleases")
# self.isSuccess = gst.isSuccess
# self.resultMessage = str(gst.resultMessage)
# data = []
# for row in rows:
# data.append({
# "gst_release_id": row[0],
# "pmc_no": row[1],
# "invoice_no": row[2],
# "basic_amount": row[3],
# "final_amount": row[4],
# "total_amount": row[5],
# "utr": row[6],
# "contractor_id": row[7]
# })
# return data
# # ------------------- Get GST Release By ID -------------------
# def GetGSTReleaseByID(self, gst_release_id):
# gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# row = gst.GetDataByID(gst_release_id, request=None, storedproc="GetGSTReleaseById")
# self.isSuccess = gst.isSuccess
# self.resultMessage = str(gst.resultMessage)
# if row:
# return {
# "gst_release_id": row[0],
# "pmc_no": row[1],
# "invoice_no": row[2],
# "basic_amount": row[3],
# "final_amount": row[4],
# "total_amount": row[5],
# "utr": row[6],
# "contractor_id": row[7]
# }
# return None
# # ------------------- Edit GST Release -------------------
# def EditGSTRelease(self, request, gst_release_id):
# pmc_no = request.form.get('PMC_No', '').strip()
# invoice_no = request.form.get('invoice_No', '').strip()
# gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# gst.EditItem(
# request=request,
# childid=gst_release_id,
# parentid=None,
# childname=f"{pmc_no}-{invoice_no}",
# storedprocupdate="UpdateGSTRelease" # stored procedure handles extra fields
# )
# self.isSuccess = gst.isSuccess
# self.resultMessage = str(gst.resultMessage)
# # ------------------- Delete GST Release -------------------
# def DeleteGSTRelease(self, gst_release_id):
# gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# gst.DeleteItem(
# itemID=gst_release_id,
# request=None,
# storedprocDelete="DeleteGSTReleaseById"
# )
# self.isSuccess = gst.isSuccess
# self.resultMessage = str(gst.resultMessage)
from flask import request, jsonify from flask import request, jsonify
from model.ItemCRUD import ItemCRUD from model.ItemCRUD import ItemCRUD
from model.Utilities import ItemCRUDType from model.Utilities import ItemCRUDType
class GSTRelease: class GSTRelease:
def __init__(self): def __init__(self):
@@ -115,6 +14,12 @@ class GSTRelease:
try: try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease) gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Print the full form data
print("===== DEBUG: FORM DATA =====")
for key, value in request.form.items():
print(f"{key} : {value}")
print("=============================")
data = { data = {
"PMC_No": request.form.get("PMC_No", "").strip(), "PMC_No": request.form.get("PMC_No", "").strip(),
"Invoice_No": request.form.get("Invoice_No", "").strip(), "Invoice_No": request.form.get("Invoice_No", "").strip(),
@@ -125,6 +30,11 @@ class GSTRelease:
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0) "Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
} }
print("===== DEBUG: PARSED DATA =====")
print(data)
print("==============================")
# Add GST Release
gst.AddItem( gst.AddItem(
request=request, request=request,
data=data, data=data,
@@ -132,6 +42,8 @@ class GSTRelease:
storedprocadd="AddGSTReleaseFromExcel" storedprocadd="AddGSTReleaseFromExcel"
) )
print(f"AddItem result: isSuccess={gst.isSuccess}, message={gst.resultMessage}")
self.isSuccess = gst.isSuccess self.isSuccess = gst.isSuccess
self.resultMessage = str(gst.resultMessage) self.resultMessage = str(gst.resultMessage)
@@ -142,20 +54,26 @@ class GSTRelease:
return jsonify({"success": self.isSuccess, "message": self.resultMessage}) return jsonify({"success": self.isSuccess, "message": self.resultMessage})
# ------------------- Edit GST Release -------------------
def EditGSTRelease(self, request, gst_release_id): def EditGSTRelease(self, request, gst_release_id):
try: try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease) gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Map form inputs to stored procedure parameters
data = { data = {
"PMC_No": request.form.get("PMC_No", "").strip(), "p_pmc_no": request.form.get("PMC_No", "").strip(),
"Invoice_No": request.form.get("Invoice_No", "").strip(), "p_invoice_no": request.form.get("invoice_no", "").strip(),
"Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0), "p_basic_amount": float(request.form.get("Basic_Amount", 0) or 0),
"Final_Amount": float(request.form.get("Final_Amount", 0) or 0), "p_final_amount": float(request.form.get("Final_Amount", 0) or 0),
"Total_Amount": float(request.form.get("Total_Amount", 0) or 0), "p_total_amount": float(request.form.get("Total_Amount", 0) or 0),
"UTR": request.form.get("UTR", "").strip() "p_utr": request.form.get("UTR", "").strip(),
"p_gst_release_id": gst_release_id
} }
print("===== DEBUG: UPDATE DATA =====")
print(data)
print("==============================")
# Call your stored procedure
gst.EditItem( gst.EditItem(
request=request, request=request,
childid=gst_release_id, childid=gst_release_id,
@@ -171,8 +89,6 @@ class GSTRelease:
self.isSuccess = False self.isSuccess = False
self.resultMessage = str(e) self.resultMessage = str(e)
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
# ------------------- Delete GST Release ------------------- # ------------------- Delete GST Release -------------------
def DeleteGSTRelease(self, gst_release_id): def DeleteGSTRelease(self, gst_release_id):
try: try:
@@ -198,7 +114,6 @@ class GSTRelease:
def GetAllGSTReleases(self): def GetAllGSTReleases(self):
try: try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease) gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
rows = gst.GetAllData(None, "GetAllGSTReleases") rows = gst.GetAllData(None, "GetAllGSTReleases")
data = [] data = []
@@ -224,7 +139,6 @@ class GSTRelease:
def GetGSTReleaseByID(self, gst_release_id): def GetGSTReleaseByID(self, gst_release_id):
try: try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease) gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
row = gst.GetDataByID(gst_release_id, "GetGSTReleaseById") row = gst.GetDataByID(gst_release_id, "GetGSTReleaseById")
if row: if row:

View File

@@ -3,7 +3,7 @@ $(document).ready(function () {
let holdType = $(this).val().replace(/^\s+/, ""); let holdType = $(this).val().replace(/^\s+/, "");
$(this).val(holdType); $(this).val(holdType);
let reg = /^[A-Za-z]/; let reg = /^.+$/; // all pattern allow
if (!reg.test(holdType)) { if (!reg.test(holdType)) {
$("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red"); $("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red");

View File

@@ -1,5 +1,160 @@
// Subcontractor autocomplete functionality // $(document).ready(function () {
$(document).ready(function () { // // ===============================
// // FORM / TABLE TOGGLE
// // ===============================
// if ($('#addForm').length && $('#addTable').length) {
// $('#addForm').show();
// $('#addTable').hide();
// $('#addButton').click(function () {
// $('#addForm').show();
// $('#addTable').hide();
// });
// $('#displayButton').click(function () {
// $('#addForm').hide();
// $('#addTable').show();
// });
// }
// // ===============================
// // Subcontractor autocomplete
// // ===============================
// $("#subcontractor").keyup(function () {
// let query = $(this).val();
// if (query !== "") {
// $.ajax({
// url: "/search_subcontractor",
// method: "POST",
// data: { query: query },
// success: function (data) {
// $("#subcontractor_list").fadeIn().html(data);
// }
// });
// } else {
// $("#subcontractor_list").fadeOut();
// }
// });
// $(document).on("click", "li", function () {
// $("#subcontractor").val($(this).text());
// $("#subcontractor_id").val($(this).attr("data-id"));
// $("#subcontractor_list").fadeOut();
// });
// // Focus
// if (document.getElementById('subcontractor')) {
// document.getElementById('subcontractor').focus();
// }
// // ===============================
// // ADD INVOICE
// // ===============================
// if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
// $("#invoiceForm").on("submit", function (e) {
// e.preventDefault();
// let formData = $(this).serialize();
// $.ajax({
// url: '/add_invoice',
// method: 'POST',
// data: formData,
// dataType: 'json',
// success: function (response) {
// if (response.status === "success") {
// alert(response.message || "Invoice added successfully!");
// $('#invoiceForm')[0].reset(); // clear form
// $('#addForm').hide();
// $('#addTable').show(); // switch to table
// location.reload(); // optional refresh
// } else {
// alert(response.message || "Error adding invoice.");
// }
// },
// error: function (xhr) {
// alert(xhr.responseJSON?.message || "Submission failed. Please try again.");
// }
// });
// });
// }
// // Example AJAX update function
// function updateInvoice(invoiceId, formElement) {
// const formData = $(formElement).serialize();
// $.ajax({
// url: '/update_invoice/' + invoiceId,
// method: 'POST',
// data: formData,
// dataType: 'json',
// success: function(response) {
// if(response.status === "success") {
// alert(response.message || "Invoice updated successfully!");
// // ✅ Hide Add Form, Show Table
// $('#addForm').hide();
// $('#addTable').show();
// // Optional: reload table or refresh page
// location.reload();
// } else {
// alert(response.message || "Update failed. Please try again.");
// }
// },
// error: function(xhr) {
// alert(xhr.responseJSON?.message || "Error updating invoice.");
// }
// });
// }
// // ===============================
// // DELETE INVOICE
// // ===============================
// function deleteInvoice(invoiceId, element) {
// if (!confirm("Are you sure you want to delete this invoice?")) return;
// $.ajax({
// url: '/delete_invoice/' + invoiceId,
// method: 'GET',
// dataType: 'json',
// success: function (response) {
// alert(response.message || "Invoice deleted successfully!");
// if (element) $(element).closest("tr").remove();
// },
// error: function (xhr) {
// alert(xhr.responseJSON?.message || "Error deleting invoice. Please try again.");
// }
// });
// }
$(document).ready(function () {
// ===============================
// FORM / TABLE TOGGLE
// ===============================
if ($('#addForm').length && $('#addTable').length) {
// Default: show form, hide table
$('#addForm').show();
$('#addTable').hide();
// ✅ Check URL hash to show table instead
if (window.location.hash === "#addTable") {
$('#addForm').hide();
$('#addTable').show();
}
$('#addButton').click(function () {
$('#addForm').show();
$('#addTable').hide();
});
$('#displayButton').click(function () {
$('#addForm').hide();
$('#addTable').show();
});
}
// ===============================
// Subcontractor autocomplete
// ===============================
$("#subcontractor").keyup(function () { $("#subcontractor").keyup(function () {
let query = $(this).val(); let query = $(this).val();
if (query !== "") { if (query !== "") {
@@ -21,42 +176,96 @@
$("#subcontractor_id").val($(this).attr("data-id")); $("#subcontractor_id").val($(this).attr("data-id"));
$("#subcontractor_list").fadeOut(); $("#subcontractor_list").fadeOut();
}); });
});
// Success Alert: show alert and reload after 3 seconds // Focus
function showSuccessAlert() { if (document.getElementById('subcontractor')) {
const alertBox = document.getElementById("invoiceSuccessAlert"); document.getElementById('subcontractor').focus();
alertBox.classList.add("show");
setTimeout(() => {
alertBox.classList.remove("show");
// Reload page or redirect after alert hides
window.location.href = '/add_invoice';
}, 3000);
} }
// Submit form via AJAX // ===============================
// ADD INVOICE
// ===============================
if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
$("#invoiceForm").on("submit", function (e) { $("#invoiceForm").on("submit", function (e) {
e.preventDefault(); e.preventDefault();
let formData = $(this).serialize(); let formData = $(this).serialize();
$.ajax({ $.ajax({
url: '/add_invoice', url: '/add_invoice',
method: 'POST', method: 'POST',
data: formData, data: formData,
dataType: 'json',
success: function (response) { success: function (response) {
if(response.status === "success") { if (response.status === "success") {
showSuccessAlert(); alert(response.message || "Invoice added successfully!");
$('#invoiceForm')[0].reset(); // clear form
$('#addForm').hide();
$('#addTable').show(); // switch to table
location.reload(); // optional refresh
} else { } else {
alert(response.message); alert(response.message || "Error adding invoice.");
} }
}, },
error: function (xhr, status, error) { error: function (xhr) {
alert("Submission failed: " + error); let msg = xhr.responseJSON?.message || "Submission failed. Please try again.";
alert(msg);
} }
}); });
}); });
}
// ===============================
// UPDATE INVOICE
// ===============================
function updateInvoice(invoiceId, formElement) {
const formData = $(formElement).serialize();
$.ajax({
url: '/update_invoice/' + invoiceId,
method: 'POST',
data: formData,
dataType: 'json',
success: function(response) {
if(response.status === "success") {
alert(response.message || "Invoice updated successfully!");
window.onload = function () { // Redirect to Add Invoice page's table part
document.getElementById('subcontractor').focus(); window.location.href = "/add_invoice#addTable";
}; } else {
alert(response.message || "Update failed. Please try again.");
}
},
error: function(xhr) {
let msg = xhr.responseJSON?.message || "Error updating invoice.";
alert(msg);
}
});
}
window.updateInvoice = updateInvoice; // make globally accessible
// ===============================
// DELETE INVOICE
// ===============================
function deleteInvoice(invoiceId, element) {
if (!confirm("Are you sure you want to delete this invoice?")) return;
$.ajax({
url: '/delete_invoice/' + invoiceId,
method: 'GET',
dataType: 'json',
success: function (response) {
if (response.status === "success") {
alert(response.message || "Invoice deleted successfully!");
if (element) $(element).closest("tr").remove();
} else {
alert(response.message || "Error deleting invoice.");
}
},
error: function (xhr) {
let msg = xhr.responseJSON?.message || "Error deleting invoice. Please try again.";
alert(msg);
}
});
}
window.deleteInvoice = deleteInvoice; // make globally accessible
});

View File

@@ -1,26 +1,23 @@
// Search on table using search inpute options // Search on table using search inpute options
function searchTable() { function searchTable() {
let input = document.getElementById("searchBar").value.toLowerCase(); let input = document.getElementById("searchBar").value.toLowerCase();
let rows = document.querySelectorAll("table tbody tr"); let tables = document.querySelectorAll("table");
rows.forEach(row => { tables.forEach(table => {
let blockName = row.cells[1].textContent.toLowerCase(); let rows = table.querySelectorAll("tr");
let districtName = row.cells[2].textContent.toLowerCase();
let villageName = row.cells[3].textContent.toLowerCase();
if (blockName.includes(input) || districtName.includes(input)|| villageName.includes(input)) { rows.forEach((row, index) => {
row.style.display = ""; if (index === 0) return; // header skip
} else {
row.style.display = "none"; let text = row.textContent.toLowerCase();
}
row.style.display = text.includes(input) ? "" : "none";
});
}); });
} }
// Common Sorting Script for Tables // Common Sorting Script for Tables
function sortTable(n, dir) { function sortTable(n, dir) {
var table, rows, switching, i, x, y, shouldSwitch; var table, rows, switching, i, x, y, shouldSwitch;
@@ -57,14 +54,14 @@ function sortTable(n, dir) {
} }
// Attach sorting functionality to all sortable tables // Attach sorting functionality to all sortable tables
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function () {
// Find all elements with the class "sortable-header" // Find all elements with the class "sortable-header"
var sortableHeaders = document.querySelectorAll(".sortable-header"); var sortableHeaders = document.querySelectorAll(".sortable-header");
sortableHeaders.forEach(function(header) { sortableHeaders.forEach(function (header) {
// Attach click event for ascending sort // Attach click event for ascending sort
if (header.querySelector(".sort-asc")) { if (header.querySelector(".sort-asc")) {
header.querySelector(".sort-asc").addEventListener("click", function() { header.querySelector(".sort-asc").addEventListener("click", function () {
var columnIndex = Array.from(header.parentNode.children).indexOf(header); var columnIndex = Array.from(header.parentNode.children).indexOf(header);
sortTable(columnIndex, "asc"); sortTable(columnIndex, "asc");
}); });
@@ -72,7 +69,7 @@ document.addEventListener("DOMContentLoaded", function() {
// Attach click event for descending sort // Attach click event for descending sort
if (header.querySelector(".sort-desc")) { if (header.querySelector(".sort-desc")) {
header.querySelector(".sort-desc").addEventListener("click", function() { header.querySelector(".sort-desc").addEventListener("click", function () {
var columnIndex = Array.from(header.parentNode.children).indexOf(header); var columnIndex = Array.from(header.parentNode.children).indexOf(header);
sortTable(columnIndex, "desc"); sortTable(columnIndex, "desc");
}); });
@@ -106,3 +103,30 @@ document.addEventListener("DOMContentLoaded", function () {
addButton.classList.remove("active-button"); addButton.classList.remove("active-button");
}); });
}); });
document.addEventListener("DOMContentLoaded", function () {
let tables = document.querySelectorAll("table");
tables.forEach(table => {
let header = table.querySelector("tr:first-child");
if (header) {
header.style.position = "sticky";
header.style.top = "0";
header.style.background = "#fff";
header.style.zIndex = "2";
}
if (!table.parentElement.classList.contains("table-wrapper")) {
let wrapper = document.createElement("div");
wrapper.classList.add("table-wrapper");
wrapper.style.maxHeight = "65vh"
wrapper.style.overflowY = "auto";
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
}
});
});

View File

@@ -1,10 +1,37 @@
window.onload = function () { window.onload = function () {
if (document.getElementById('Village_Name')) {
document.getElementById('Village_Name').focus(); document.getElementById('Village_Name').focus();
}
}; };
$(document).ready(function () { $(document).ready(function () {
// ✅ RUN ONLY IF THIS PAGE HAS FORM/TABLE
if ($('#addForm').length && $('#addTable').length) {
// ✅ DEFAULT VIEW → SHOW FORM
$('#addForm').show();
$('#addTable').hide();
// 🔥 BUTTON TOGGLE
$('#addButton').click(function () {
$('#addForm').show();
$('#addTable').hide();
});
$('#displayButton').click(function () {
$('#addForm').hide();
$('#addTable').show();
});
}
// ===============================
// STATE → DISTRICT // STATE → DISTRICT
// ===============================
if ($('#state_Id').length) {
$('#state_Id').change(function () { $('#state_Id').change(function () {
var stateId = $(this).val(); var stateId = $(this).val();
@@ -23,25 +50,24 @@ $(document).ready(function () {
districtDropdown.append('<option value="" disabled selected>Select District</option>'); districtDropdown.append('<option value="" disabled selected>Select District</option>');
data.forEach(function (district) { data.forEach(function (district) {
districtDropdown.append( districtDropdown.append(
'<option value="' + district.id + '">' + district.name + '</option>' '<option value="' + district.id + '">' + district.name + '</option>'
); );
}); });
districtDropdown.prop('disabled', false); districtDropdown.prop('disabled', false);
}
});
}
});
} }
});
}
});
// ===============================
// DISTRICT → BLOCK // DISTRICT → BLOCK
// ===============================
if ($('#district_Id').length) {
$('#district_Id').change(function () { $('#district_Id').change(function () {
var districtId = $(this).val(); var districtId = $(this).val();
@@ -60,25 +86,24 @@ $(document).ready(function () {
blockDropdown.append('<option value="" disabled selected>Select Block</option>'); blockDropdown.append('<option value="" disabled selected>Select Block</option>');
data.forEach(function (block) { data.forEach(function (block) {
blockDropdown.append( blockDropdown.append(
'<option value="' + block.id + '">' + block.name + '</option>' '<option value="' + block.id + '">' + block.name + '</option>'
); );
}); });
blockDropdown.prop('disabled', false); blockDropdown.prop('disabled', false);
}
});
}
});
} }
});
}
});
// ===============================
// VILLAGE NAME VALIDATION // VILLAGE NAME VALIDATION
// ===============================
if ($('#Village_Name').length) {
$('#Village_Name').on('input', function () { $('#Village_Name').on('input', function () {
var villageName = $(this).val(); var villageName = $(this).val();
@@ -96,13 +121,16 @@ $(document).ready(function () {
$('#villageMessage').text(''); $('#villageMessage').text('');
$('#submitVillage').prop('disabled', false); $('#submitVillage').prop('disabled', false);
}
});
} }
});
// ===============================
// CHECK DUPLICATE VILLAGE // CHECK DUPLICATE VILLAGE
// ===============================
if ($('#Village_Name').length && $('#block_Id').length) {
$('#Village_Name, #block_Id').on('change keyup', function () { $('#Village_Name, #block_Id').on('change keyup', function () {
var blockId = $('#block_Id').val(); var blockId = $('#block_Id').val();
@@ -111,7 +139,6 @@ $(document).ready(function () {
if (blockId && villageName) { if (blockId && villageName) {
$.ajax({ $.ajax({
url: '/check_village', url: '/check_village',
type: 'POST', type: 'POST',
@@ -137,9 +164,7 @@ $(document).ready(function () {
.css('color', 'green'); .css('color', 'green');
$('#submitVillage').prop('disabled', false); $('#submitVillage').prop('disabled', false);
} }
}, },
error: function () { error: function () {
@@ -149,50 +174,77 @@ $(document).ready(function () {
.css('color', 'red'); .css('color', 'red');
$('#submitVillage').prop('disabled', true); $('#submitVillage').prop('disabled', true);
}
});
}
});
} }
});
} // ===============================
// ADD VILLAGE (SAFE SCOPE FIX)
// ===============================
if ($('#villageForm').length) {
});
// ADD VILLAGE
$('#villageForm').submit(function (event) { $('#villageForm').submit(function (event) {
event.preventDefault(); event.preventDefault();
$.ajax({ $.ajax({
url: '/add_village', url: '/add_village',
type: 'POST', type: 'POST',
data: $(this).serialize(), data: $(this).serialize(),
success: function (response) { success: function (response) {
if (response.status === 'success') { if (response && response.status === 'success') {
alert('Village added successfully!'); alert(response.message || 'Village added successfully!');
// ✅ clear form
$('#villageForm')[0].reset();
// ✅ switch to table
$('#addForm').hide();
$('#addTable').show();
// optional refresh
location.reload(); location.reload();
} else { } else {
alert(response.message || 'Error adding village. Please try again.');
alert('Error adding village. Please try again.');
} }
}, },
error: function () { error: function () {
alert('An error occurred. Please try again.'); alert('An error occurred. Please try again.');
}
});
});
} }
});
});
}); });
// ===============================
// DELETE FUNCTION (SAFE)
// ===============================
function deleteVillage(villageId, element) {
if (!confirm("Are you sure you want to delete this village?")) return;
$.ajax({
url: '/delete_village/' + villageId,
type: 'GET',
dataType: 'json',
success: function (response) {
alert(response.message); // ✅ now shows "Village deleted successfully!"
if (element) $(element).closest("tr").remove();
},
error: function () {
alert("Error deleting village. Please try again.");
}
});
}

View File

@@ -22,10 +22,14 @@
<div class="row1"> <div class="row1">
<div> <div>
<label for="subcontractor">Subcontractor Name:</label> <label for="subcontractor">Subcontractor Name:</label>
<!-- Text input for user-friendly autocomplete -->
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/> <input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
<!-- Hidden input for backend; must match model's Contractor_ID -->
<input type="hidden" id="subcontractor_id" name="Contractor_ID"/>
<div id="subcontractor_list" class="autocomplete-items"></div> <div id="subcontractor_list" class="autocomplete-items"></div>
</div> </div>
</div> </div>
<label for="PMC_No">PMC No:</label><br> <label for="PMC_No">PMC No:</label><br>
@@ -37,19 +41,19 @@
</select><br><br> </select><br><br>
<label for="invoice_No">Invoice No:</label><br> <label for="invoice_No">Invoice No:</label><br>
<input type="text" id="invoice_No" name="invoice_No" required><br><br> <input type="text" id="invoice_No" name="Invoice_No" required><br><br>
<label for="basic_amount">Basic Amount:</label><br> <label for="basic_amount">Basic Amount:</label><br>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required><br><br> <input type="number" step="0.01" id="basic_amount" name="Basic_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="final_amount">Final Amount:</label><br> <label for="final_amount">Final Amount:</label><br>
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required><br><br> <input type="number" step="0.01" id="final_amount" name="Final_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="total_amount">Total Amount:</label><br> <label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" placeholder="₹ - 00.00" required><br><br> <input type="number" step="0.01" id="total_amount" name="Total_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="utr">UTR:</label><br> <label for="utr">UTR:</label><br>
<input type="text" id="utr" name="utr" required><br><br> <input type="text" id="utr" name="UTR" required><br><br>
<button type="submit">Submit GST Release</button> <button type="submit">Submit GST Release</button>
</form> </form>
@@ -68,10 +72,11 @@
{% endwith %} {% endwith %}
</div> </div>
<!-- GST Release History Table -->
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none;">
<div class="search-container"> <div class="search-container">
<h2>GST Release History</h2> <h2>GST Release History</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input type="text" id="searchBar" placeholder="Search..." onkeyup="searchTable()">
</div> </div>
<table id="sortableTable" border="1"> <table id="sortableTable" border="1">
<thead> <thead>
@@ -90,24 +95,22 @@
<tbody> <tbody>
{% for gst_rel in gst_releases %} {% for gst_rel in gst_releases %}
<tr> <tr>
<td>{{ gst_rel[0] }}</td> <td>{{ gst_rel.gst_release_id }}</td>
<td>{{ gst_rel[1] }}</td> <td>{{ gst_rel.pmc_no }}</td>
<td>{{ gst_rel[2] }}</td> <td>{{ gst_rel.invoice_no }}</td>
<td>{{ gst_rel[3] }}</td> <td>{{ gst_rel.basic_amount }}</td>
<td>{{ gst_rel[4] }}</td> <td>{{ gst_rel.final_amount }}</td>
<td>{{ gst_rel[5] }}</td> <td>{{ gst_rel.total_amount }}</td>
<td>{{ gst_rel[6] }}</td> <td>{{ gst_rel.utr }}</td>
<td> <td>
<a href="/edit_gst_release/{{ gst_rel[0] }}"> <a href="{{ url_for('gst_release_bp.edit_gst_release', gst_release_id=gst_rel.gst_release_id) }}">
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" <img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon">
class="icon">
</a> </a>
</td> </td>
<td> <td>
<a href="/delete_gst_release/{{ gst_rel[0] }}" <a href="{{ url_for('gst_release_bp.delete_gst_release', gst_release_id=gst_rel.gst_release_id) }}"
onclick="return confirm('Are you sure you want to delete this GST Release?')"> onclick="return confirm('Are you sure you want to delete this GST Release?')">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" <img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
class="icon">
</a> </a>
</td> </td>
</tr> </tr>
@@ -118,46 +121,61 @@
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Handle subcontractor autocomplete
document.getElementById("subcontractor").addEventListener("input", function () { const subcontractorInput = document.getElementById("subcontractor");
const subcontractorIdInput = document.getElementById("subcontractor_id");
const subcontractorList = document.getElementById("subcontractor_list");
const pmcDropdown = document.getElementById("PMC_No");
const form = document.querySelector('form');
// --------------------------
// Subcontractor autocomplete
// --------------------------
subcontractorInput.addEventListener("input", function () {
const query = this.value; const query = this.value;
const list = document.getElementById("subcontractor_list");
if (query.length < 2) { if (query.length < 2) {
list.innerHTML = ''; subcontractorList.innerHTML = '';
subcontractorIdInput.value = ''; // reset hidden id
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; // reset PMC dropdown
return; return;
} }
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`) fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
list.innerHTML = ''; subcontractorList.innerHTML = '';
data.results.forEach(item => { data.results.forEach(item => {
const div = document.createElement("div"); const div = document.createElement("div");
div.setAttribute("data-id", item.id); div.setAttribute("data-id", item.id);
div.textContent = item.name; div.textContent = item.name;
list.appendChild(div); subcontractorList.appendChild(div);
}); });
}); });
}); });
// Handle subcontractor selection // --------------------------
document.getElementById("subcontractor_list").addEventListener("click", function (e) { // Subcontractor selection
// --------------------------
subcontractorList.addEventListener("click", function (e) {
const selectedId = e.target.getAttribute("data-id"); const selectedId = e.target.getAttribute("data-id");
const selectedName = e.target.textContent; const selectedName = e.target.textContent;
if (selectedId) { if (selectedId) {
document.getElementById("subcontractor_id").value = selectedId; // Set hidden field for backend
document.getElementById("subcontractor").value = selectedName; subcontractorIdInput.value = selectedId;
document.getElementById("subcontractor_list").innerHTML = "";
// Update PMC dropdown for selected subcontractor // Set text input to selected name
subcontractorInput.value = selectedName;
// Clear the autocomplete list
subcontractorList.innerHTML = "";
// Fetch and populate PMC dropdown
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`) fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const pmcDropdown = document.getElementById("PMC_No");
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
data.pmc_nos.forEach(pmc => { data.pmc_nos.forEach(pmc => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = pmc; option.value = pmc;
@@ -167,6 +185,22 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
}); });
// --------------------------
// Form submit validation
// --------------------------
form.addEventListener('submit', function(e) {
if (!subcontractorIdInput.value) {
e.preventDefault();
alert("Please select a subcontractor from the list.");
subcontractorInput.focus();
} else if (!pmcDropdown.value) {
e.preventDefault();
alert("Please select a PMC No.");
pmcDropdown.focus();
}
});
}); });
</script> </script>

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<head> <head>
<title>Manage Hold Types</title> <title>Manage Hold Types</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
@@ -7,14 +8,15 @@
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head> </head>
<body> <body>
<div class="button-container"> <div class="button-container">
<button id="addButton" class="action-button">Add</button> <button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button> <button id="displayButton" class="action-button">Display</button>
</div> </div>
<div id="addForm"> <div id="addForm">
<h2>Add Hold Types</h2> <h2>Add Hold Types</h2>
<form id="holdTypeForm" method="POST" action="{{ url_for('hold_types.add_hold_type') }}"> <form id="holdTypeForm" method="POST" action="{{ url_for('hold_types.add_hold_type') }}">
<label>Enter Hold Amount Type:</label> <label>Enter Hold Amount Type:</label>
@@ -22,9 +24,9 @@
<span id="holdTypeMessage"></span> <span id="holdTypeMessage"></span>
<button type="submit" value="Add Hold Type" id="successPopup">Add Hold Type</button> <button type="submit" value="Add Hold Type" id="successPopup">Add Hold Type</button>
</form> </form>
</div> </div>
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none;">
<div class="search-container"> <div class="search-container">
<h2>Hold Type List</h2> <h2>Hold Type List</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
@@ -46,14 +48,26 @@
<tr> <tr>
<td>{{ htd['hold_type_id'] }}</td> <td>{{ htd['hold_type_id'] }}</td>
<td>{{ htd['hold_type'] }}</td> <td>{{ htd['hold_type'] }}</td>
<td><a href="{{ url_for('hold_types.edit_hold_type', id=htd['hold_type_id']) }}">Edit</a></td> <td style="text-align:center;">
<td><button class="delete-button" data-id="{{ htd['hold_type_id'] }}">Delete</button></td> <a href="{{ url_for('hold_types.edit_hold_type', id=htd['hold_type_id']) }}">
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
class="icon">
</a>
</td>
<td style="text-align:center;">
<a href="{{ url_for('hold_types.delete_hold_type', id=htd['hold_type_id']) }}"
onclick="return confirm('Are you sure you want to delete this hold type?');">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
class="icon">
</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
<a href="/">Back to Dashboard</a> <a href="/">Back to Dashboard</a>
</div> </div>
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -1,48 +1,67 @@
{% extends 'base.html' %} {% extends 'base.html' %} {% block content %}
{% block content %}
<head xmlns="http://www.w3.org/1999/html"> <head xmlns="http://www.w3.org/1999/html">
<meta charset="UTF-8"/> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Add Invoice</title> <title>Add Invoice</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}"> <link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='css/invoice.css') }}"
/>
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script> <script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
<script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script> <script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script>
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head> </head>
<body> <body>
{% if success == 'true' %} {% if success == 'true' %}
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert"> <div
class="alert alert-success alert-dismissible fade show mt-3"
role="alert"
>
✅ Invoice added successfully! ✅ Invoice added successfully!
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button
</div> type="button"
{% endif %} class="btn-close"
data-bs-dismiss="alert"
aria-label="Close"
></button>
</div>
{% endif %}
<!-- Flash Messages -->
<!-- Flash Messages --> {% with messages = get_flashed_messages(with_categories=true) %} {% if
{% with messages = get_flashed_messages(with_categories=true) %} messages %}
{% if messages %}
<div class="flash-messages"> <div class="flash-messages">
{% for category, message in messages %} {% for category, message in messages %}
<div class="alert {{ category }}">{{ message }}</div> <div class="alert {{ category }}">{{ message }}</div>
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %} {% endwith %}
{% endwith %}
<div class="button-container"> <div class="button-container">
<button id="addButton" class="action-button">Add</button> <button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button> <button id="displayButton" class="action-button">Display</button>
</div> </div>
<div id="addForm" style="display: none;"> <div id="addForm" style="display: none">
<h2>Add Invoice</h2> <h2>Add Invoice</h2>
<form id="invoiceForm" action="{{ url_for('invoice.add_invoice') }}" method="POST"> <form
id="invoiceForm"
action="{{ url_for('invoice.add_invoice') }}"
method="POST"
>
<div class="row1"> <div class="row1">
<div> <div>
<label for="subcontractor">Subcontractor Name:</label> <label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/> <input
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/> type="text"
id="subcontractor"
name="subcontractor"
required
autocomplete="off"
/>
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
<div id="subcontractor_list"></div> <div id="subcontractor_list"></div>
</div> </div>
</div> </div>
@@ -53,13 +72,15 @@
<select id="village" name="village" required> <select id="village" name="village" required>
<option value="">-- Select Village --</option> <option value="">-- Select Village --</option>
{% for village in villages %} {% for village in villages %}
<option value="{{ village.Village_Name }}">{{ village.Village_Name }}</option> <option value="{{ village.Village_Name }}">
{{ village.Village_Name }}
</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div> <div>
<label for="pmc_no">PMC No:</label> <label for="pmc_no">PMC No:</label>
<input type="text" id="pmc_no" name="pmc_no" required/> <input type="text" id="pmc_no" name="pmc_no" required />
<div id="pmc_no_list" class="autocomplete-list"></div> <div id="pmc_no_list" class="autocomplete-list"></div>
</div> </div>
</div> </div>
@@ -67,81 +88,187 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="work_type">Work Type:</label> <label for="work_type">Work Type:</label>
<input type="text" id="work_type" name="work_type" required/> <input type="text" id="work_type" name="work_type" required />
</div> </div>
<div> <div>
<label for="invoice_details">Invoice Details:</label> <label for="invoice_details">Invoice Details:</label>
<textarea id="invoice_details" name="invoice_details" required></textarea> <textarea
id="invoice_details"
name="invoice_details"
required
></textarea>
</div> </div>
</div> </div>
<div class="row2"> <div class="row2">
<div> <div>
<label for="invoice_no">Invoice No:</label> <label for="invoice_no">Invoice No:</label>
<input type="text" id="invoice_no" name="invoice_no" required/> <input type="text" id="invoice_no" name="invoice_no" required />
</div> </div>
<div> <div>
<label for="invoice_date">Invoice Date:</label> <label for="invoice_date">Invoice Date:</label>
<input type="date" id="invoice_date" name="invoice_date" required/> <input type="date" id="invoice_date" name="invoice_date" required />
</div> </div>
</div> </div>
<div class="row3"> <div class="row3">
<div> <div>
<label for="basic_amount">Basic Amount:</label> <label for="basic_amount">Basic Amount:</label>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="basic_amount"
name="basic_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
<div> <div>
<label for="debit_amount">Debit Amount:</label> <label for="debit_amount">Debit Amount:</label>
<input type="number" step="0.01" id="debit_amount" name="debit_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="debit_amount"
name="debit_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
<div> <div>
<label for="after_debit_amount">After Debit Amount:</label> <label for="after_debit_amount">After Debit Amount:</label>
<input type="number" step="0.01" id="after_debit_amount" name="after_debit_amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="after_debit_amount"
name="after_debit_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div>
</div> </div>
</div>
<div class="row3"> <div class="row3">
<div class="percentage-field"> <div class="percentage-field">
<label for="gst_percentage">GST %:</label> <label for="gst_percentage">GST %:</label>
<input type="number" step="0.01" id="gst_percentage" name="gst_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="gst_percentage"
name="gst_percentage"
placeholder="%"
/>
<label for="gst_amount">GST Amount:</label> <label for="gst_amount">GST Amount:</label>
<input type="number" step="0.01" id="gst_amount" name="gst_amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="gst_amount"
name="gst_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div> <div>
<label for="amount">Amount:</label> <label for="amount">Amount:</label>
<input type="number" step="0.01" id="amount" name="amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="amount"
name="amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div class="percentage-field"> <div class="percentage-field">
<label for="tds_percentage">TDS %:</label> <label for="tds_percentage">TDS %:</label>
<input type="number" step="0.01" id="tds_percentage" name="tds_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="tds_percentage"
name="tds_percentage"
placeholder="%"
/>
<label for="tds_amount">TDS Amount:</label> <label for="tds_amount">TDS Amount:</label>
<input type="number" step="0.01" id="tds_amount" name="tds_amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="tds_amount"
name="tds_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div>
</div> </div>
</div>
<div class="row3"> <div class="row3">
<div class="percentage-field"> <div class="percentage-field">
<label for="sd_percentage">SD %:</label> <label for="sd_percentage">SD %:</label>
<input type="number" step="0.01" id="sd_percentage" name="sd_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="sd_percentage"
name="sd_percentage"
placeholder="%"
/>
<label for="sd_amount">SD Amount:</label> <label for="sd_amount">SD Amount:</label>
<input type="number" step="0.01" id="sd_amount" name="sd_amount" placeholder="₹ - 00.00" readonly required> <input
type="number"
step="0.01"
id="sd_amount"
name="sd_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div class="percentage-field"> <div class="percentage-field">
<label for="commission_percentage">On Commission %:</label> <label for="commission_percentage">On Commission %:</label>
<input type="number" step="0.01" id="commission_percentage" name="commission_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="commission_percentage"
name="commission_percentage"
placeholder="%"
/>
<label for="on_commission">On Commission:</label> <label for="on_commission">On Commission:</label>
<input type="number" step="0.01" id="on_commission" name="on_commission" placeholder="₹ - 00.00" readonly required> <input
type="number"
step="0.01"
id="on_commission"
name="on_commission"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div class="percentage-field"> <div class="percentage-field">
<label for="hydro_percentage">Hydro Testing %:</label> <label for="hydro_percentage">Hydro Testing %:</label>
<input type="number" step="0.01" id="hydro_percentage" name="hydro_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="hydro_percentage"
name="hydro_percentage"
placeholder="%"
/>
<label for="hydro_testing">Hydro Testing:</label> <label for="hydro_testing">Hydro Testing:</label>
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" placeholder="₹ - 00.00" readonly required> <input
type="number"
step="0.01"
id="hydro_testing"
name="hydro_testing"
placeholder="₹ - 00.00"
readonly
required
/>
</div>
</div> </div>
</div>
<div class="hold-row"> <div class="hold-row">
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button> <button type="button" id="add_hold_amount" class="button">
+ Add Hold Amount
</button>
</div> </div>
<!-- Dynamically added hold amount fields --> <!-- Dynamically added hold amount fields -->
@@ -150,23 +277,42 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="gst_sd_amount">GST SD Amount:</label> <label for="gst_sd_amount">GST SD Amount:</label>
<input type="number" step="0.01" id="gst_sd_amount" name="gst_sd_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="gst_sd_amount"
name="gst_sd_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
<div> <div>
<label for="final_amount">Final Amount:</label> <label for="final_amount">Final Amount:</label>
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="final_amount"
name="final_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
</div> </div>
<button type="submit" class="button">Submit</button> <button type="submit" class="button">Submit</button>
</form> </form>
</div> </div>
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none">
<!-- Invoice Table Section --> <!-- Invoice Table Section -->
<div class="search-container"> <div class="search-container">
<h2>Invoice List</h2> <h2>Invoice List</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input
type="text"
id="searchBar"
placeholder="Searching..."
onkeyup="searchTable()"
/>
{% if invoices %} {% if invoices %}
<table class="invoice-table"> <table class="invoice-table">
<thead> <thead>
@@ -218,14 +364,27 @@
<td>{{ invoice.Final_Amount }}</td> <td>{{ invoice.Final_Amount }}</td>
<td> <td>
<!-- Edit --> <!-- Edit -->
<a href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"> <a
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"> href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"
>
<img
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
alt="Edit"
class="icon edit-btn"
data-id="{{ invoice.Invoice_Id }}"
/>
</a> </a>
</td> </td>
<td> <td>
<!-- Delete --> <a
<a href="{{ url_for('invoice.delete_invoice_route', invoice_id=invoice.Invoice_Id) }}" onclick="return confirm('Are you sure?')"> href="javascript:void(0);"
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon"> onclick="deleteInvoice({{ invoice.Invoice_Id }}, this)"
>
<img
src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
alt="Delete"
class="icon"
/>
</a> </a>
</td> </td>
</tr> </tr>
@@ -235,29 +394,31 @@
{% else %} {% else %}
<p>No invoices found.</p> <p>No invoices found.</p>
{% endif %} {% endif %}
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener("DOMContentLoaded", function () {
// Get all the input fields // Get all the input fields
const basicAmount = document.getElementById('basic_amount'); const basicAmount = document.getElementById("basic_amount");
const debitAmount = document.getElementById('debit_amount'); const debitAmount = document.getElementById("debit_amount");
const afterDebitAmount = document.getElementById('after_debit_amount'); const afterDebitAmount = document.getElementById("after_debit_amount");
const amount = document.getElementById('amount'); const amount = document.getElementById("amount");
// Percentage fields // Percentage fields
const gstPercentage = document.getElementById('gst_percentage'); const gstPercentage = document.getElementById("gst_percentage");
const gstAmount = document.getElementById('gst_amount'); const gstAmount = document.getElementById("gst_amount");
const tdsPercentage = document.getElementById('tds_percentage'); const tdsPercentage = document.getElementById("tds_percentage");
const tdsAmount = document.getElementById('tds_amount'); const tdsAmount = document.getElementById("tds_amount");
const sdPercentage = document.getElementById('sd_percentage'); const sdPercentage = document.getElementById("sd_percentage");
const sdAmountInput = document.getElementById('sd_amount'); const sdAmountInput = document.getElementById("sd_amount");
const commissionPercentage = document.getElementById('commission_percentage'); const commissionPercentage = document.getElementById(
const onCommission = document.getElementById('on_commission'); "commission_percentage",
const hydroPercentage = document.getElementById('hydro_percentage'); );
const hydroTesting = document.getElementById('hydro_testing'); const onCommission = document.getElementById("on_commission");
const gstSdAmount = document.getElementById('gst_sd_amount'); const hydroPercentage = document.getElementById("hydro_percentage");
const finalAmount = document.getElementById('final_amount'); const hydroTesting = document.getElementById("hydro_testing");
const gstSdAmount = document.getElementById("gst_sd_amount");
const finalAmount = document.getElementById("final_amount");
// Calculate after debit amount when basic or debit amount changes // Calculate after debit amount when basic or debit amount changes
function calculateAfterDebitAmount() { function calculateAfterDebitAmount() {
@@ -333,7 +494,9 @@ document.addEventListener('DOMContentLoaded', function() {
// Get hold amounts // Get hold amounts
let totalHold = 0; let totalHold = 0;
document.querySelectorAll('input[name="hold_amount[]"]').forEach(input => { document
.querySelectorAll('input[name="hold_amount[]"]')
.forEach((input) => {
totalHold += parseFloat(input.value) || 0; totalHold += parseFloat(input.value) || 0;
}); });
@@ -343,28 +506,31 @@ document.addEventListener('DOMContentLoaded', function() {
} }
// Add event listeners // Add event listeners
basicAmount.addEventListener('input', calculateAfterDebitAmount); basicAmount.addEventListener("input", calculateAfterDebitAmount);
debitAmount.addEventListener('input', calculateAfterDebitAmount); debitAmount.addEventListener("input", calculateAfterDebitAmount);
// Percentage fields // Percentage fields
gstPercentage.addEventListener('input', calculateGST); gstPercentage.addEventListener("input", calculateGST);
tdsPercentage.addEventListener('input', calculateOtherDeductions); tdsPercentage.addEventListener("input", calculateOtherDeductions);
sdPercentage.addEventListener('input', calculateOtherDeductions); sdPercentage.addEventListener("input", calculateOtherDeductions);
commissionPercentage.addEventListener('input', calculateOtherDeductions); commissionPercentage.addEventListener(
hydroPercentage.addEventListener('input', calculateOtherDeductions); "input",
calculateOtherDeductions,
);
hydroPercentage.addEventListener("input", calculateOtherDeductions);
// Listen for changes in hold amounts // Listen for changes in hold amounts
document.addEventListener('holdAmountChanged', calculateFinalAmount); document.addEventListener("holdAmountChanged", calculateFinalAmount);
}); });
// Optional JS for auto-hiding flash // Optional JS for auto-hiding flash
setTimeout(() => { setTimeout(() => {
document.querySelectorAll('.alert').forEach(el => el.style.display = 'none'); document
}, 5000); .querySelectorAll(".alert")
</script> .forEach((el) => (el.style.display = "none"));
}, 5000);
</script>
</div> </div>
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -8,22 +9,23 @@
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script> <script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
</head> </head>
<body> <body>
<div class="button-container"> <div class="button-container">
<button id="addButton" class="action-button">Add</button> <button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button> <button id="displayButton" class="action-button">Display</button>
</div> </div>
<div id="addForm" style="display: none;"> <div id="addForm" style="display: none;">
<h2>Add Payment</h2> <h2>Add Payment</h2>
<form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)"> <form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)">
<div class="row1"> <div class="row1">
<div> <div>
<label for="subcontractor">Subcontractor Name:</label> <label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/> <input type="text" id="subcontractor" name="subcontractor" required autocomplete="off" />
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/> <input type="hidden" id="subcontractor_id" name="subcontractor_id" />
<div id="subcontractor_list" class="autocomplete-items"></div> <div id="subcontractor_list" class="autocomplete-items"></div>
</div> </div>
</div> </div>
@@ -34,19 +36,22 @@
</select><br><br> </select><br><br>
<label for="invoice_No">Invoice No:</label><br> <label for="invoice_No">Invoice No:</label><br>
<input type="number" step="0.01" id="invoice_No" name="invoice_No" ><br><br> <input type="number" step="0.01" id="invoice_No" name="invoice_No"><br><br>
<label for="Payment_Amount">Amount:</label><br> <label for="Payment_Amount">Amount:</label><br>
<input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required oninput="calculateTDSAndTotal()"><br><br> <input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required
oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Percentage">TDS Percentage (%):</label><br> <label for="TDS_Percentage">TDS Percentage (%):</label><br>
<input type="number" step="0.01" id="TDS_Percentage" name="TDS_Percentage" oninput="calculateTDSAndTotal()"><br><br> <input type="number" step="0.01" id="TDS_Percentage" name="TDS_Percentage"
oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Payment_Amount">TDS Amount:</label><br> <label for="TDS_Payment_Amount">TDS Amount:</label><br>
<input type="number" step="0.01" id="TDS_Payment_Amount" name="TDS_Payment_Amount" required readonly><br><br> <input type="number" step="0.01" id="TDS_Payment_Amount" name="TDS_Payment_Amount" required
readonly><br><br>
<label for="total_amount">Total Amount:</label><br> <label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br> <input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br>
<label for="utr">UTR:</label><br> <label for="utr">UTR:</label><br>
@@ -54,13 +59,13 @@
<button type="submit">Submit Payment</button> <button type="submit">Submit Payment</button>
</form> </form>
</div> </div>
<div id="successPopup" class="success-popup"> <div id="successPopup" class="success-popup">
<i>&#10004;</i> Payment added successfully! <i>&#10004;</i> Payment added successfully!
</div> </div>
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none;">
<div class="search-container"> <div class="search-container">
<h2>Payment History</h2> <h2>Payment History</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
@@ -89,8 +94,17 @@
<td>{{ payment[4] }}</td> <td>{{ payment[4] }}</td>
<td>{{ payment[5] }}</td> <td>{{ payment[5] }}</td>
<td>{{ payment[6] }}</td> <td>{{ payment[6] }}</td>
<td><a href="/edit_payment/{{ payment[0] }}"><img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"></a></td> <td><a href="/edit_payment/{{ payment[0] }}"><img
<td><a href="/delete_payment/{{ payment[0] }}" onclick="return confirm('Are you sure you want to delete this payment?')"><img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon"></a></td> src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
class="icon"></a></td>
<td>
<a href="{{ url_for('payment_bp.delete_payment', payment_id=payment[0]) }}"
onclick="return confirm('Are you sure you want to delete this Payment?')">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
class="icon">
</a>
</td>
</tr> </tr>
<!-- <tr> <!-- <tr>
<td>{{ payment['Payment_Id'] }}</td> <td>{{ payment['Payment_Id'] }}</td>
@@ -106,10 +120,10 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
<script> <script>
document.getElementById("subcontractor").addEventListener("input", function () { document.getElementById("subcontractor").addEventListener("input", function () {
const query = this.value; const query = this.value;
const list = document.getElementById("subcontractor_list"); const list = document.getElementById("subcontractor_list");
@@ -129,9 +143,9 @@ document.getElementById("subcontractor").addEventListener("input", function () {
list.appendChild(div); list.appendChild(div);
}); });
}); });
}); });
document.getElementById("subcontractor_list").addEventListener("click", function (e) { document.getElementById("subcontractor_list").addEventListener("click", function (e) {
const selectedId = e.target.getAttribute("data-id"); const selectedId = e.target.getAttribute("data-id");
const selectedName = e.target.textContent; const selectedName = e.target.textContent;
@@ -171,11 +185,11 @@ document.getElementById("subcontractor_list").addEventListener("click", function
alert("Failed to fetch PMC numbers."); alert("Failed to fetch PMC numbers.");
}); });
} }
}); });
</script> </script>
<script> <script>
function calculateTDSAndTotal() { function calculateTDSAndTotal() {
const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0; const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0;
const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0; const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0;
@@ -184,10 +198,10 @@ function calculateTDSAndTotal() {
document.getElementById("TDS_Payment_Amount").value = tdsAmount; document.getElementById("TDS_Payment_Amount").value = tdsAmount;
document.getElementById("total_amount").value = totalAmount; document.getElementById("total_amount").value = totalAmount;
} }
</script> </script>
<script src="{{ url_for('static', filename='js/showSuccessAlert.js') }}"></script> <script src="{{ url_for('static', filename='js/showSuccessAlert.js') }}"></script>
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -1,22 +1,24 @@
{% extends 'base.html' %} {% extends 'base.html' %} {% block content %}
{% block content %}
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Village Management</title> <title>Village Management</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='css/style.css') }}"
/>
<script src="{{ url_for('static', filename='js/village.js') }}"></script> <script src="{{ url_for('static', filename='js/village.js') }}"></script>
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head> </head>
<body> <body>
<!-- Button Container to Center Buttons -->
<!-- Button Container to Center Buttons --> <div class="button-container">
<div class="button-container">
<button id="addButton" class="action-button">Add</button> <button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button> <button id="displayButton" class="action-button">Display</button>
</div> </div>
<div id="addForm" style="display: none;"> <div id="addForm" style="display: none">
<div class="container"> <div class="container">
<div class="form-block"> <div class="form-block">
<h2>Add a New Village</h2> <h2>Add a New Village</h2>
@@ -40,19 +42,30 @@
</select> </select>
<label for="Village_Name">Village Name:</label> <label for="Village_Name">Village Name:</label>
<input type="text" id="Village_Name" name="Village_Name" placeholder="Enter Village Name" required> <input
type="text"
id="Village_Name"
name="Village_Name"
placeholder="Enter Village Name"
required
/>
<span id="villageMessage"></span> <span id="villageMessage"></span>
<button type="submit" id="submitVillage" disabled>Add Village</button> <button type="submit" id="submitVillage" disabled>Add Village</button>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none">
<div class="search-container"> <div class="search-container">
<h2>Display Villages</h2> <h2>Display Villages</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input
type="text"
id="searchBar"
placeholder="Searching..."
onkeyup="searchTable()"
/>
</div> </div>
<table id="sortableTable" border="1"> <table id="sortableTable" border="1">
@@ -63,12 +76,15 @@
<span class="sort-buttons"> <span class="sort-buttons">
<span class="sort-asc">⬆️</span> <span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span> <span class="sort-desc">⬇️</span>
</span></th> </span>
<th class="sortable-header">Block Name </th>
<th class="sortable-header">
Block Name
<span class="sort-buttons"> <span class="sort-buttons">
<span class="sort-asc">⬆️</span> <span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span> <span class="sort-desc">⬇️</span>
</span></th> </span>
</th>
<th>Update</th> <th>Update</th>
<th>Delete</th> <th>Delete</th>
</tr> </tr>
@@ -78,22 +94,36 @@
<td>{{ village[1] }}</td> <td>{{ village[1] }}</td>
<td>{{ village[2] }}</td> <td>{{ village[2] }}</td>
<td> <td>
<a href="{{ url_for('village.edit_village', village_id=village[0]) }}"> <a
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" href="{{ url_for('village.edit_village', village_id=village[0]) }}"
class="icon"> >
<img
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
alt="Edit"
class="icon"
/>
</a> </a>
</td> </td>
<td> <td>
<a href="{{ url_for('village.delete_village', village_id=village[0]) }}" <a href="javascript:void(0);"
onclick="return confirm('Are you sure you want to delete this village?');"> onclick="deleteVillage({{ village[0] }}, this)">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" <img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
class="icon"> class="icon">
</a> </a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<!-- Flash Alerts -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<script>
{% for category, message in messages %}
alert("{{ message }}");
{% endfor %}
</script>
{% endif %}
{% endwith %}
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -9,37 +9,37 @@
<body> <body>
<h2>Edit GST Release</h2> <h2>Edit GST Release</h2>
<form action="/edit_gst_release/{{ gst_release_data[0] }}" method="POST"> <form action="/edit_gst_release/{{ gst_release_data.gst_release_id }}" method="POST">
<!-- <label for="invoice_id">Invoice Id:</label><br>-->
<!-- <input type="number" id="invoice_id" name="invoice_id" value="{{ gst_release_data[0] }}" required><br><br>-->
<!-- PMC Number -->
<label for="PMC_No">PMC No :</label><br> <label for="PMC_No">PMC No :</label><br>
<input type="number" id="PMC_No" name="PMC_No" value="{{ gst_release_data[1] }}" required><br><br> <input type="text" id="PMC_No" name="PMC_No" value="{{ gst_release_data.pmc_no }}" required><br><br>
<label for="invoice_No">Invoice No:</label><br> <!-- Invoice Number -->
<input type="number" step="0.01" id="invoice_No" name="invoice_No" value="{{ gst_release_data[2] }}" <label for="invoice_no">Invoice No:</label><br>
required><br><br> <input type="text" id="invoice_no" name="invoice_no" value="{{ gst_release_data.invoice_no }}" required><br><br>
<!-- Basic Amount -->
<label for="Basic_Amount">Basic Amount:</label><br>
<input type="number" step="0.01" id="Basic_Amount" name="Basic_Amount" value="{{ gst_release_data.basic_amount }}" required><br><br>
<label for="basic_amount">Basic Amount:</label><br> <!-- Final Amount -->
<input type="number" step="0.01" id="basic_amount" name="basic_amount" value="{{ gst_release_data[3] }}" <label for="Final_Amount">Final Amount:</label><br>
required><br><br> <input type="number" step="0.01" id="Final_Amount" name="Final_Amount" value="{{ gst_release_data.final_amount }}" required><br><br>
<label for="final_amount">Final Amount:</label><br> <!-- Total Amount -->
<input type="number" step="0.01" id="final_amount" name="final_amount" value="{{ gst_release_data[4] }}" <label for="Total_Amount">Total Amount:</label><br>
required><br><br> <input type="number" step="0.01" id="Total_Amount" name="Total_Amount" value="{{ gst_release_data.total_amount }}" required><br><br>
<label for="total_amount">Total Amount:</label><br> <!-- UTR -->
<input type="number" step="0.01" id="total_amount" name="total_amount" value="{{ gst_release_data[5] }}" <label for="UTR">UTR:</label><br>
required><br><br> <input type="text" id="UTR" name="UTR" value="{{ gst_release_data.utr }}" readonly required><br><br>
<label for="utr">UTR:</label><br> <!-- Hidden Contractor ID -->
<input type="text" id="utr" name="utr" value="{{ gst_release_data[6] }}" <input type="hidden" id="Contractor_ID" name="Contractor_ID" value="{{ gst_release_data.contractor_id }}">
required readonly><br><br>
<button type="submit">Update GST Release</button> <button type="submit">Update GST Release</button>
</form> </form>
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<head> <head>
<title>Edit Hold Type</title> <title>Edit Hold Type</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
@@ -10,10 +10,10 @@
<body> <body>
<h2>Edit Hold Type</h2> <h2>Edit Hold Type</h2>
<form id="updateHoldTypeForm"> <form id="updateHoldTypeForm" method="POST">
<input type="hidden" id="hold_type_id" value="{{ hold_type[0] }}"> <input type="hidden" id="hold_type_id" value="{{ hold_type.hold_type_id }}">
<label for="edit_hold_type">Hold Type:</label> <label for="edit_hold_type">Hold Type:</label>
<input type="text" id="edit_hold_type" name="hold_type" value="{{ hold_type[1] }}" required> <input type="text" id="edit_hold_type" name="hold_type" value="{{ hold_type.hold_type }}" required>
<button type="submit">Update</button> <button type="submit">Update</button>
<a href="{{ url_for('hold_types.add_hold_type') }}"></a> <a href="{{ url_for('hold_types.add_hold_type') }}"></a>
</form> </form>
@@ -37,7 +37,7 @@
formData.append('hold_type', newHoldType); formData.append('hold_type', newHoldType);
$.ajax({ $.ajax({
url: '/update_hold_type/' + holdTypeId, url: '/edit_hold_type/' + holdTypeId,
method: 'POST', method: 'POST',
data: formData, data: formData,
processData: false, processData: false,
@@ -55,8 +55,3 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -2,17 +2,18 @@
{% block content %} {% block content %}
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Edit Invoice</title> <title>Edit Invoice</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style1.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style1.css') }}">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head> </head>
<body> <body>
<div id="editForm"> <div id="editForm">
<h2>Edit Invoice</h2> <h2>Edit Invoice</h2>
<!-- Success Alert Box --> <!-- Success Alert Box -->
@@ -20,7 +21,8 @@
Invoice successfully updated! Invoice successfully updated!
</div> </div>
<form id="invoiceForm" action="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}" method="POST"> <form id="invoiceForm" action="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"
method="POST">
<!-- Subcontractor Field --> <!-- Subcontractor Field -->
<div class="row1"> <div class="row1">
@@ -28,7 +30,8 @@
<label for="subcontractor">Subcontractor Name:</label> <label for="subcontractor">Subcontractor Name:</label>
<input class="form-control" list="subcontractor_list" id="subcontractor" name="subcontractor" <input class="form-control" list="subcontractor_list" id="subcontractor" name="subcontractor"
value="{{ invoice.Contractor_Name }}" placeholder="Type to search..." required> value="{{ invoice.Contractor_Name }}" placeholder="Type to search..." required>
<input type="hidden" id="subcontractor_id" name="subcontractor_id" value="{{ invoice.Subcontractor_Id }}"> <input type="hidden" id="subcontractor_id" name="subcontractor_id"
value="{{ invoice.Subcontractor_Id }}">
<datalist id="subcontractor_list"></datalist> <datalist id="subcontractor_list"></datalist>
</div> </div>
</div> </div>
@@ -37,12 +40,12 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="village">Village Name:</label> <label for="village">Village Name:</label>
<input type="text" id="village" name="village" value="{{ invoice.Village_Name }}" required/> <input type="text" id="village" name="village" value="{{ invoice.Village_Name }}" required />
<datalist id="village_list"></datalist> <datalist id="village_list"></datalist>
</div> </div>
<div> <div>
<label for="pmc_no">PMC No:</label> <label for="pmc_no">PMC No:</label>
<input type="text" id="pmc_no" name="pmc_no" value="{{ invoice.PMC_No }}" required/> <input type="text" id="pmc_no" name="pmc_no" value="{{ invoice.PMC_No }}" required />
</div> </div>
</div> </div>
@@ -50,11 +53,12 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="work_type">Work Type:</label> <label for="work_type">Work Type:</label>
<input type="text" id="work_type" name="work_type" value="{{ invoice.Work_Type }}" required/> <input type="text" id="work_type" name="work_type" value="{{ invoice.Work_Type }}" required />
</div> </div>
<div> <div>
<label for="invoice_details">Invoice Details:</label> <label for="invoice_details">Invoice Details:</label>
<textarea id="invoice_details" name="invoice_details" required>{{ invoice.Invoice_Details }}</textarea> <textarea id="invoice_details" name="invoice_details"
required>{{ invoice.Invoice_Details }}</textarea>
</div> </div>
</div> </div>
@@ -62,11 +66,12 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="invoice_no">Invoice No:</label> <label for="invoice_no">Invoice No:</label>
<input type="text" id="invoice_no" name="invoice_no" value="{{ invoice.Invoice_No }}" required/> <input type="text" id="invoice_no" name="invoice_no" value="{{ invoice.invoice_no }}" required />
</div> </div>
<div> <div>
<label for="invoice_date">Invoice Date:</label> <label for="invoice_date">Invoice Date:</label>
<input type="date" id="invoice_date" name="invoice_date" value="{{ invoice.Invoice_Date }}" required/> <input type="date" id="invoice_date" name="invoice_date" value="{{ invoice.Invoice_Date }}"
required />
</div> </div>
</div> </div>
@@ -74,15 +79,18 @@
<div class="row3"> <div class="row3">
<div> <div>
<label for="basic_amount">Basic Amount:</label> <label for="basic_amount">Basic Amount:</label>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" value="{{ invoice.Basic_Amount }}" required/> <input type="number" step="0.01" id="basic_amount" name="basic_amount"
value="{{ invoice.Basic_Amount }}" required />
</div> </div>
<div> <div>
<label for="debit_amount">Debit Amount:</label> <label for="debit_amount">Debit Amount:</label>
<input type="number" step="0.01" id="debit_amount" name="debit_amount" value="{{ invoice.Debit_Amount }}" required/> <input type="number" step="0.01" id="debit_amount" name="debit_amount"
value="{{ invoice.Debit_Amount }}" required />
</div> </div>
<div> <div>
<label for="after_debit_amount">After Debit Amount:</label> <label for="after_debit_amount">After Debit Amount:</label>
<input type="number" step="0.01" id="after_debit_amount" name="after_debit_amount" value="{{ invoice.After_Debit_Amount }}" required/> <input type="number" step="0.01" id="after_debit_amount" name="after_debit_amount"
value="{{ invoice.After_Debit_Amount }}" required />
</div> </div>
</div> </div>
@@ -90,15 +98,17 @@
<div class="row3"> <div class="row3">
<div> <div>
<label for="amount">Amount:</label> <label for="amount">Amount:</label>
<input type="number" step="0.01" id="amount" name="amount" value="{{ invoice.Amount }}" required/> <input type="number" step="0.01" id="amount" name="amount" value="{{ invoice.Amount }}" required />
</div> </div>
<div> <div>
<label for="gst_amount">GST Amount:</label> <label for="gst_amount">GST Amount:</label>
<input type="number" step="0.01" id="gst_amount" name="gst_amount" value="{{ invoice.GST_Amount }}" required/> <input type="number" step="0.01" id="gst_amount" name="gst_amount" value="{{ invoice.GST_Amount }}"
required />
</div> </div>
<div> <div>
<label for="tds_amount">TDS Amount:</label> <label for="tds_amount">TDS Amount:</label>
<input type="number" step="0.01" id="tds_amount" name="tds_amount" value="{{ invoice.TDS_Amount }}" required/> <input type="number" step="0.01" id="tds_amount" name="tds_amount" value="{{ invoice.TDS_Amount }}"
required />
</div> </div>
</div> </div>
@@ -106,34 +116,39 @@
<div class="row3"> <div class="row3">
<div> <div>
<label for="sd_amount">SD Amount:</label> <label for="sd_amount">SD Amount:</label>
<input type="number" step="0.01" id="sd_amount" name="sd_amount" value="{{ invoice.SD_Amount }}" required/> <input type="number" step="0.01" id="sd_amount" name="sd_amount" value="{{ invoice.SD_Amount }}"
required />
</div> </div>
<div> <div>
<label for="on_commission">On Commission:</label> <label for="on_commission">On Commission:</label>
<input type="number" step="0.01" id="on_commission" name="on_commission" value="{{ invoice.On_Commission }}" required/> <input type="number" step="0.01" id="on_commission" name="on_commission"
value="{{ invoice.On_Commission }}" required />
</div> </div>
<div> <div>
<label for="hydro_testing">Hydro Testing:</label> <label for="hydro_testing">Hydro Testing:</label>
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" value="{{ invoice.Hydro_Testing }}" required/> <input type="number" step="0.01" id="hydro_testing" name="hydro_testing"
value="{{ invoice.Hydro_Testing }}" required />
</div> </div>
</div> </div>
<!-- Hold Amount Section --> <!-- Hold Amount Section -->
<div id="hold_amount_container"> <div id="hold_amount_container">
{% set seen_types = [] %} {% set seen_types = [] %}
{% for hold in invoice.hold_amounts %} {% for hold in invoice.hold_amounts %}
{% if hold.hold_type not in seen_types %} {% if hold.hold_type not in seen_types %}
{% set _ = seen_types.append(hold.hold_type) %} {% set _ = seen_types.append(hold.hold_type) %}
<div class="hold-amount-row"> <div class="hold-amount-row">
<label for="hold_type_{{ loop.index }}">Hold Type:</label> <label for="hold_type_{{ loop.index }}">Hold Type:</label>
<input type="text" id="hold_type_{{ loop.index }}" name="hold_type[]" value="{{ hold.hold_type }}" required/> <input type="text" id="hold_type_{{ loop.index }}" name="hold_type[]" value="{{ hold.hold_type }}"
required />
<label for="hold_amount_{{ loop.index }}">Hold Amount:</label> <label for="hold_amount_{{ loop.index }}">Hold Amount:</label>
<input type="number" step="0.01" id="hold_amount_{{ loop.index }}" name="hold_amount[]" value="{{ hold.hold_amount }}" required/> <input type="number" step="0.01" id="hold_amount_{{ loop.index }}" name="hold_amount[]"
value="{{ hold.hold_amount }}" required />
<button type="button" class="remove-hold-amount">Remove</button> <button type="button" class="remove-hold-amount">Remove</button>
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
<div class="hold-row"> <div class="hold-row">
@@ -144,24 +159,26 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="gst_sd_amount">GST SD Amount:</label> <label for="gst_sd_amount">GST SD Amount:</label>
<input type="number" step="0.01" id="gst_sd_amount" name="gst_sd_amount" value="{{ invoice.GST_SD_Amount }}" required/> <input type="number" step="0.01" id="gst_sd_amount" name="gst_sd_amount"
value="{{ invoice.GST_SD_Amount }}" required />
</div> </div>
<div> <div>
<label for="final_amount">Final Amount:</label> <label for="final_amount">Final Amount:</label>
<input type="number" step="0.01" id="final_amount" name="final_amount" value="{{ invoice.Final_Amount }}" required/> <input type="number" step="0.01" id="final_amount" name="final_amount"
value="{{ invoice.Final_Amount }}" required />
</div> </div>
</div> </div>
<!-- Submit Button --> <!-- Submit Button -->
<button type="submit" class="button">Update</button> <button type="submit" class="button">Update</button>
</form> </form>
</div> </div>
<!-- JS for dynamic hold amount rows --> <!-- JS for dynamic hold amount rows -->
<script> <script>
$(document).ready(function() { $(document).ready(function () {
// Add new hold amount row // Add new hold amount row
$("#add_hold_amount").click(function() { $("#add_hold_amount").click(function () {
const index = $("#hold_amount_container .hold-amount-row").length; const index = $("#hold_amount_container .hold-amount-row").length;
const newRow = ` const newRow = `
<div class="hold-amount-row"> <div class="hold-amount-row">
@@ -176,32 +193,34 @@ $(document).ready(function() {
}); });
// Remove hold amount row // Remove hold amount row
$(document).on("click", ".remove-hold-amount", function() { $(document).on("click", ".remove-hold-amount", function () {
$(this).closest(".hold-amount-row").remove(); $(this).closest(".hold-amount-row").remove();
}); });
// Submit form via AJAX // Submit form via AJAX
$("#invoiceForm").submit(function(e) { $("#invoiceForm").submit(function (e) {
e.preventDefault(); e.preventDefault();
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: $(this).attr("action"), url: $(this).attr("action"),
data: $(this).serialize(), data: $(this).serialize(),
success: function(response) { dataType: 'json', // ensure JSON is returned
if(response.status === "success") { success: function (response) {
$("#invoiceSuccessAlert").fadeIn().delay(3000).fadeOut(); if (response.status === "success") {
alert("Invoice updated successfully!"); // <-- Popup alert alert("Invoice updated successfully!"); // <-- Popup alert
}
// ✅ Redirect to Add Invoice page (table part visible)
window.location.href = "{{ url_for('invoice.add_invoice') }}#addTable";
}
}, },
error: function(xhr) { error: function (xhr) {
alert("Error: " + xhr.responseJSON.message); alert("Error: " + xhr.responseJSON?.message || "Something went wrong!");
} }
}); });
}); });
}); });
</script> </script>
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -28,25 +28,17 @@
<button type="submit">Update Village</button> <button type="submit">Update Village</button>
</form> </form>
</div> <!-- Flash Messages (hidden, used for JS popup) -->
<div id="flash-messages-container" style="display:none;">
<!-- Flash Message (Hidden, used for JS popup) -->
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}
{% for category, message in messages %} {% for category, message in messages %}
<div id="flash-message" data-category="{{ category }}" style="display:none;">{{ message }}</div> <div class="flash-message" data-category="{{ category }}">{{ message }}</div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endwith %} {% endwith %}
</div> </div>
<script>
window.onload = function () {
const flash = document.getElementById('flash-message');
if (flash && flash.innerText.trim() !== "") {
alert(flash.innerText);
}
};
</script>
</body> </body>
{% endblock %} {% endblock %}