Compare commits
14 Commits
laxmi-dev
...
swapnil-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d84ba520cf | ||
| 0aeaf775dd | |||
| 88e8771b51 | |||
| 6c74b5d3bf | |||
| 47ba78d72c | |||
| 1946a98d59 | |||
| 8f35e08429 | |||
|
|
91b078a0c3 | ||
|
|
eb46929893 | ||
|
|
14e799a1d4 | ||
| 8ab1b69033 | |||
| dbeec9962d | |||
| 8750f268db | |||
|
|
b78526ad9f |
@@ -10,21 +10,21 @@ hold_bp = Blueprint("hold_types", __name__)
|
||||
@hold_bp.route('/add_hold_type', methods=['GET','POST'])
|
||||
@login_required
|
||||
def add_hold_type():
|
||||
|
||||
hold = HoldTypes()
|
||||
hold = HoldTypes()
|
||||
|
||||
if request.method == 'POST':
|
||||
hold.AddHoldType(request) # ✅
|
||||
return hold.resultMessage
|
||||
hold.AddHoldType(request)
|
||||
# ✅ 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(
|
||||
"add_hold_type.html",
|
||||
Hold_Types_data=hold_types
|
||||
)
|
||||
|
||||
|
||||
# ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ----------------
|
||||
@hold_bp.route('/check_hold_type', methods=['POST'])
|
||||
@login_required
|
||||
|
||||
@@ -21,7 +21,6 @@ def activity_log():
|
||||
end_date,
|
||||
user_name
|
||||
)
|
||||
|
||||
return render_template(
|
||||
"activity_log.html",
|
||||
logs=filtered_logs,
|
||||
|
||||
@@ -83,17 +83,29 @@ def check_village():
|
||||
return village.CheckVillage(request=request)
|
||||
|
||||
|
||||
# ------------------------- Delete Village -------------------------
|
||||
@village_bp.route('/delete_village/<int:village_id>')
|
||||
@login_required
|
||||
def delete_village(village_id):
|
||||
|
||||
village = Village()
|
||||
village.DeleteVillage(request=request, village_id=village_id)
|
||||
|
||||
flash(village.resultMessage, "success" if village.isSuccess else "error")
|
||||
return redirect(url_for('village.add_village'))
|
||||
# ✅ Convert resultMessage to string if it's a Response or tuple
|
||||
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 -------------------------
|
||||
@village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])
|
||||
|
||||
@@ -27,13 +27,27 @@ class FolderAndFile:
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
return folder
|
||||
|
||||
# -----------------------------
|
||||
# FILE PATH METHODS
|
||||
# -----------------------------
|
||||
@staticmethod
|
||||
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
|
||||
def get_download_path(filename):
|
||||
return os.path.join(FolderAndFile.get_download_folder(), filename)
|
||||
|
||||
|
||||
# FILE PATH METHODS - upload file
|
||||
@staticmethod
|
||||
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)
|
||||
@@ -66,7 +66,7 @@ def get_all_villages():
|
||||
return fetch_all(cursor)
|
||||
return execute_db_operation(operation)
|
||||
|
||||
|
||||
|
||||
# ------------------- Invoice Functions -------------------
|
||||
def insert_invoice(data, village_id):
|
||||
def operation(cursor):
|
||||
|
||||
@@ -167,7 +167,7 @@ class ItemCRUD:
|
||||
# ======================================================
|
||||
# NORMAL SINGLE-FIELD (Village / Block / State)
|
||||
# ======================================================
|
||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
||||
if not re.match(RegEx.allPattern, childname):
|
||||
self.isSuccess = False
|
||||
self.resultMessage = HtmlHelper.json_response(
|
||||
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
|
||||
@@ -232,14 +232,13 @@ class ItemCRUD:
|
||||
if self.itemCRUDType.name == "GSTRelease" and data:
|
||||
|
||||
cursor.callproc(storedprocupdate, (
|
||||
childid,
|
||||
data['PMC_No'],
|
||||
data['Invoice_No'],
|
||||
data['Basic_Amount'],
|
||||
data['Final_Amount'],
|
||||
data['Total_Amount'],
|
||||
data['UTR'],
|
||||
data['Contractor_ID']
|
||||
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()
|
||||
|
||||
@@ -276,7 +275,7 @@ class ItemCRUD:
|
||||
# ======================================================
|
||||
# NORMAL SINGLE-FIELD
|
||||
# ======================================================
|
||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
||||
if not re.match(RegEx.allPattern, childname):
|
||||
self.isSuccess = False
|
||||
self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message']
|
||||
return
|
||||
@@ -364,7 +363,7 @@ class ItemCRUD:
|
||||
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(
|
||||
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
|
||||
)
|
||||
|
||||
118
model/Log.py
118
model/Log.py
@@ -1,29 +1,41 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
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:
|
||||
@staticmethod
|
||||
def log_action(action, details=""):
|
||||
"""Add a log entry."""
|
||||
log_data = LogData()
|
||||
log_data.add_log(action, details)
|
||||
|
||||
"""Log user actions with timestamp, user, action, and details."""
|
||||
logData = LogData()
|
||||
logData.WriteLog(action, details="")
|
||||
|
||||
class LogData:
|
||||
filepath = ""
|
||||
timestamp = None
|
||||
|
||||
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.user = getattr(current_user, "cn", None) \
|
||||
or getattr(current_user, "username", None) \
|
||||
or getattr(current_user, "sAMAccountName", "Unknown")
|
||||
self.user = LogData.get_current_user()
|
||||
|
||||
|
||||
def add_log(self, action, details=""):
|
||||
"""Create/Add a log entry."""
|
||||
@staticmethod
|
||||
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:
|
||||
f.write(
|
||||
f"Timestamp: {self.timestamp} | "
|
||||
@@ -32,73 +44,41 @@ class LogData:
|
||||
f"Details: {details}\n"
|
||||
)
|
||||
|
||||
def get_all_logs(self):
|
||||
"""Read all logs."""
|
||||
def GetActivitiesLog(self):
|
||||
logs = []
|
||||
|
||||
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:
|
||||
parts = line.strip().split(" | ")
|
||||
if len(parts) == 4:
|
||||
logs.append({
|
||||
"timestamp": parts[0].split(":", 1)[1].strip(),
|
||||
"user": parts[1].split(":", 1)[1].strip(),
|
||||
"action": parts[2].split(":", 1)[1].strip(),
|
||||
"details": parts[3].split(":", 1)[1].strip()
|
||||
"timestamp": parts[0].replace("Timestamp:", "").strip(),
|
||||
"user": parts[1].replace("User:", "").strip(),
|
||||
"action": parts[2].replace("Action:", "").strip(),
|
||||
"details": parts[3].replace("Details:", "").strip()
|
||||
})
|
||||
return logs
|
||||
|
||||
def get_filtered_logs(self, start_date=None, end_date=None, user_name=None):
|
||||
"""Filter logs by date and/or user."""
|
||||
logs = self.get_all_logs()
|
||||
def GetFilteredActivitiesLog(self, startDate, endDate, userName):
|
||||
filtered_logs = self.GetActivitiesLog()
|
||||
|
||||
# Filter by date
|
||||
if start_date or end_date:
|
||||
start_dt = datetime.strptime(start_date, "%Y-%m-%d") if start_date else datetime.min
|
||||
end_dt = datetime.strptime(end_date, "%Y-%m-%d") if end_date else datetime.max
|
||||
logs = [
|
||||
log for log in logs
|
||||
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
||||
]
|
||||
# Date filter
|
||||
if startDate or endDate:
|
||||
try:
|
||||
start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
|
||||
end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
|
||||
|
||||
# Filter by username
|
||||
if user_name:
|
||||
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"
|
||||
)
|
||||
filtered_logs = [
|
||||
log for log in filtered_logs
|
||||
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
||||
]
|
||||
|
||||
except Exception as e:
|
||||
print("Date filter error:", e)
|
||||
|
||||
# Username filter
|
||||
if userName:
|
||||
filtered_logs = [log for log in filtered_logs if userName.lower() in log["user"].lower()]
|
||||
|
||||
return filtered_logs
|
||||
@@ -12,6 +12,7 @@ class ItemCRUDType(Enum):
|
||||
|
||||
class RegEx:
|
||||
patternAlphabetOnly = "^[A-Za-z ]+$"
|
||||
allPattern = "^(?!\s*$).+"
|
||||
|
||||
|
||||
class ResponseHandler:
|
||||
|
||||
@@ -8,12 +8,18 @@ class GSTRelease:
|
||||
def __init__(self):
|
||||
self.isSuccess = False
|
||||
self.resultMessage = ""
|
||||
|
||||
|
||||
# ------------------- Add GST Release -------------------
|
||||
def AddGSTRelease(self, request):
|
||||
try:
|
||||
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 = {
|
||||
"PMC_No": request.form.get("PMC_No", "").strip(),
|
||||
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
||||
@@ -24,6 +30,10 @@ class GSTRelease:
|
||||
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
|
||||
}
|
||||
|
||||
print("===== DEBUG: PARSED DATA =====")
|
||||
print(data)
|
||||
print("==============================")
|
||||
|
||||
# Add GST Release
|
||||
gst.AddItem(
|
||||
request=request,
|
||||
@@ -31,12 +41,8 @@ class GSTRelease:
|
||||
storedprocfetch="CheckGSTReleaseExists",
|
||||
storedprocadd="AddGSTReleaseFromExcel"
|
||||
)
|
||||
|
||||
# Check if addition was successful
|
||||
if gst.isSuccess:
|
||||
print(f"GST Release Added: {data}")
|
||||
else:
|
||||
print(f"Failed to add GST Release: {gst.resultMessage}")
|
||||
|
||||
print(f"AddItem result: isSuccess={gst.isSuccess}, message={gst.resultMessage}")
|
||||
|
||||
self.isSuccess = gst.isSuccess
|
||||
self.resultMessage = str(gst.resultMessage)
|
||||
@@ -48,20 +54,26 @@ class GSTRelease:
|
||||
|
||||
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
|
||||
|
||||
# ------------------- Edit GST Release -------------------
|
||||
def EditGSTRelease(self, request, gst_release_id):
|
||||
try:
|
||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||
|
||||
# Map form inputs to stored procedure parameters
|
||||
data = {
|
||||
"PMC_No": request.form.get("PMC_No", "").strip(),
|
||||
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
||||
"Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0),
|
||||
"Final_Amount": float(request.form.get("Final_Amount", 0) or 0),
|
||||
"Total_Amount": float(request.form.get("Total_Amount", 0) or 0),
|
||||
"UTR": request.form.get("UTR", "").strip()
|
||||
"p_pmc_no": request.form.get("PMC_No", "").strip(),
|
||||
"p_invoice_no": request.form.get("invoice_no", "").strip(),
|
||||
"p_basic_amount": float(request.form.get("Basic_Amount", 0) or 0),
|
||||
"p_final_amount": float(request.form.get("Final_Amount", 0) or 0),
|
||||
"p_total_amount": float(request.form.get("Total_Amount", 0) or 0),
|
||||
"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(
|
||||
request=request,
|
||||
childid=gst_release_id,
|
||||
@@ -77,8 +89,6 @@ class GSTRelease:
|
||||
self.isSuccess = False
|
||||
self.resultMessage = str(e)
|
||||
|
||||
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
|
||||
|
||||
# ------------------- Delete GST Release -------------------
|
||||
def DeleteGSTRelease(self, gst_release_id):
|
||||
try:
|
||||
|
||||
@@ -3,7 +3,7 @@ $(document).ready(function () {
|
||||
let holdType = $(this).val().replace(/^\s+/, "");
|
||||
$(this).val(holdType);
|
||||
|
||||
let reg = /^[A-Za-z]/;
|
||||
let reg = /^.+$/; // all pattern allow
|
||||
|
||||
if (!reg.test(holdType)) {
|
||||
$("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red");
|
||||
|
||||
@@ -1,62 +1,271 @@
|
||||
// Subcontractor autocomplete functionality
|
||||
$(document).ready(function () {
|
||||
$("#subcontractor").keyup(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 () {
|
||||
let query = $(this).val();
|
||||
if (query !== "") {
|
||||
$.ajax({
|
||||
url: "/search_subcontractor",
|
||||
method: "POST",
|
||||
data: { query: query },
|
||||
success: function (data) {
|
||||
$("#subcontractor_list").fadeIn().html(data);
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
url: "/search_subcontractor",
|
||||
method: "POST",
|
||||
data: { query: query },
|
||||
success: function (data) {
|
||||
$("#subcontractor_list").fadeIn().html(data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$("#subcontractor_list").fadeOut();
|
||||
$("#subcontractor_list").fadeOut();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on("click", "li", function () {
|
||||
$(document).on("click", "li", function () {
|
||||
$("#subcontractor").val($(this).text());
|
||||
$("#subcontractor_id").val($(this).attr("data-id"));
|
||||
$("#subcontractor_list").fadeOut();
|
||||
});
|
||||
});
|
||||
|
||||
// Success Alert: show alert and reload after 3 seconds
|
||||
function showSuccessAlert() {
|
||||
const alertBox = document.getElementById("invoiceSuccessAlert");
|
||||
alertBox.classList.add("show");
|
||||
setTimeout(() => {
|
||||
alertBox.classList.remove("show");
|
||||
// Reload page or redirect after alert hides
|
||||
window.location.href = '/add_invoice';
|
||||
}, 3000);
|
||||
// Focus
|
||||
if (document.getElementById('subcontractor')) {
|
||||
document.getElementById('subcontractor').focus();
|
||||
}
|
||||
|
||||
// Submit form via AJAX
|
||||
$("#invoiceForm").on("submit", function (e) {
|
||||
e.preventDefault();
|
||||
let formData = $(this).serialize();
|
||||
$.ajax({
|
||||
url: '/add_invoice',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
success: function (response) {
|
||||
if(response.status === "success") {
|
||||
showSuccessAlert();
|
||||
} else {
|
||||
alert(response.message);
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
alert("Submission failed: " + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
// ===============================
|
||||
// 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) {
|
||||
let msg = xhr.responseJSON?.message || "Submission failed. Please try again.";
|
||||
alert(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// UPDATE INVOICE
|
||||
// ===============================
|
||||
function updateInvoice(invoiceId, formElement) {
|
||||
const formData = $(formElement).serialize();
|
||||
|
||||
window.onload = function () {
|
||||
document.getElementById('subcontractor').focus();
|
||||
};
|
||||
$.ajax({
|
||||
url: '/update_invoice/' + invoiceId,
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if(response.status === "success") {
|
||||
alert(response.message || "Invoice updated successfully!");
|
||||
|
||||
// Redirect to Add Invoice page's table part
|
||||
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
|
||||
});
|
||||
@@ -1,26 +1,23 @@
|
||||
|
||||
// Search on table using search inpute options
|
||||
function searchTable() {
|
||||
let input = document.getElementById("searchBar").value.toLowerCase();
|
||||
let rows = document.querySelectorAll("table tbody tr");
|
||||
let tables = document.querySelectorAll("table");
|
||||
|
||||
rows.forEach(row => {
|
||||
let blockName = row.cells[1].textContent.toLowerCase();
|
||||
let districtName = row.cells[2].textContent.toLowerCase();
|
||||
let villageName = row.cells[3].textContent.toLowerCase();
|
||||
tables.forEach(table => {
|
||||
let rows = table.querySelectorAll("tr");
|
||||
|
||||
if (blockName.includes(input) || districtName.includes(input)|| villageName.includes(input)) {
|
||||
row.style.display = "";
|
||||
} else {
|
||||
row.style.display = "none";
|
||||
}
|
||||
rows.forEach((row, index) => {
|
||||
if (index === 0) return; // header skip
|
||||
|
||||
let text = row.textContent.toLowerCase();
|
||||
|
||||
row.style.display = text.includes(input) ? "" : "none";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Common Sorting Script for Tables
|
||||
function sortTable(n, dir) {
|
||||
var table, rows, switching, i, x, y, shouldSwitch;
|
||||
@@ -57,14 +54,14 @@ function sortTable(n, dir) {
|
||||
}
|
||||
|
||||
// Attach sorting functionality to all sortable tables
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Find all elements with the class "sortable-header"
|
||||
var sortableHeaders = document.querySelectorAll(".sortable-header");
|
||||
|
||||
sortableHeaders.forEach(function(header) {
|
||||
sortableHeaders.forEach(function (header) {
|
||||
// Attach click event for ascending sort
|
||||
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);
|
||||
sortTable(columnIndex, "asc");
|
||||
});
|
||||
@@ -72,7 +69,7 @@ document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
// Attach click event for descending sort
|
||||
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);
|
||||
sortTable(columnIndex, "desc");
|
||||
});
|
||||
@@ -105,4 +102,31 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
displayButton.classList.add("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);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,264 +1,250 @@
|
||||
|
||||
|
||||
window.onload = function () {
|
||||
document.getElementById('Village_Name').focus();
|
||||
if (document.getElementById('Village_Name')) {
|
||||
document.getElementById('Village_Name').focus();
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// 🔥 RESTORE VIEW MODE AFTER RELOAD
|
||||
var viewMode = localStorage.getItem("viewMode");
|
||||
// ✅ RUN ONLY IF THIS PAGE HAS FORM/TABLE
|
||||
if ($('#addForm').length && $('#addTable').length) {
|
||||
|
||||
if (viewMode === "table") {
|
||||
$('#addForm').hide();
|
||||
$('#addTable').show();
|
||||
} else {
|
||||
// ✅ DEFAULT VIEW → SHOW FORM
|
||||
$('#addForm').show();
|
||||
$('#addTable').hide();
|
||||
|
||||
|
||||
// 🔥 BUTTON TOGGLE
|
||||
$('#addButton').click(function () {
|
||||
$('#addForm').show();
|
||||
$('#addTable').hide();
|
||||
});
|
||||
|
||||
$('#displayButton').click(function () {
|
||||
$('#addForm').hide();
|
||||
$('#addTable').show();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 🔥 BUTTON TOGGLE LOGIC
|
||||
|
||||
$('#addButton').click(function () {
|
||||
$('#addForm').show();
|
||||
$('#addTable').hide();
|
||||
|
||||
localStorage.setItem("viewMode", "form");
|
||||
});
|
||||
|
||||
$('#displayButton').click(function () {
|
||||
$('#addForm').hide();
|
||||
$('#addTable').show();
|
||||
|
||||
localStorage.setItem("viewMode", "table");
|
||||
});
|
||||
|
||||
|
||||
// ===============================
|
||||
// STATE → DISTRICT
|
||||
$('#state_Id').change(function () {
|
||||
// ===============================
|
||||
if ($('#state_Id').length) {
|
||||
|
||||
var stateId = $(this).val();
|
||||
$('#state_Id').change(function () {
|
||||
|
||||
if (stateId) {
|
||||
var stateId = $(this).val();
|
||||
|
||||
$.ajax({
|
||||
url: '/get_districts/' + stateId,
|
||||
type: 'GET',
|
||||
if (stateId) {
|
||||
|
||||
success: function (data) {
|
||||
$.ajax({
|
||||
url: '/get_districts/' + stateId,
|
||||
type: 'GET',
|
||||
|
||||
var districtDropdown = $('#district_Id');
|
||||
success: function (data) {
|
||||
|
||||
districtDropdown.empty();
|
||||
districtDropdown.append('<option value="" disabled selected>Select District</option>');
|
||||
var districtDropdown = $('#district_Id');
|
||||
|
||||
data.forEach(function (district) {
|
||||
districtDropdown.empty();
|
||||
districtDropdown.append('<option value="" disabled selected>Select District</option>');
|
||||
|
||||
districtDropdown.append(
|
||||
'<option value="' + district.id + '">' + district.name + '</option>'
|
||||
);
|
||||
data.forEach(function (district) {
|
||||
districtDropdown.append(
|
||||
'<option value="' + district.id + '">' + district.name + '</option>'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
districtDropdown.prop('disabled', false);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
districtDropdown.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// DISTRICT → BLOCK
|
||||
$('#district_Id').change(function () {
|
||||
// ===============================
|
||||
if ($('#district_Id').length) {
|
||||
|
||||
var districtId = $(this).val();
|
||||
$('#district_Id').change(function () {
|
||||
|
||||
if (districtId) {
|
||||
var districtId = $(this).val();
|
||||
|
||||
$.ajax({
|
||||
url: '/get_blocks/' + districtId,
|
||||
type: 'GET',
|
||||
if (districtId) {
|
||||
|
||||
success: function (data) {
|
||||
$.ajax({
|
||||
url: '/get_blocks/' + districtId,
|
||||
type: 'GET',
|
||||
|
||||
var blockDropdown = $('#block_Id');
|
||||
success: function (data) {
|
||||
|
||||
blockDropdown.empty();
|
||||
blockDropdown.append('<option value="" disabled selected>Select Block</option>');
|
||||
var blockDropdown = $('#block_Id');
|
||||
|
||||
data.forEach(function (block) {
|
||||
blockDropdown.empty();
|
||||
blockDropdown.append('<option value="" disabled selected>Select Block</option>');
|
||||
|
||||
blockDropdown.append(
|
||||
'<option value="' + block.id + '">' + block.name + '</option>'
|
||||
);
|
||||
data.forEach(function (block) {
|
||||
blockDropdown.append(
|
||||
'<option value="' + block.id + '">' + block.name + '</option>'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
blockDropdown.prop('disabled', false);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
blockDropdown.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// VILLAGE NAME VALIDATION
|
||||
$('#Village_Name').on('input', function () {
|
||||
// ===============================
|
||||
if ($('#Village_Name').length) {
|
||||
|
||||
var villageName = $(this).val();
|
||||
var validPattern = /^[A-Za-z ]*$/;
|
||||
$('#Village_Name').on('input', function () {
|
||||
|
||||
if (!validPattern.test(villageName)) {
|
||||
var villageName = $(this).val();
|
||||
var validPattern = /^[A-Za-z ]*$/;
|
||||
|
||||
$('#villageMessage')
|
||||
.text('Only letters and spaces are allowed!')
|
||||
.css('color', 'red');
|
||||
if (!validPattern.test(villageName)) {
|
||||
|
||||
$('#submitVillage').prop('disabled', true);
|
||||
$('#villageMessage')
|
||||
.text('Only letters and spaces are allowed!')
|
||||
.css('color', 'red');
|
||||
|
||||
} else {
|
||||
$('#submitVillage').prop('disabled', true);
|
||||
|
||||
$('#villageMessage').text('');
|
||||
$('#submitVillage').prop('disabled', false);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
$('#villageMessage').text('');
|
||||
$('#submitVillage').prop('disabled', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// CHECK DUPLICATE VILLAGE
|
||||
$('#Village_Name, #block_Id').on('change keyup', function () {
|
||||
// ===============================
|
||||
if ($('#Village_Name').length && $('#block_Id').length) {
|
||||
|
||||
var blockId = $('#block_Id').val();
|
||||
var villageName = $('#Village_Name').val().trim();
|
||||
$('#Village_Name, #block_Id').on('change keyup', function () {
|
||||
|
||||
if (blockId && villageName) {
|
||||
var blockId = $('#block_Id').val();
|
||||
var villageName = $('#Village_Name').val().trim();
|
||||
|
||||
$.ajax({
|
||||
if (blockId && villageName) {
|
||||
|
||||
url: '/check_village',
|
||||
type: 'POST',
|
||||
$.ajax({
|
||||
url: '/check_village',
|
||||
type: 'POST',
|
||||
|
||||
data: {
|
||||
block_Id: blockId,
|
||||
Village_Name: villageName
|
||||
},
|
||||
data: {
|
||||
block_Id: blockId,
|
||||
Village_Name: villageName
|
||||
},
|
||||
|
||||
success: function (response) {
|
||||
success: function (response) {
|
||||
|
||||
if (response.status === 'exists') {
|
||||
if (response.status === 'exists') {
|
||||
|
||||
$('#villageMessage')
|
||||
.text(response.message)
|
||||
.css('color', 'red');
|
||||
|
||||
$('#submitVillage').prop('disabled', true);
|
||||
|
||||
} else {
|
||||
|
||||
$('#villageMessage')
|
||||
.text(response.message)
|
||||
.css('color', 'green');
|
||||
|
||||
$('#submitVillage').prop('disabled', false);
|
||||
}
|
||||
},
|
||||
|
||||
error: function () {
|
||||
|
||||
$('#villageMessage')
|
||||
.text(response.message)
|
||||
.text('Error checking village name')
|
||||
.css('color', 'red');
|
||||
|
||||
$('#submitVillage').prop('disabled', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// ADD VILLAGE (SAFE SCOPE FIX)
|
||||
// ===============================
|
||||
if ($('#villageForm').length) {
|
||||
|
||||
$('#villageForm').submit(function (event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
$.ajax({
|
||||
url: '/add_village',
|
||||
type: 'POST',
|
||||
data: $(this).serialize(),
|
||||
|
||||
success: function (response) {
|
||||
|
||||
if (response && response.status === 'success') {
|
||||
|
||||
alert(response.message || 'Village added successfully!');
|
||||
|
||||
// ✅ clear form
|
||||
$('#villageForm')[0].reset();
|
||||
|
||||
// ✅ switch to table
|
||||
$('#addForm').hide();
|
||||
$('#addTable').show();
|
||||
|
||||
// optional refresh
|
||||
location.reload();
|
||||
|
||||
} else {
|
||||
|
||||
$('#villageMessage')
|
||||
.text(response.message)
|
||||
.css('color', 'green');
|
||||
|
||||
$('#submitVillage').prop('disabled', false);
|
||||
|
||||
alert(response.message || 'Error adding village. Please try again.');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
error: function () {
|
||||
|
||||
$('#villageMessage')
|
||||
.text('Error checking village name')
|
||||
.css('color', 'red');
|
||||
|
||||
$('#submitVillage').prop('disabled', true);
|
||||
|
||||
alert('An error occurred. Please try again.');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// ADD VILLAGE
|
||||
$('#villageForm').submit(function (event) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
$.ajax({
|
||||
|
||||
url: '/add_village',
|
||||
type: 'POST',
|
||||
data: $(this).serialize(),
|
||||
|
||||
success: function (response) {
|
||||
|
||||
if (response.status === 'success') {
|
||||
|
||||
alert('Village added successfully!');
|
||||
location.reload();
|
||||
|
||||
} else {
|
||||
|
||||
alert(response.message || 'Error adding village. Please try again.');
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
error: function () {
|
||||
|
||||
alert('An error occurred. Please try again.');
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// 🔥 DELETE FUNCTION (UPDATED)
|
||||
function deleteVillage(villageId) {
|
||||
|
||||
if (!confirm("Are you sure you want to delete this village?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ save that user is on table
|
||||
localStorage.setItem("viewMode", "table");
|
||||
// ===============================
|
||||
// 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',
|
||||
|
||||
success: function () {
|
||||
|
||||
setTimeout(function () {
|
||||
|
||||
alert("Village deleted successfully!");
|
||||
|
||||
// reload but stay on table
|
||||
location.reload();
|
||||
|
||||
}, 1000);
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
@@ -15,17 +15,21 @@
|
||||
<button id="addButton" class="action-button">Add</button>
|
||||
<button id="displayButton" class="action-button">Display</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="addForm" style="display: none;">
|
||||
<h2>Add GST Release</h2>
|
||||
<form action="/add_gst_release" method="POST" onsubmit="showSuccessAlert(event)">
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
|
||||
<div id="subcontractor_list" class="autocomplete-items"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<!-- Text input for user-friendly autocomplete -->
|
||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
||||
|
||||
<!-- 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>
|
||||
</div>
|
||||
|
||||
<label for="PMC_No">PMC No:</label><br>
|
||||
@@ -37,19 +41,19 @@
|
||||
</select><br><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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
</form>
|
||||
@@ -117,46 +121,61 @@
|
||||
|
||||
<script>
|
||||
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 list = document.getElementById("subcontractor_list");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
list.innerHTML = '';
|
||||
subcontractorList.innerHTML = '';
|
||||
data.results.forEach(item => {
|
||||
const div = document.createElement("div");
|
||||
div.setAttribute("data-id", item.id);
|
||||
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 selectedName = e.target.textContent;
|
||||
|
||||
if (selectedId) {
|
||||
document.getElementById("subcontractor_id").value = selectedId;
|
||||
document.getElementById("subcontractor").value = selectedName;
|
||||
document.getElementById("subcontractor_list").innerHTML = "";
|
||||
// Set hidden field for backend
|
||||
subcontractorIdInput.value = selectedId;
|
||||
|
||||
// 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)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const pmcDropdown = document.getElementById("PMC_No");
|
||||
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
|
||||
|
||||
data.pmc_nos.forEach(pmc => {
|
||||
const option = document.createElement("option");
|
||||
option.value = pmc;
|
||||
@@ -166,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>
|
||||
|
||||
|
||||
@@ -1,59 +1,73 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
|
||||
<head>
|
||||
<title>Manage Hold Types</title>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- <script src="{{ url_for('static', filename='js/hold_types.js') }}"></script> -->
|
||||
<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>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="button-container">
|
||||
<button id="addButton" class="action-button">Add</button>
|
||||
<button id="displayButton" class="action-button">Display</button>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<button id="addButton" class="action-button">Add</button>
|
||||
<button id="displayButton" class="action-button">Display</button>
|
||||
</div>
|
||||
|
||||
<div id="addForm">
|
||||
<h2>Add Hold Types</h2>
|
||||
<form id="holdTypeForm" method="POST" action="{{ url_for('hold_types.add_hold_type') }}">
|
||||
<label>Enter Hold Amount Type:</label>
|
||||
<input type="text" id="hold_type" name="hold_type" placeholder="Enter Type" required>
|
||||
<span id="holdTypeMessage"></span>
|
||||
<button type="submit" value="Add Hold Type" id="successPopup">Add Hold Type</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="addTable" style="display: none;">
|
||||
<div class="search-container">
|
||||
<h2>Hold Type List</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
</div>
|
||||
<table id="sortableTable" border="1">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th class="sortable-header">
|
||||
Hold Type
|
||||
<span class="sort-buttons">
|
||||
<span class="sort-asc">⬆️</span>
|
||||
<span class="sort-desc">⬇️</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
{% for htd in Hold_Types_data %}
|
||||
<tr>
|
||||
<td>{{ htd['hold_type_id'] }}</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><button class="delete-button" data-id="{{ htd['hold_type_id'] }}">Delete</button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div id="addForm">
|
||||
<h2>Add Hold Types</h2>
|
||||
<form id="holdTypeForm" method="POST" action="{{ url_for('hold_types.add_hold_type') }}">
|
||||
<label>Enter Hold Amount Type:</label>
|
||||
<input type="text" id="hold_type" name="hold_type" placeholder="Enter Type" required>
|
||||
<span id="holdTypeMessage"></span>
|
||||
<button type="submit" value="Add Hold Type" id="successPopup">Add Hold Type</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<a href="/">Back to Dashboard</a>
|
||||
</div>
|
||||
<div id="addTable" style="display: none;">
|
||||
<div class="search-container">
|
||||
<h2>Hold Type List</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
</div>
|
||||
<table id="sortableTable" border="1">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th class="sortable-header">
|
||||
Hold Type
|
||||
<span class="sort-buttons">
|
||||
<span class="sort-asc">⬆️</span>
|
||||
<span class="sort-desc">⬇️</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
{% for htd in Hold_Types_data %}
|
||||
<tr>
|
||||
<td>{{ htd['hold_type_id'] }}</td>
|
||||
<td>{{ htd['hold_type'] }}</td>
|
||||
<td style="text-align:center;">
|
||||
<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>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<a href="/">Back to Dashboard</a>
|
||||
</div>
|
||||
</body>
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,176 +1,322 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
<head xmlns="http://www.w3.org/1999/html">
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Add Invoice</title>
|
||||
<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/holdAmount.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Add Invoice</title>
|
||||
<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/holdAmount.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
{% if success == 'true' %}
|
||||
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
||||
{% if success == 'true' %}
|
||||
<div
|
||||
class="alert alert-success alert-dismissible fade show mt-3"
|
||||
role="alert"
|
||||
>
|
||||
✅ Invoice added successfully!
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<!-- Flash Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="flash-messages">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert {{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="alert"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="button-container">
|
||||
<!-- Flash Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %} {% if
|
||||
messages %}
|
||||
<div class="flash-messages">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert {{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %} {% endwith %}
|
||||
|
||||
<div class="button-container">
|
||||
<button id="addButton" class="action-button">Add</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>
|
||||
|
||||
<form id="invoiceForm" action="{{ url_for('invoice.add_invoice') }}" method="POST">
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
|
||||
<div id="subcontractor_list"></div>
|
||||
</div>
|
||||
<form
|
||||
id="invoiceForm"
|
||||
action="{{ url_for('invoice.add_invoice') }}"
|
||||
method="POST"
|
||||
>
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subcontractor"
|
||||
name="subcontractor"
|
||||
required
|
||||
autocomplete="off"
|
||||
/>
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
|
||||
<div id="subcontractor_list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="village">Village Name:</label>
|
||||
<select id="village" name="village" required>
|
||||
<option value="">-- Select Village --</option>
|
||||
{% for village in villages %}
|
||||
<option value="{{ village.Village_Name }}">{{ village.Village_Name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="pmc_no">PMC No:</label>
|
||||
<input type="text" id="pmc_no" name="pmc_no" required/>
|
||||
<div id="pmc_no_list" class="autocomplete-list"></div>
|
||||
</div>
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="village">Village Name:</label>
|
||||
<select id="village" name="village" required>
|
||||
<option value="">-- Select Village --</option>
|
||||
{% for village in villages %}
|
||||
<option value="{{ village.Village_Name }}">
|
||||
{{ village.Village_Name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="work_type">Work Type:</label>
|
||||
<input type="text" id="work_type" name="work_type" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_details">Invoice Details:</label>
|
||||
<textarea id="invoice_details" name="invoice_details" required></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label for="pmc_no">PMC No:</label>
|
||||
<input type="text" id="pmc_no" name="pmc_no" required />
|
||||
<div id="pmc_no_list" class="autocomplete-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="invoice_no">Invoice No:</label>
|
||||
<input type="text" id="invoice_no" name="invoice_no" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_date">Invoice Date:</label>
|
||||
<input type="date" id="invoice_date" name="invoice_date" required/>
|
||||
</div>
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="work_type">Work Type:</label>
|
||||
<input type="text" id="work_type" name="work_type" required />
|
||||
</div>
|
||||
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="basic_amount">Basic Amount:</label>
|
||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="debit_amount">Debit Amount:</label>
|
||||
<input type="number" step="0.01" id="debit_amount" name="debit_amount" placeholder="₹ - 00.00" required/>
|
||||
</div>
|
||||
<div>
|
||||
<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/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row3">
|
||||
<div class="percentage-field">
|
||||
<label for="gst_percentage">GST %:</label>
|
||||
<input type="number" step="0.01" id="gst_percentage" name="gst_percentage" placeholder="%"/>
|
||||
<label for="gst_amount">GST Amount:</label>
|
||||
<input type="number" step="0.01" id="gst_amount" name="gst_amount" placeholder="₹ - 00.00" readonly required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="amount">Amount:</label>
|
||||
<input type="number" step="0.01" id="amount" name="amount" placeholder="₹ - 00.00" readonly required/>
|
||||
</div>
|
||||
<div class="percentage-field">
|
||||
<label for="tds_percentage">TDS %:</label>
|
||||
<input type="number" step="0.01" id="tds_percentage" name="tds_percentage" placeholder="%"/>
|
||||
<label for="tds_amount">TDS Amount:</label>
|
||||
<input type="number" step="0.01" id="tds_amount" name="tds_amount" placeholder="₹ - 00.00" readonly required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row3">
|
||||
<div class="percentage-field">
|
||||
<label for="sd_percentage">SD %:</label>
|
||||
<input type="number" step="0.01" id="sd_percentage" name="sd_percentage" placeholder="%"/>
|
||||
<label for="sd_amount">SD Amount:</label>
|
||||
<input type="number" step="0.01" id="sd_amount" name="sd_amount" placeholder="₹ - 00.00" readonly required>
|
||||
</div>
|
||||
<div class="percentage-field">
|
||||
<label for="commission_percentage">On Commission %:</label>
|
||||
<input type="number" step="0.01" id="commission_percentage" name="commission_percentage" placeholder="%"/>
|
||||
<label for="on_commission">On Commission:</label>
|
||||
<input type="number" step="0.01" id="on_commission" name="on_commission" placeholder="₹ - 00.00" readonly required>
|
||||
</div>
|
||||
<div class="percentage-field">
|
||||
<label for="hydro_percentage">Hydro Testing %:</label>
|
||||
<input type="number" step="0.01" id="hydro_percentage" name="hydro_percentage" placeholder="%"/>
|
||||
<label for="hydro_testing">Hydro Testing:</label>
|
||||
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" placeholder="₹ - 00.00" readonly required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hold-row">
|
||||
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button>
|
||||
<div>
|
||||
<label for="invoice_details">Invoice Details:</label>
|
||||
<textarea
|
||||
id="invoice_details"
|
||||
name="invoice_details"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dynamically added hold amount fields -->
|
||||
<div id="hold_amount_container"></div>
|
||||
|
||||
<div class="row2">
|
||||
<div>
|
||||
<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/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="final_amount">Final Amount:</label>
|
||||
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required/>
|
||||
</div>
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="invoice_no">Invoice No:</label>
|
||||
<input type="text" id="invoice_no" name="invoice_no" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_date">Invoice Date:</label>
|
||||
<input type="date" id="invoice_date" name="invoice_date" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button">Submit</button>
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="basic_amount">Basic Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="basic_amount"
|
||||
name="basic_amount"
|
||||
placeholder="₹ - 00.00"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="debit_amount">Debit Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="debit_amount"
|
||||
name="debit_amount"
|
||||
placeholder="₹ - 00.00"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<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
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row3">
|
||||
<div class="percentage-field">
|
||||
<label for="gst_percentage">GST %:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="gst_percentage"
|
||||
name="gst_percentage"
|
||||
placeholder="%"
|
||||
/>
|
||||
<label for="gst_amount">GST Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="gst_amount"
|
||||
name="gst_amount"
|
||||
placeholder="₹ - 00.00"
|
||||
readonly
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="amount">Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="amount"
|
||||
name="amount"
|
||||
placeholder="₹ - 00.00"
|
||||
readonly
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="percentage-field">
|
||||
<label for="tds_percentage">TDS %:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="tds_percentage"
|
||||
name="tds_percentage"
|
||||
placeholder="%"
|
||||
/>
|
||||
<label for="tds_amount">TDS Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="tds_amount"
|
||||
name="tds_amount"
|
||||
placeholder="₹ - 00.00"
|
||||
readonly
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row3">
|
||||
<div class="percentage-field">
|
||||
<label for="sd_percentage">SD %:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="sd_percentage"
|
||||
name="sd_percentage"
|
||||
placeholder="%"
|
||||
/>
|
||||
<label for="sd_amount">SD Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="sd_amount"
|
||||
name="sd_amount"
|
||||
placeholder="₹ - 00.00"
|
||||
readonly
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="percentage-field">
|
||||
<label for="commission_percentage">On Commission %:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="commission_percentage"
|
||||
name="commission_percentage"
|
||||
placeholder="%"
|
||||
/>
|
||||
<label for="on_commission">On Commission:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="on_commission"
|
||||
name="on_commission"
|
||||
placeholder="₹ - 00.00"
|
||||
readonly
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="percentage-field">
|
||||
<label for="hydro_percentage">Hydro Testing %:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="hydro_percentage"
|
||||
name="hydro_percentage"
|
||||
placeholder="%"
|
||||
/>
|
||||
<label for="hydro_testing">Hydro Testing:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="hydro_testing"
|
||||
name="hydro_testing"
|
||||
placeholder="₹ - 00.00"
|
||||
readonly
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hold-row">
|
||||
<button type="button" id="add_hold_amount" class="button">
|
||||
+ Add Hold Amount
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Dynamically added hold amount fields -->
|
||||
<div id="hold_amount_container"></div>
|
||||
|
||||
<div class="row2">
|
||||
<div>
|
||||
<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
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="final_amount">Final Amount:</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
id="final_amount"
|
||||
name="final_amount"
|
||||
placeholder="₹ - 00.00"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="addTable" style="display: none;">
|
||||
<div id="addTable" style="display: none">
|
||||
<!-- Invoice Table Section -->
|
||||
<div class="search-container">
|
||||
<h2>Invoice List</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
{% if invoices %}
|
||||
<table class="invoice-table">
|
||||
<h2>Invoice List</h2>
|
||||
<input
|
||||
type="text"
|
||||
id="searchBar"
|
||||
placeholder="Searching..."
|
||||
onkeyup="searchTable()"
|
||||
/>
|
||||
{% if invoices %}
|
||||
<table class="invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr>
|
||||
<th>Invoice Id</th>
|
||||
<th>SubContractor Name</th>
|
||||
<th>PMC No</th>
|
||||
@@ -192,11 +338,11 @@
|
||||
<th>Final Amount</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for invoice in invoices %}
|
||||
<tr>
|
||||
{% for invoice in invoices %}
|
||||
<tr>
|
||||
<td>{{ invoice.Invoice_Id }}</td>
|
||||
<td>{{ invoice.Contractor_Name }}</td>
|
||||
<td>{{ invoice.PMC_No }}</td>
|
||||
@@ -217,62 +363,77 @@
|
||||
<td>{{ invoice.GST_SD_Amount }}</td>
|
||||
<td>{{ invoice.Final_Amount }}</td>
|
||||
<td>
|
||||
<!-- Edit -->
|
||||
<a 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">
|
||||
</a>
|
||||
<!-- Edit -->
|
||||
<a
|
||||
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>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Delete -->
|
||||
<a href="{{ url_for('invoice.delete_invoice_route', invoice_id=invoice.Invoice_Id) }}" onclick="return confirm('Are you sure?')">
|
||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
|
||||
</a>
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
onclick="deleteInvoice({{ invoice.Invoice_Id }}, this)"
|
||||
>
|
||||
<img
|
||||
src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
|
||||
alt="Delete"
|
||||
class="icon"
|
||||
/>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No invoices found.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No invoices found.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Get all the input fields
|
||||
const basicAmount = document.getElementById('basic_amount');
|
||||
const debitAmount = document.getElementById('debit_amount');
|
||||
const afterDebitAmount = document.getElementById('after_debit_amount');
|
||||
const amount = document.getElementById('amount');
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Get all the input fields
|
||||
const basicAmount = document.getElementById("basic_amount");
|
||||
const debitAmount = document.getElementById("debit_amount");
|
||||
const afterDebitAmount = document.getElementById("after_debit_amount");
|
||||
const amount = document.getElementById("amount");
|
||||
|
||||
// Percentage fields
|
||||
const gstPercentage = document.getElementById('gst_percentage');
|
||||
const gstAmount = document.getElementById('gst_amount');
|
||||
const tdsPercentage = document.getElementById('tds_percentage');
|
||||
const tdsAmount = document.getElementById('tds_amount');
|
||||
const sdPercentage = document.getElementById('sd_percentage');
|
||||
const sdAmountInput = document.getElementById('sd_amount');
|
||||
const commissionPercentage = document.getElementById('commission_percentage');
|
||||
const onCommission = document.getElementById('on_commission');
|
||||
const hydroPercentage = document.getElementById('hydro_percentage');
|
||||
const hydroTesting = document.getElementById('hydro_testing');
|
||||
const gstSdAmount = document.getElementById('gst_sd_amount');
|
||||
const finalAmount = document.getElementById('final_amount');
|
||||
// Percentage fields
|
||||
const gstPercentage = document.getElementById("gst_percentage");
|
||||
const gstAmount = document.getElementById("gst_amount");
|
||||
const tdsPercentage = document.getElementById("tds_percentage");
|
||||
const tdsAmount = document.getElementById("tds_amount");
|
||||
const sdPercentage = document.getElementById("sd_percentage");
|
||||
const sdAmountInput = document.getElementById("sd_amount");
|
||||
const commissionPercentage = document.getElementById(
|
||||
"commission_percentage",
|
||||
);
|
||||
const onCommission = document.getElementById("on_commission");
|
||||
const hydroPercentage = document.getElementById("hydro_percentage");
|
||||
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
|
||||
function calculateAfterDebitAmount() {
|
||||
const basic = parseFloat(basicAmount.value) || 0;
|
||||
const debit = parseFloat(debitAmount.value) || 0;
|
||||
const afterDebit = basic - debit;
|
||||
afterDebitAmount.value = afterDebit.toFixed(2);
|
||||
calculateGST();
|
||||
}
|
||||
// Calculate after debit amount when basic or debit amount changes
|
||||
function calculateAfterDebitAmount() {
|
||||
const basic = parseFloat(basicAmount.value) || 0;
|
||||
const debit = parseFloat(debitAmount.value) || 0;
|
||||
const afterDebit = basic - debit;
|
||||
afterDebitAmount.value = afterDebit.toFixed(2);
|
||||
calculateGST();
|
||||
}
|
||||
|
||||
// Calculate GST and Amount
|
||||
function calculateGST() {
|
||||
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
||||
// Calculate GST and Amount
|
||||
function calculateGST() {
|
||||
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
||||
|
||||
if (gstPercentage.value) {
|
||||
if (gstPercentage.value) {
|
||||
const gstPerc = parseFloat(gstPercentage.value) || 0;
|
||||
const gstAmt = (baseAmount * gstPerc) / 100;
|
||||
gstAmount.value = gstAmt.toFixed(2);
|
||||
@@ -280,91 +441,96 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Calculate Amount (After Debit + GST)
|
||||
amount.value = (baseAmount + gstAmt).toFixed(2);
|
||||
} else {
|
||||
} else {
|
||||
amount.value = baseAmount.toFixed(2);
|
||||
}
|
||||
|
||||
calculateOtherDeductions();
|
||||
}
|
||||
|
||||
calculateOtherDeductions();
|
||||
}
|
||||
// Calculate other deductions (TDS, SD, Commission, Hydro)
|
||||
function calculateOtherDeductions() {
|
||||
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
||||
|
||||
// Calculate other deductions (TDS, SD, Commission, Hydro)
|
||||
function calculateOtherDeductions() {
|
||||
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
||||
|
||||
// Calculate TDS
|
||||
if (tdsPercentage.value) {
|
||||
// Calculate TDS
|
||||
if (tdsPercentage.value) {
|
||||
const tdsPerc = parseFloat(tdsPercentage.value) || 0;
|
||||
const tdsAmt = (baseAmount * tdsPerc) / 100;
|
||||
tdsAmount.value = tdsAmt.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate SD
|
||||
if (sdPercentage.value) {
|
||||
// Calculate SD
|
||||
if (sdPercentage.value) {
|
||||
const sdPerc = parseFloat(sdPercentage.value) || 0;
|
||||
const sdAmt = (baseAmount * sdPerc) / 100;
|
||||
sdAmountInput.value = sdAmt.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate Commission
|
||||
if (commissionPercentage.value) {
|
||||
// Calculate Commission
|
||||
if (commissionPercentage.value) {
|
||||
const commPerc = parseFloat(commissionPercentage.value) || 0;
|
||||
const commAmt = (baseAmount * commPerc) / 100;
|
||||
onCommission.value = commAmt.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate Hydro Testing
|
||||
if (hydroPercentage.value) {
|
||||
// Calculate Hydro Testing
|
||||
if (hydroPercentage.value) {
|
||||
const hydroPerc = parseFloat(hydroPercentage.value) || 0;
|
||||
const hydroAmt = (baseAmount * hydroPerc) / 100;
|
||||
hydroTesting.value = hydroAmt.toFixed(2);
|
||||
}
|
||||
|
||||
calculateFinalAmount();
|
||||
}
|
||||
|
||||
calculateFinalAmount();
|
||||
}
|
||||
// Calculate final amount
|
||||
function calculateFinalAmount() {
|
||||
const amt = parseFloat(amount.value) || 0;
|
||||
const tds = parseFloat(tdsAmount.value) || 0;
|
||||
const sd = parseFloat(sdAmountInput.value) || 0;
|
||||
const commission = parseFloat(onCommission.value) || 0;
|
||||
const hydro = parseFloat(hydroTesting.value) || 0;
|
||||
const gstSd = parseFloat(gstSdAmount.value) || 0;
|
||||
|
||||
// Calculate final amount
|
||||
function calculateFinalAmount() {
|
||||
const amt = parseFloat(amount.value) || 0;
|
||||
const tds = parseFloat(tdsAmount.value) || 0;
|
||||
const sd = parseFloat(sdAmountInput.value) || 0;
|
||||
const commission = parseFloat(onCommission.value) || 0;
|
||||
const hydro = parseFloat(hydroTesting.value) || 0;
|
||||
const gstSd = parseFloat(gstSdAmount.value) || 0;
|
||||
// Get hold amounts
|
||||
let totalHold = 0;
|
||||
document
|
||||
.querySelectorAll('input[name="hold_amount[]"]')
|
||||
.forEach((input) => {
|
||||
totalHold += parseFloat(input.value) || 0;
|
||||
});
|
||||
|
||||
// Get hold amounts
|
||||
let totalHold = 0;
|
||||
document.querySelectorAll('input[name="hold_amount[]"]').forEach(input => {
|
||||
totalHold += parseFloat(input.value) || 0;
|
||||
});
|
||||
// Final Amount = Amount - TDS - SD - Commission - Hydro - GST SD - Hold Amounts
|
||||
const final = amt - tds - sd - commission - hydro - gstSd - totalHold;
|
||||
finalAmount.value = final.toFixed(2);
|
||||
}
|
||||
|
||||
// Final Amount = Amount - TDS - SD - Commission - Hydro - GST SD - Hold Amounts
|
||||
const final = amt - tds - sd - commission - hydro - gstSd - totalHold;
|
||||
finalAmount.value = final.toFixed(2);
|
||||
}
|
||||
// Add event listeners
|
||||
basicAmount.addEventListener("input", calculateAfterDebitAmount);
|
||||
debitAmount.addEventListener("input", calculateAfterDebitAmount);
|
||||
|
||||
// Add event listeners
|
||||
basicAmount.addEventListener('input', calculateAfterDebitAmount);
|
||||
debitAmount.addEventListener('input', calculateAfterDebitAmount);
|
||||
// Percentage fields
|
||||
gstPercentage.addEventListener("input", calculateGST);
|
||||
tdsPercentage.addEventListener("input", calculateOtherDeductions);
|
||||
sdPercentage.addEventListener("input", calculateOtherDeductions);
|
||||
commissionPercentage.addEventListener(
|
||||
"input",
|
||||
calculateOtherDeductions,
|
||||
);
|
||||
hydroPercentage.addEventListener("input", calculateOtherDeductions);
|
||||
|
||||
// Percentage fields
|
||||
gstPercentage.addEventListener('input', calculateGST);
|
||||
tdsPercentage.addEventListener('input', calculateOtherDeductions);
|
||||
sdPercentage.addEventListener('input', calculateOtherDeductions);
|
||||
commissionPercentage.addEventListener('input', calculateOtherDeductions);
|
||||
hydroPercentage.addEventListener('input', calculateOtherDeductions);
|
||||
// Listen for changes in hold amounts
|
||||
document.addEventListener("holdAmountChanged", calculateFinalAmount);
|
||||
});
|
||||
|
||||
// Listen for changes in hold amounts
|
||||
document.addEventListener('holdAmountChanged', calculateFinalAmount);
|
||||
});
|
||||
|
||||
// Optional JS for auto-hiding flash
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.alert').forEach(el => el.style.display = 'none');
|
||||
}, 5000);
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
// Optional JS for auto-hiding flash
|
||||
setTimeout(() => {
|
||||
document
|
||||
.querySelectorAll(".alert")
|
||||
.forEach((el) => (el.style.display = "none"));
|
||||
}, 5000);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,99 +1,129 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Village Management</title>
|
||||
<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/search_on_table.js') }}"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Village Management</title>
|
||||
<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/search_on_table.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Button Container to Center Buttons -->
|
||||
<div class="button-container">
|
||||
<!-- Button Container to Center Buttons -->
|
||||
<div class="button-container">
|
||||
<button id="addButton" class="action-button">Add</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="form-block">
|
||||
<h2>Add a New Village</h2>
|
||||
<form id="villageForm" method="POST">
|
||||
<label for="state_Id">State:</label>
|
||||
<select id="state_Id" name="state_Id" required>
|
||||
<option value="" disabled selected>Select State</option>
|
||||
{% for state in states %}
|
||||
<option value="{{ state[0] }}">{{ state[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<label for="district_Id">District:</label>
|
||||
<select id="district_Id" name="district_Id" required disabled>
|
||||
<option value="" disabled selected>Select District</option>
|
||||
</select>
|
||||
<div class="form-block">
|
||||
<h2>Add a New Village</h2>
|
||||
<form id="villageForm" method="POST">
|
||||
<label for="state_Id">State:</label>
|
||||
<select id="state_Id" name="state_Id" required>
|
||||
<option value="" disabled selected>Select State</option>
|
||||
{% for state in states %}
|
||||
<option value="{{ state[0] }}">{{ state[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<label for="block_Id">Block:</label>
|
||||
<select id="block_Id" name="block_Id" required disabled>
|
||||
<option value="" disabled selected>Select Block</option>
|
||||
</select>
|
||||
<label for="district_Id">District:</label>
|
||||
<select id="district_Id" name="district_Id" required disabled>
|
||||
<option value="" disabled selected>Select District</option>
|
||||
</select>
|
||||
|
||||
<label for="Village_Name">Village Name:</label>
|
||||
<input type="text" id="Village_Name" name="Village_Name" placeholder="Enter Village Name" required>
|
||||
<span id="villageMessage"></span>
|
||||
<label for="block_Id">Block:</label>
|
||||
<select id="block_Id" name="block_Id" required disabled>
|
||||
<option value="" disabled selected>Select Block</option>
|
||||
</select>
|
||||
|
||||
<button type="submit" id="submitVillage" disabled>Add Village</button>
|
||||
</form>
|
||||
</div>
|
||||
<label for="Village_Name">Village Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="Village_Name"
|
||||
name="Village_Name"
|
||||
placeholder="Enter Village Name"
|
||||
required
|
||||
/>
|
||||
<span id="villageMessage"></span>
|
||||
|
||||
<button type="submit" id="submitVillage" disabled>Add Village</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="addTable" style="display: none;">
|
||||
<div id="addTable" style="display: none">
|
||||
<div class="search-container">
|
||||
<h2>Display Villages</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
<h2>Display Villages</h2>
|
||||
<input
|
||||
type="text"
|
||||
id="searchBar"
|
||||
placeholder="Searching..."
|
||||
onkeyup="searchTable()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<table id="sortableTable" border="1">
|
||||
<tr>
|
||||
<th>Village Sr No</th>
|
||||
<th class="sortable-header">
|
||||
Village Name
|
||||
<span class="sort-buttons">
|
||||
<span class="sort-asc">⬆️</span>
|
||||
<span class="sort-desc">⬇️</span>
|
||||
</span></th>
|
||||
<th class="sortable-header">Block Name
|
||||
<span class="sort-buttons">
|
||||
<span class="sort-asc">⬆️</span>
|
||||
<span class="sort-desc">⬇️</span>
|
||||
</span></th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
{% for village in villages %}
|
||||
<tr>
|
||||
<td>{{ village[0] }}</td>
|
||||
<td>{{ village[1] }}</td>
|
||||
<td>{{ village[2] }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('village.edit_village', village_id=village[0]) }}">
|
||||
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
|
||||
class="icon">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#"
|
||||
onclick="deleteVillage({{ village[0] }}); return false;">
|
||||
<tr>
|
||||
<th>Village Sr No</th>
|
||||
<th class="sortable-header">
|
||||
Village Name
|
||||
<span class="sort-buttons">
|
||||
<span class="sort-asc">⬆️</span>
|
||||
<span class="sort-desc">⬇️</span>
|
||||
</span>
|
||||
</th>
|
||||
<th class="sortable-header">
|
||||
Block Name
|
||||
<span class="sort-buttons">
|
||||
<span class="sort-asc">⬆️</span>
|
||||
<span class="sort-desc">⬇️</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
{% for village in villages %}
|
||||
<tr>
|
||||
<td>{{ village[0] }}</td>
|
||||
<td>{{ village[1] }}</td>
|
||||
<td>{{ village[2] }}</td>
|
||||
<td>
|
||||
<a
|
||||
href="{{ url_for('village.edit_village', village_id=village[0]) }}"
|
||||
>
|
||||
<img
|
||||
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
|
||||
alt="Edit"
|
||||
class="icon"
|
||||
/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="javascript:void(0);"
|
||||
onclick="deleteVillage({{ village[0] }}, this)">
|
||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
|
||||
alt="Delete" class="icon">
|
||||
class="icon">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</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>
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,37 +9,37 @@
|
||||
<body>
|
||||
<h2>Edit GST Release</h2>
|
||||
|
||||
<form action="/edit_gst_release/{{ gst_release_data[0] }}" 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>-->
|
||||
<form action="/edit_gst_release/{{ gst_release_data.gst_release_id }}" method="POST">
|
||||
|
||||
<!-- PMC Number -->
|
||||
<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>
|
||||
<input type="number" step="0.01" id="invoice_No" name="invoice_No" value="{{ gst_release_data[2] }}"
|
||||
required><br><br>
|
||||
<!-- Invoice Number -->
|
||||
<label for="invoice_no">Invoice No:</label><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>
|
||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" value="{{ gst_release_data[3] }}"
|
||||
required><br><br>
|
||||
<!-- Final Amount -->
|
||||
<label for="Final_Amount">Final Amount:</label><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>
|
||||
<input type="number" step="0.01" id="final_amount" name="final_amount" value="{{ gst_release_data[4] }}"
|
||||
required><br><br>
|
||||
<!-- Total Amount -->
|
||||
<label for="Total_Amount">Total Amount:</label><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>
|
||||
<input type="number" step="0.01" id="total_amount" name="total_amount" value="{{ gst_release_data[5] }}"
|
||||
required><br><br>
|
||||
<!-- UTR -->
|
||||
<label for="UTR">UTR:</label><br>
|
||||
<input type="text" id="UTR" name="UTR" value="{{ gst_release_data.utr }}" readonly required><br><br>
|
||||
|
||||
<label for="utr">UTR:</label><br>
|
||||
<input type="text" id="utr" name="utr" value="{{ gst_release_data[6] }}"
|
||||
required readonly><br><br>
|
||||
<!-- Hidden Contractor ID -->
|
||||
<input type="hidden" id="Contractor_ID" name="Contractor_ID" value="{{ gst_release_data.contractor_id }}">
|
||||
|
||||
<button type="submit">Update GST Release</button>
|
||||
</form>
|
||||
|
||||
|
||||
</body>
|
||||
{% endblock %}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<head>
|
||||
<title>Edit Hold Type</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
<body>
|
||||
<h2>Edit Hold Type</h2>
|
||||
<form id="updateHoldTypeForm">
|
||||
<input type="hidden" id="hold_type_id" value="{{ hold_type[0] }}">
|
||||
<form id="updateHoldTypeForm" method="POST">
|
||||
<input type="hidden" id="hold_type_id" value="{{ hold_type.hold_type_id }}">
|
||||
<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>
|
||||
<a href="{{ url_for('hold_types.add_hold_type') }}"></a>
|
||||
</form>
|
||||
@@ -37,7 +37,7 @@
|
||||
formData.append('hold_type', newHoldType);
|
||||
|
||||
$.ajax({
|
||||
url: '/update_hold_type/' + holdTypeId,
|
||||
url: '/edit_hold_type/' + holdTypeId,
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
@@ -54,9 +54,4 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -2,168 +2,185 @@
|
||||
{% block content %}
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<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/style1.css') }}">
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="editForm">
|
||||
<h2>Edit Invoice</h2>
|
||||
<div id="editForm">
|
||||
<h2>Edit Invoice</h2>
|
||||
|
||||
<!-- Success Alert Box -->
|
||||
<div id="invoiceSuccessAlert" class="success-alert" style="display:none;">
|
||||
Invoice successfully updated!
|
||||
<!-- Success Alert Box -->
|
||||
<div id="invoiceSuccessAlert" class="success-alert" style="display:none;">
|
||||
Invoice successfully updated!
|
||||
</div>
|
||||
|
||||
<form id="invoiceForm" action="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"
|
||||
method="POST">
|
||||
|
||||
<!-- Subcontractor Field -->
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input class="form-control" list="subcontractor_list" id="subcontractor" name="subcontractor"
|
||||
value="{{ invoice.Contractor_Name }}" placeholder="Type to search..." required>
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"
|
||||
value="{{ invoice.Subcontractor_Id }}">
|
||||
<datalist id="subcontractor_list"></datalist>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Village and PMC No Fields -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="village">Village Name:</label>
|
||||
<input type="text" id="village" name="village" value="{{ invoice.Village_Name }}" required />
|
||||
<datalist id="village_list"></datalist>
|
||||
</div>
|
||||
<div>
|
||||
<label for="pmc_no">PMC No:</label>
|
||||
<input type="text" id="pmc_no" name="pmc_no" value="{{ invoice.PMC_No }}" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Work Type and Invoice Details -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="work_type">Work Type:</label>
|
||||
<input type="text" id="work_type" name="work_type" value="{{ invoice.Work_Type }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_details">Invoice Details:</label>
|
||||
<textarea id="invoice_details" name="invoice_details"
|
||||
required>{{ invoice.Invoice_Details }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Invoice No and Invoice Date -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="invoice_no">Invoice No:</label>
|
||||
<input type="text" id="invoice_no" name="invoice_no" value="{{ invoice.invoice_no }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_date">Invoice Date:</label>
|
||||
<input type="date" id="invoice_date" name="invoice_date" value="{{ invoice.Invoice_Date }}"
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Amount Fields -->
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="basic_amount">Basic Amount:</label>
|
||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount"
|
||||
value="{{ invoice.Basic_Amount }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="debit_amount">Debit Amount:</label>
|
||||
<input type="number" step="0.01" id="debit_amount" name="debit_amount"
|
||||
value="{{ invoice.Debit_Amount }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<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 />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GST, TDS, and Other Amounts -->
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="amount">Amount:</label>
|
||||
<input type="number" step="0.01" id="amount" name="amount" value="{{ invoice.Amount }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="gst_amount">GST Amount:</label>
|
||||
<input type="number" step="0.01" id="gst_amount" name="gst_amount" value="{{ invoice.GST_Amount }}"
|
||||
required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="tds_amount">TDS Amount:</label>
|
||||
<input type="number" step="0.01" id="tds_amount" name="tds_amount" value="{{ invoice.TDS_Amount }}"
|
||||
required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SD, On Commission, Hydro Testing -->
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="sd_amount">SD Amount:</label>
|
||||
<input type="number" step="0.01" id="sd_amount" name="sd_amount" value="{{ invoice.SD_Amount }}"
|
||||
required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="on_commission">On Commission:</label>
|
||||
<input type="number" step="0.01" id="on_commission" name="on_commission"
|
||||
value="{{ invoice.On_Commission }}" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="hydro_testing">Hydro Testing:</label>
|
||||
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing"
|
||||
value="{{ invoice.Hydro_Testing }}" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hold Amount Section -->
|
||||
<div id="hold_amount_container">
|
||||
{% set seen_types = [] %}
|
||||
{% for hold in invoice.hold_amounts %}
|
||||
{% if hold.hold_type not in seen_types %}
|
||||
{% set _ = seen_types.append(hold.hold_type) %}
|
||||
<div class="hold-amount-row">
|
||||
<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 />
|
||||
<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 />
|
||||
<button type="button" class="remove-hold-amount">Remove</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hold-row">
|
||||
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button>
|
||||
</div>
|
||||
|
||||
<!-- Final Amounts -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<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 />
|
||||
</div>
|
||||
<div>
|
||||
<label for="final_amount">Final Amount:</label>
|
||||
<input type="number" step="0.01" id="final_amount" name="final_amount"
|
||||
value="{{ invoice.Final_Amount }}" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<button type="submit" class="button">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<form id="invoiceForm" action="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}" method="POST">
|
||||
|
||||
<!-- Subcontractor Field -->
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input class="form-control" list="subcontractor_list" id="subcontractor" name="subcontractor"
|
||||
value="{{ invoice.Contractor_Name }}" placeholder="Type to search..." required>
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id" value="{{ invoice.Subcontractor_Id }}">
|
||||
<datalist id="subcontractor_list"></datalist>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Village and PMC No Fields -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="village">Village Name:</label>
|
||||
<input type="text" id="village" name="village" value="{{ invoice.Village_Name }}" required/>
|
||||
<datalist id="village_list"></datalist>
|
||||
</div>
|
||||
<div>
|
||||
<label for="pmc_no">PMC No:</label>
|
||||
<input type="text" id="pmc_no" name="pmc_no" value="{{ invoice.PMC_No }}" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Work Type and Invoice Details -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="work_type">Work Type:</label>
|
||||
<input type="text" id="work_type" name="work_type" value="{{ invoice.Work_Type }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_details">Invoice Details:</label>
|
||||
<textarea id="invoice_details" name="invoice_details" required>{{ invoice.Invoice_Details }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Invoice No and Invoice Date -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<label for="invoice_no">Invoice No:</label>
|
||||
<input type="text" id="invoice_no" name="invoice_no" value="{{ invoice.Invoice_No }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="invoice_date">Invoice Date:</label>
|
||||
<input type="date" id="invoice_date" name="invoice_date" value="{{ invoice.Invoice_Date }}" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Amount Fields -->
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="basic_amount">Basic Amount:</label>
|
||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" value="{{ invoice.Basic_Amount }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="debit_amount">Debit Amount:</label>
|
||||
<input type="number" step="0.01" id="debit_amount" name="debit_amount" value="{{ invoice.Debit_Amount }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<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/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GST, TDS, and Other Amounts -->
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="amount">Amount:</label>
|
||||
<input type="number" step="0.01" id="amount" name="amount" value="{{ invoice.Amount }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="gst_amount">GST Amount:</label>
|
||||
<input type="number" step="0.01" id="gst_amount" name="gst_amount" value="{{ invoice.GST_Amount }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="tds_amount">TDS Amount:</label>
|
||||
<input type="number" step="0.01" id="tds_amount" name="tds_amount" value="{{ invoice.TDS_Amount }}" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SD, On Commission, Hydro Testing -->
|
||||
<div class="row3">
|
||||
<div>
|
||||
<label for="sd_amount">SD Amount:</label>
|
||||
<input type="number" step="0.01" id="sd_amount" name="sd_amount" value="{{ invoice.SD_Amount }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="on_commission">On Commission:</label>
|
||||
<input type="number" step="0.01" id="on_commission" name="on_commission" value="{{ invoice.On_Commission }}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="hydro_testing">Hydro Testing:</label>
|
||||
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" value="{{ invoice.Hydro_Testing }}" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hold Amount Section -->
|
||||
<div id="hold_amount_container">
|
||||
{% set seen_types = [] %}
|
||||
{% for hold in invoice.hold_amounts %}
|
||||
{% if hold.hold_type not in seen_types %}
|
||||
{% set _ = seen_types.append(hold.hold_type) %}
|
||||
<div class="hold-amount-row">
|
||||
<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/>
|
||||
<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/>
|
||||
<button type="button" class="remove-hold-amount">Remove</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hold-row">
|
||||
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button>
|
||||
</div>
|
||||
|
||||
<!-- Final Amounts -->
|
||||
<div class="row2">
|
||||
<div>
|
||||
<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/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="final_amount">Final Amount:</label>
|
||||
<input type="number" step="0.01" id="final_amount" name="final_amount" value="{{ invoice.Final_Amount }}" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<button type="submit" class="button">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- JS for dynamic hold amount rows -->
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Add new hold amount row
|
||||
$("#add_hold_amount").click(function() {
|
||||
const index = $("#hold_amount_container .hold-amount-row").length;
|
||||
const newRow = `
|
||||
<!-- JS for dynamic hold amount rows -->
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// Add new hold amount row
|
||||
$("#add_hold_amount").click(function () {
|
||||
const index = $("#hold_amount_container .hold-amount-row").length;
|
||||
const newRow = `
|
||||
<div class="hold-amount-row">
|
||||
<label for="hold_type_${index}">Hold Type:</label>
|
||||
<input type="text" id="hold_type_${index}" name="hold_type[]" required/>
|
||||
@@ -172,36 +189,38 @@ $(document).ready(function() {
|
||||
<button type="button" class="remove-hold-amount">Remove</button>
|
||||
</div>
|
||||
`;
|
||||
$("#hold_amount_container").append(newRow);
|
||||
});
|
||||
$("#hold_amount_container").append(newRow);
|
||||
});
|
||||
|
||||
// Remove hold amount row
|
||||
$(document).on("click", ".remove-hold-amount", function() {
|
||||
$(this).closest(".hold-amount-row").remove();
|
||||
});
|
||||
// Remove hold amount row
|
||||
$(document).on("click", ".remove-hold-amount", function () {
|
||||
$(this).closest(".hold-amount-row").remove();
|
||||
});
|
||||
|
||||
// Submit form via AJAX
|
||||
$("#invoiceForm").submit(function(e) {
|
||||
e.preventDefault();
|
||||
// Submit form via AJAX
|
||||
$("#invoiceForm").submit(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $(this).attr("action"),
|
||||
data: $(this).serialize(),
|
||||
success: function(response) {
|
||||
if(response.status === "success") {
|
||||
$("#invoiceSuccessAlert").fadeIn().delay(3000).fadeOut();
|
||||
alert("Invoice updated successfully!"); // <-- Popup alert
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $(this).attr("action"),
|
||||
data: $(this).serialize(),
|
||||
dataType: 'json', // ensure JSON is returned
|
||||
success: function (response) {
|
||||
if (response.status === "success") {
|
||||
alert("Invoice updated successfully!"); // <-- Popup alert
|
||||
|
||||
},
|
||||
error: function(xhr) {
|
||||
alert("Error: " + xhr.responseJSON.message);
|
||||
}
|
||||
// ✅ Redirect to Add Invoice page (table part visible)
|
||||
window.location.href = "{{ url_for('invoice.add_invoice') }}#addTable";
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
alert("Error: " + xhr.responseJSON?.message || "Something went wrong!");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
{% endblock %}
|
||||
@@ -28,25 +28,17 @@
|
||||
|
||||
<button type="submit">Update Village</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Flash Message (Hidden, used for JS popup) -->
|
||||
<!-- Flash Messages (hidden, used for JS popup) -->
|
||||
<div id="flash-messages-container" style="display:none;">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div id="flash-message" data-category="{{ category }}" style="display:none;">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="flash-message" data-category="{{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.onload = function () {
|
||||
const flash = document.getElementById('flash-message');
|
||||
if (flash && flash.innerText.trim() !== "") {
|
||||
alert(flash.innerText);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user