Compare commits
9 Commits
8f35e08429
...
swapnil-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d84ba520cf | ||
| 0aeaf775dd | |||
| 88e8771b51 | |||
| 6c74b5d3bf | |||
| 47ba78d72c | |||
| 1946a98d59 | |||
|
|
91b078a0c3 | ||
|
|
eb46929893 | ||
|
|
14e799a1d4 |
@@ -21,7 +21,6 @@ def activity_log():
|
|||||||
end_date,
|
end_date,
|
||||||
user_name
|
user_name
|
||||||
)
|
)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"activity_log.html",
|
"activity_log.html",
|
||||||
logs=filtered_logs,
|
logs=filtered_logs,
|
||||||
|
|||||||
@@ -27,13 +27,27 @@ class FolderAndFile:
|
|||||||
os.makedirs(folder, exist_ok=True)
|
os.makedirs(folder, exist_ok=True)
|
||||||
return folder
|
return folder
|
||||||
|
|
||||||
# -----------------------------
|
@staticmethod
|
||||||
# FILE PATH METHODS
|
def get_logs_folder():
|
||||||
# -----------------------------
|
folder = os.path.join(current_app.root_path, "logs")
|
||||||
|
|
||||||
|
if not os.path.exists(folder):
|
||||||
|
os.makedirs(folder)
|
||||||
|
|
||||||
|
os.makedirs(folder, exist_ok=True)
|
||||||
|
return folder
|
||||||
|
|
||||||
|
# FILE PATH METHODS - download
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_download_path(filename):
|
def get_download_path(filename):
|
||||||
return os.path.join(FolderAndFile.get_download_folder(), filename)
|
return os.path.join(FolderAndFile.get_download_folder(), filename)
|
||||||
|
|
||||||
|
# FILE PATH METHODS - upload file
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_upload_path(filename):
|
def get_upload_path(filename):
|
||||||
return os.path.join(FolderAndFile.get_upload_folder(), filename)
|
return os.path.join(FolderAndFile.get_upload_folder(), filename)
|
||||||
|
|
||||||
|
# FILE PATH METHODS - activity log file
|
||||||
|
@staticmethod
|
||||||
|
def get_activity_log_path(filename):
|
||||||
|
return os.path.join(FolderAndFile.get_logs_folder(), filename)
|
||||||
@@ -232,14 +232,13 @@ class ItemCRUD:
|
|||||||
if self.itemCRUDType.name == "GSTRelease" and data:
|
if self.itemCRUDType.name == "GSTRelease" and data:
|
||||||
|
|
||||||
cursor.callproc(storedprocupdate, (
|
cursor.callproc(storedprocupdate, (
|
||||||
childid,
|
data['p_pmc_no'], # PMC_No
|
||||||
data['PMC_No'],
|
data['p_invoice_no'], # Invoice_No
|
||||||
data['Invoice_No'],
|
data['p_basic_amount'], # Basic_Amount
|
||||||
data['Basic_Amount'],
|
data['p_final_amount'], # Final_Amount
|
||||||
data['Final_Amount'],
|
data['p_total_amount'], # Total_Amount
|
||||||
data['Total_Amount'],
|
data['p_utr'], # UTR
|
||||||
data['UTR'],
|
data['p_gst_release_id']# GST_Release_Id
|
||||||
data['Contractor_ID']
|
|
||||||
))
|
))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
|
|||||||
114
model/Log.py
114
model/Log.py
@@ -1,29 +1,41 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import datetime
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask_login import current_user
|
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from model.FolderAndFile import FolderAndFile
|
||||||
|
|
||||||
class LogHelper:
|
class LogHelper:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log_action(action, details=""):
|
def log_action(action, details=""):
|
||||||
"""Add a log entry."""
|
"""Log user actions with timestamp, user, action, and details."""
|
||||||
log_data = LogData()
|
logData = LogData()
|
||||||
log_data.add_log(action, details)
|
logData.WriteLog(action, details="")
|
||||||
|
|
||||||
|
|
||||||
class LogData:
|
class LogData:
|
||||||
|
filepath = ""
|
||||||
|
timestamp = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.filepath = os.path.join(current_app.root_path, 'activity.log')
|
self.filepath = FolderAndFile.get_activity_log_path('activity.log')
|
||||||
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
self.user = getattr(current_user, "cn", None) \
|
self.user = LogData.get_current_user()
|
||||||
or getattr(current_user, "username", None) \
|
|
||||||
or getattr(current_user, "sAMAccountName", "Unknown")
|
|
||||||
|
|
||||||
|
|
||||||
def add_log(self, action, details=""):
|
@staticmethod
|
||||||
"""Create/Add a log entry."""
|
def get_current_user():
|
||||||
|
if hasattr(current_user, "cn") and current_user.cn:
|
||||||
|
return current_user.cn
|
||||||
|
elif hasattr(current_user, "username") and current_user.username:
|
||||||
|
return current_user.username
|
||||||
|
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
|
||||||
|
return current_user.sAMAccountName
|
||||||
|
return "Unknown"
|
||||||
|
|
||||||
|
def WriteLog(self, action, details=""):
|
||||||
|
"""Log user actions with timestamp, user, action, and details."""
|
||||||
|
|
||||||
with open(self.filepath, "a", encoding="utf-8") as f:
|
with open(self.filepath, "a", encoding="utf-8") as f:
|
||||||
f.write(
|
f.write(
|
||||||
f"Timestamp: {self.timestamp} | "
|
f"Timestamp: {self.timestamp} | "
|
||||||
@@ -32,73 +44,41 @@ class LogData:
|
|||||||
f"Details: {details}\n"
|
f"Details: {details}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_all_logs(self):
|
def GetActivitiesLog(self):
|
||||||
"""Read all logs."""
|
|
||||||
logs = []
|
logs = []
|
||||||
|
|
||||||
if os.path.exists(self.filepath):
|
if os.path.exists(self.filepath):
|
||||||
with open(self.filepath, 'r', encoding="utf-8") as f:
|
with open(self.filepath, 'r') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
parts = line.strip().split(" | ")
|
parts = line.strip().split(" | ")
|
||||||
if len(parts) == 4:
|
if len(parts) == 4:
|
||||||
logs.append({
|
logs.append({
|
||||||
"timestamp": parts[0].split(":", 1)[1].strip(),
|
"timestamp": parts[0].replace("Timestamp:", "").strip(),
|
||||||
"user": parts[1].split(":", 1)[1].strip(),
|
"user": parts[1].replace("User:", "").strip(),
|
||||||
"action": parts[2].split(":", 1)[1].strip(),
|
"action": parts[2].replace("Action:", "").strip(),
|
||||||
"details": parts[3].split(":", 1)[1].strip()
|
"details": parts[3].replace("Details:", "").strip()
|
||||||
})
|
})
|
||||||
return logs
|
return logs
|
||||||
|
|
||||||
def get_filtered_logs(self, start_date=None, end_date=None, user_name=None):
|
def GetFilteredActivitiesLog(self, startDate, endDate, userName):
|
||||||
"""Filter logs by date and/or user."""
|
filtered_logs = self.GetActivitiesLog()
|
||||||
logs = self.get_all_logs()
|
|
||||||
|
|
||||||
# Filter by date
|
# Date filter
|
||||||
if start_date or end_date:
|
if startDate or endDate:
|
||||||
start_dt = datetime.strptime(start_date, "%Y-%m-%d") if start_date else datetime.min
|
try:
|
||||||
end_dt = datetime.strptime(end_date, "%Y-%m-%d") if end_date else datetime.max
|
start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
|
||||||
logs = [
|
end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
|
||||||
log for log in logs
|
|
||||||
|
filtered_logs = [
|
||||||
|
log for log in filtered_logs
|
||||||
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
||||||
]
|
]
|
||||||
|
|
||||||
# Filter by username
|
except Exception as e:
|
||||||
if user_name:
|
print("Date filter error:", e)
|
||||||
logs = [log for log in logs if user_name.lower() in log.get("user", "").lower()]
|
|
||||||
|
|
||||||
return logs
|
|
||||||
|
|
||||||
def update_log(self, index, action=None, details=None):
|
|
||||||
"""Update a specific log entry by index (0-based)."""
|
|
||||||
logs = self.get_all_logs()
|
|
||||||
if 0 <= index < len(logs):
|
|
||||||
if action:
|
|
||||||
logs[index]["action"] = action
|
|
||||||
if details:
|
|
||||||
logs[index]["details"] = details
|
|
||||||
self._rewrite_logs_file(logs)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def delete_log(self, index):
|
|
||||||
"""Delete a specific log entry by index (0-based)."""
|
|
||||||
logs = self.get_all_logs()
|
|
||||||
if 0 <= index < len(logs):
|
|
||||||
logs.pop(index)
|
|
||||||
self._rewrite_logs_file(logs)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# ------------------- INTERNAL HELPER -------------------
|
|
||||||
|
|
||||||
def _rewrite_logs_file(self, logs):
|
|
||||||
"""Overwrite the log file with current logs."""
|
|
||||||
with open(self.filepath, "w", encoding="utf-8") as f:
|
|
||||||
for log in logs:
|
|
||||||
f.write(
|
|
||||||
f"Timestamp: {log['timestamp']} | "
|
|
||||||
f"User: {log['user']} | "
|
|
||||||
f"Action: {log['action']} | "
|
|
||||||
f"Details: {log['details']}\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Username filter
|
||||||
|
if userName:
|
||||||
|
filtered_logs = [log for log in filtered_logs if userName.lower() in log["user"].lower()]
|
||||||
|
|
||||||
|
return filtered_logs
|
||||||
@@ -14,6 +14,12 @@ class GSTRelease:
|
|||||||
try:
|
try:
|
||||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||||
|
|
||||||
|
# Print the full form data
|
||||||
|
print("===== DEBUG: FORM DATA =====")
|
||||||
|
for key, value in request.form.items():
|
||||||
|
print(f"{key} : {value}")
|
||||||
|
print("=============================")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"PMC_No": request.form.get("PMC_No", "").strip(),
|
"PMC_No": request.form.get("PMC_No", "").strip(),
|
||||||
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
||||||
@@ -24,6 +30,10 @@ class GSTRelease:
|
|||||||
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
|
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print("===== DEBUG: PARSED DATA =====")
|
||||||
|
print(data)
|
||||||
|
print("==============================")
|
||||||
|
|
||||||
# Add GST Release
|
# Add GST Release
|
||||||
gst.AddItem(
|
gst.AddItem(
|
||||||
request=request,
|
request=request,
|
||||||
@@ -32,11 +42,7 @@ class GSTRelease:
|
|||||||
storedprocadd="AddGSTReleaseFromExcel"
|
storedprocadd="AddGSTReleaseFromExcel"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if addition was successful
|
print(f"AddItem result: isSuccess={gst.isSuccess}, message={gst.resultMessage}")
|
||||||
if gst.isSuccess:
|
|
||||||
print(f"GST Release Added: {data}")
|
|
||||||
else:
|
|
||||||
print(f"Failed to add GST Release: {gst.resultMessage}")
|
|
||||||
|
|
||||||
self.isSuccess = gst.isSuccess
|
self.isSuccess = gst.isSuccess
|
||||||
self.resultMessage = str(gst.resultMessage)
|
self.resultMessage = str(gst.resultMessage)
|
||||||
@@ -48,20 +54,26 @@ class GSTRelease:
|
|||||||
|
|
||||||
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
|
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
|
||||||
|
|
||||||
# ------------------- Edit GST Release -------------------
|
|
||||||
def EditGSTRelease(self, request, gst_release_id):
|
def EditGSTRelease(self, request, gst_release_id):
|
||||||
try:
|
try:
|
||||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||||
|
|
||||||
|
# Map form inputs to stored procedure parameters
|
||||||
data = {
|
data = {
|
||||||
"PMC_No": request.form.get("PMC_No", "").strip(),
|
"p_pmc_no": request.form.get("PMC_No", "").strip(),
|
||||||
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
"p_invoice_no": request.form.get("invoice_no", "").strip(),
|
||||||
"Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0),
|
"p_basic_amount": float(request.form.get("Basic_Amount", 0) or 0),
|
||||||
"Final_Amount": float(request.form.get("Final_Amount", 0) or 0),
|
"p_final_amount": float(request.form.get("Final_Amount", 0) or 0),
|
||||||
"Total_Amount": float(request.form.get("Total_Amount", 0) or 0),
|
"p_total_amount": float(request.form.get("Total_Amount", 0) or 0),
|
||||||
"UTR": request.form.get("UTR", "").strip()
|
"p_utr": request.form.get("UTR", "").strip(),
|
||||||
|
"p_gst_release_id": gst_release_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print("===== DEBUG: UPDATE DATA =====")
|
||||||
|
print(data)
|
||||||
|
print("==============================")
|
||||||
|
|
||||||
|
# Call your stored procedure
|
||||||
gst.EditItem(
|
gst.EditItem(
|
||||||
request=request,
|
request=request,
|
||||||
childid=gst_release_id,
|
childid=gst_release_id,
|
||||||
@@ -77,8 +89,6 @@ class GSTRelease:
|
|||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = str(e)
|
self.resultMessage = str(e)
|
||||||
|
|
||||||
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
|
|
||||||
|
|
||||||
# ------------------- Delete GST Release -------------------
|
# ------------------- Delete GST Release -------------------
|
||||||
def DeleteGSTRelease(self, gst_release_id):
|
def DeleteGSTRelease(self, gst_release_id):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,160 @@
|
|||||||
// Subcontractor autocomplete functionality
|
// $(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 () {
|
$(document).ready(function () {
|
||||||
|
// ===============================
|
||||||
|
// FORM / TABLE TOGGLE
|
||||||
|
// ===============================
|
||||||
|
if ($('#addForm').length && $('#addTable').length) {
|
||||||
|
// Default: show form, hide table
|
||||||
|
$('#addForm').show();
|
||||||
|
$('#addTable').hide();
|
||||||
|
|
||||||
|
// ✅ Check URL hash to show table instead
|
||||||
|
if (window.location.hash === "#addTable") {
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#addButton').click(function () {
|
||||||
|
$('#addForm').show();
|
||||||
|
$('#addTable').hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#displayButton').click(function () {
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Subcontractor autocomplete
|
||||||
|
// ===============================
|
||||||
$("#subcontractor").keyup(function () {
|
$("#subcontractor").keyup(function () {
|
||||||
let query = $(this).val();
|
let query = $(this).val();
|
||||||
if (query !== "") {
|
if (query !== "") {
|
||||||
@@ -21,42 +176,96 @@
|
|||||||
$("#subcontractor_id").val($(this).attr("data-id"));
|
$("#subcontractor_id").val($(this).attr("data-id"));
|
||||||
$("#subcontractor_list").fadeOut();
|
$("#subcontractor_list").fadeOut();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Success Alert: show alert and reload after 3 seconds
|
// Focus
|
||||||
function showSuccessAlert() {
|
if (document.getElementById('subcontractor')) {
|
||||||
const alertBox = document.getElementById("invoiceSuccessAlert");
|
document.getElementById('subcontractor').focus();
|
||||||
alertBox.classList.add("show");
|
|
||||||
setTimeout(() => {
|
|
||||||
alertBox.classList.remove("show");
|
|
||||||
// Reload page or redirect after alert hides
|
|
||||||
window.location.href = '/add_invoice';
|
|
||||||
}, 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit form via AJAX
|
// ===============================
|
||||||
|
// ADD INVOICE
|
||||||
|
// ===============================
|
||||||
|
if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
|
||||||
$("#invoiceForm").on("submit", function (e) {
|
$("#invoiceForm").on("submit", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let formData = $(this).serialize();
|
let formData = $(this).serialize();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/add_invoice',
|
url: '/add_invoice',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: formData,
|
data: formData,
|
||||||
|
dataType: 'json',
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
if (response.status === "success") {
|
if (response.status === "success") {
|
||||||
showSuccessAlert();
|
alert(response.message || "Invoice added successfully!");
|
||||||
|
$('#invoiceForm')[0].reset(); // clear form
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show(); // switch to table
|
||||||
|
location.reload(); // optional refresh
|
||||||
} else {
|
} else {
|
||||||
alert(response.message);
|
alert(response.message || "Error adding invoice.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (xhr, status, error) {
|
error: function (xhr) {
|
||||||
alert("Submission failed: " + error);
|
let msg = xhr.responseJSON?.message || "Submission failed. Please try again.";
|
||||||
|
alert(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// UPDATE INVOICE
|
||||||
|
// ===============================
|
||||||
|
function updateInvoice(invoiceId, formElement) {
|
||||||
|
const formData = $(formElement).serialize();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/update_invoice/' + invoiceId,
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if(response.status === "success") {
|
||||||
|
alert(response.message || "Invoice updated successfully!");
|
||||||
|
|
||||||
window.onload = function () {
|
// Redirect to Add Invoice page's table part
|
||||||
document.getElementById('subcontractor').focus();
|
window.location.href = "/add_invoice#addTable";
|
||||||
};
|
} else {
|
||||||
|
alert(response.message || "Update failed. Please try again.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
let msg = xhr.responseJSON?.message || "Error updating invoice.";
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.updateInvoice = updateInvoice; // make globally accessible
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// DELETE INVOICE
|
||||||
|
// ===============================
|
||||||
|
function deleteInvoice(invoiceId, element) {
|
||||||
|
if (!confirm("Are you sure you want to delete this invoice?")) return;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/delete_invoice/' + invoiceId,
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
if (response.status === "success") {
|
||||||
|
alert(response.message || "Invoice deleted successfully!");
|
||||||
|
if (element) $(element).closest("tr").remove();
|
||||||
|
} else {
|
||||||
|
alert(response.message || "Error deleting invoice.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
let msg = xhr.responseJSON?.message || "Error deleting invoice. Please try again.";
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.deleteInvoice = deleteInvoice; // make globally accessible
|
||||||
|
});
|
||||||
@@ -1,26 +1,23 @@
|
|||||||
|
|
||||||
// Search on table using search inpute options
|
// Search on table using search inpute options
|
||||||
function searchTable() {
|
function searchTable() {
|
||||||
let input = document.getElementById("searchBar").value.toLowerCase();
|
let input = document.getElementById("searchBar").value.toLowerCase();
|
||||||
let rows = document.querySelectorAll("table tbody tr");
|
let tables = document.querySelectorAll("table");
|
||||||
|
|
||||||
rows.forEach(row => {
|
tables.forEach(table => {
|
||||||
let blockName = row.cells[1].textContent.toLowerCase();
|
let rows = table.querySelectorAll("tr");
|
||||||
let districtName = row.cells[2].textContent.toLowerCase();
|
|
||||||
let villageName = row.cells[3].textContent.toLowerCase();
|
|
||||||
|
|
||||||
if (blockName.includes(input) || districtName.includes(input)|| villageName.includes(input)) {
|
rows.forEach((row, index) => {
|
||||||
row.style.display = "";
|
if (index === 0) return; // header skip
|
||||||
} else {
|
|
||||||
row.style.display = "none";
|
let text = row.textContent.toLowerCase();
|
||||||
}
|
|
||||||
|
row.style.display = text.includes(input) ? "" : "none";
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Common Sorting Script for Tables
|
// Common Sorting Script for Tables
|
||||||
function sortTable(n, dir) {
|
function sortTable(n, dir) {
|
||||||
var table, rows, switching, i, x, y, shouldSwitch;
|
var table, rows, switching, i, x, y, shouldSwitch;
|
||||||
@@ -106,3 +103,30 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
addButton.classList.remove("active-button");
|
addButton.classList.remove("active-button");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
let tables = document.querySelectorAll("table");
|
||||||
|
|
||||||
|
tables.forEach(table => {
|
||||||
|
let header = table.querySelector("tr:first-child");
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
header.style.position = "sticky";
|
||||||
|
header.style.top = "0";
|
||||||
|
header.style.background = "#fff";
|
||||||
|
header.style.zIndex = "2";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.parentElement.classList.contains("table-wrapper")) {
|
||||||
|
let wrapper = document.createElement("div");
|
||||||
|
wrapper.classList.add("table-wrapper");
|
||||||
|
wrapper.style.maxHeight = "65vh"
|
||||||
|
wrapper.style.overflowY = "auto";
|
||||||
|
|
||||||
|
table.parentNode.insertBefore(wrapper, table);
|
||||||
|
wrapper.appendChild(table);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -22,8 +22,12 @@
|
|||||||
<div class="row1">
|
<div class="row1">
|
||||||
<div>
|
<div>
|
||||||
<label for="subcontractor">Subcontractor Name:</label>
|
<label for="subcontractor">Subcontractor Name:</label>
|
||||||
|
<!-- Text input for user-friendly autocomplete -->
|
||||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
||||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
|
|
||||||
|
<!-- Hidden input for backend; must match model's Contractor_ID -->
|
||||||
|
<input type="hidden" id="subcontractor_id" name="Contractor_ID"/>
|
||||||
|
|
||||||
<div id="subcontractor_list" class="autocomplete-items"></div>
|
<div id="subcontractor_list" class="autocomplete-items"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,19 +41,19 @@
|
|||||||
</select><br><br>
|
</select><br><br>
|
||||||
|
|
||||||
<label for="invoice_No">Invoice No:</label><br>
|
<label for="invoice_No">Invoice No:</label><br>
|
||||||
<input type="text" id="invoice_No" name="invoice_No" required><br><br>
|
<input type="text" id="invoice_No" name="Invoice_No" required><br><br>
|
||||||
|
|
||||||
<label for="basic_amount">Basic Amount:</label><br>
|
<label for="basic_amount">Basic Amount:</label><br>
|
||||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required><br><br>
|
<input type="number" step="0.01" id="basic_amount" name="Basic_Amount" placeholder="₹ - 00.00" required><br><br>
|
||||||
|
|
||||||
<label for="final_amount">Final Amount:</label><br>
|
<label for="final_amount">Final Amount:</label><br>
|
||||||
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required><br><br>
|
<input type="number" step="0.01" id="final_amount" name="Final_Amount" placeholder="₹ - 00.00" required><br><br>
|
||||||
|
|
||||||
<label for="total_amount">Total Amount:</label><br>
|
<label for="total_amount">Total Amount:</label><br>
|
||||||
<input type="number" step="0.01" id="total_amount" name="total_amount" placeholder="₹ - 00.00" required><br><br>
|
<input type="number" step="0.01" id="total_amount" name="Total_Amount" placeholder="₹ - 00.00" required><br><br>
|
||||||
|
|
||||||
<label for="utr">UTR:</label><br>
|
<label for="utr">UTR:</label><br>
|
||||||
<input type="text" id="utr" name="utr" required><br><br>
|
<input type="text" id="utr" name="UTR" required><br><br>
|
||||||
|
|
||||||
<button type="submit">Submit GST Release</button>
|
<button type="submit">Submit GST Release</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -117,46 +121,61 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Handle subcontractor autocomplete
|
|
||||||
document.getElementById("subcontractor").addEventListener("input", function () {
|
const subcontractorInput = document.getElementById("subcontractor");
|
||||||
|
const subcontractorIdInput = document.getElementById("subcontractor_id");
|
||||||
|
const subcontractorList = document.getElementById("subcontractor_list");
|
||||||
|
const pmcDropdown = document.getElementById("PMC_No");
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// Subcontractor autocomplete
|
||||||
|
// --------------------------
|
||||||
|
subcontractorInput.addEventListener("input", function () {
|
||||||
const query = this.value;
|
const query = this.value;
|
||||||
const list = document.getElementById("subcontractor_list");
|
|
||||||
|
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
list.innerHTML = '';
|
subcontractorList.innerHTML = '';
|
||||||
|
subcontractorIdInput.value = ''; // reset hidden id
|
||||||
|
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; // reset PMC dropdown
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
list.innerHTML = '';
|
subcontractorList.innerHTML = '';
|
||||||
data.results.forEach(item => {
|
data.results.forEach(item => {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.setAttribute("data-id", item.id);
|
div.setAttribute("data-id", item.id);
|
||||||
div.textContent = item.name;
|
div.textContent = item.name;
|
||||||
list.appendChild(div);
|
subcontractorList.appendChild(div);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle subcontractor selection
|
// --------------------------
|
||||||
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
|
// Subcontractor selection
|
||||||
|
// --------------------------
|
||||||
|
subcontractorList.addEventListener("click", function (e) {
|
||||||
const selectedId = e.target.getAttribute("data-id");
|
const selectedId = e.target.getAttribute("data-id");
|
||||||
const selectedName = e.target.textContent;
|
const selectedName = e.target.textContent;
|
||||||
|
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
document.getElementById("subcontractor_id").value = selectedId;
|
// Set hidden field for backend
|
||||||
document.getElementById("subcontractor").value = selectedName;
|
subcontractorIdInput.value = selectedId;
|
||||||
document.getElementById("subcontractor_list").innerHTML = "";
|
|
||||||
|
|
||||||
// Update PMC dropdown for selected subcontractor
|
// Set text input to selected name
|
||||||
|
subcontractorInput.value = selectedName;
|
||||||
|
|
||||||
|
// Clear the autocomplete list
|
||||||
|
subcontractorList.innerHTML = "";
|
||||||
|
|
||||||
|
// Fetch and populate PMC dropdown
|
||||||
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const pmcDropdown = document.getElementById("PMC_No");
|
|
||||||
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
|
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
|
||||||
|
|
||||||
data.pmc_nos.forEach(pmc => {
|
data.pmc_nos.forEach(pmc => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = pmc;
|
option.value = pmc;
|
||||||
@@ -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>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,66 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %}
|
||||||
{% block content %}
|
|
||||||
<head xmlns="http://www.w3.org/1999/html">
|
<head xmlns="http://www.w3.org/1999/html">
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Add Invoice</title>
|
<title>Add Invoice</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}">
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
href="{{ url_for('static', filename='css/invoice.css') }}"
|
||||||
|
/>
|
||||||
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% if success == 'true' %}
|
{% if success == 'true' %}
|
||||||
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
<div
|
||||||
|
class="alert alert-success alert-dismissible fade show mt-3"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
✅ Invoice added successfully!
|
✅ Invoice added successfully!
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-close"
|
||||||
|
data-bs-dismiss="alert"
|
||||||
|
aria-label="Close"
|
||||||
|
></button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<!-- Flash Messages -->
|
<!-- Flash Messages -->
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %} {% if
|
||||||
{% if messages %}
|
messages %}
|
||||||
<div class="flash-messages">
|
<div class="flash-messages">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<div class="alert {{ category }}">{{ message }}</div>
|
<div class="alert {{ category }}">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %} {% endwith %}
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button id="addButton" class="action-button">Add</button>
|
<button id="addButton" class="action-button">Add</button>
|
||||||
<button id="displayButton" class="action-button">Display</button>
|
<button id="displayButton" class="action-button">Display</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addForm" style="display: none;">
|
<div id="addForm" style="display: none">
|
||||||
<h2>Add Invoice</h2>
|
<h2>Add Invoice</h2>
|
||||||
|
|
||||||
<form id="invoiceForm" action="{{ url_for('invoice.add_invoice') }}" method="POST">
|
<form
|
||||||
|
id="invoiceForm"
|
||||||
|
action="{{ url_for('invoice.add_invoice') }}"
|
||||||
|
method="POST"
|
||||||
|
>
|
||||||
<div class="row1">
|
<div class="row1">
|
||||||
<div>
|
<div>
|
||||||
<label for="subcontractor">Subcontractor Name:</label>
|
<label for="subcontractor">Subcontractor Name:</label>
|
||||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
<input
|
||||||
|
type="text"
|
||||||
|
id="subcontractor"
|
||||||
|
name="subcontractor"
|
||||||
|
required
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
|
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
|
||||||
<div id="subcontractor_list"></div>
|
<div id="subcontractor_list"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -53,7 +72,9 @@
|
|||||||
<select id="village" name="village" required>
|
<select id="village" name="village" required>
|
||||||
<option value="">-- Select Village --</option>
|
<option value="">-- Select Village --</option>
|
||||||
{% for village in villages %}
|
{% for village in villages %}
|
||||||
<option value="{{ village.Village_Name }}">{{ village.Village_Name }}</option>
|
<option value="{{ village.Village_Name }}">
|
||||||
|
{{ village.Village_Name }}
|
||||||
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +92,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="invoice_details">Invoice Details:</label>
|
<label for="invoice_details">Invoice Details:</label>
|
||||||
<textarea id="invoice_details" name="invoice_details" required></textarea>
|
<textarea
|
||||||
|
id="invoice_details"
|
||||||
|
name="invoice_details"
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -89,59 +114,161 @@
|
|||||||
<div class="row3">
|
<div class="row3">
|
||||||
<div>
|
<div>
|
||||||
<label for="basic_amount">Basic Amount:</label>
|
<label for="basic_amount">Basic Amount:</label>
|
||||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="basic_amount"
|
||||||
|
name="basic_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="debit_amount">Debit Amount:</label>
|
<label for="debit_amount">Debit Amount:</label>
|
||||||
<input type="number" step="0.01" id="debit_amount" name="debit_amount" placeholder="₹ - 00.00" required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="debit_amount"
|
||||||
|
name="debit_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="after_debit_amount">After Debit Amount:</label>
|
<label for="after_debit_amount">After Debit Amount:</label>
|
||||||
<input type="number" step="0.01" id="after_debit_amount" name="after_debit_amount" placeholder="₹ - 00.00" readonly required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="after_debit_amount"
|
||||||
|
name="after_debit_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row3">
|
<div class="row3">
|
||||||
<div class="percentage-field">
|
<div class="percentage-field">
|
||||||
<label for="gst_percentage">GST %:</label>
|
<label for="gst_percentage">GST %:</label>
|
||||||
<input type="number" step="0.01" id="gst_percentage" name="gst_percentage" placeholder="%"/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="gst_percentage"
|
||||||
|
name="gst_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
<label for="gst_amount">GST Amount:</label>
|
<label for="gst_amount">GST Amount:</label>
|
||||||
<input type="number" step="0.01" id="gst_amount" name="gst_amount" placeholder="₹ - 00.00" readonly required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="gst_amount"
|
||||||
|
name="gst_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="amount">Amount:</label>
|
<label for="amount">Amount:</label>
|
||||||
<input type="number" step="0.01" id="amount" name="amount" placeholder="₹ - 00.00" readonly required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="amount"
|
||||||
|
name="amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="percentage-field">
|
<div class="percentage-field">
|
||||||
<label for="tds_percentage">TDS %:</label>
|
<label for="tds_percentage">TDS %:</label>
|
||||||
<input type="number" step="0.01" id="tds_percentage" name="tds_percentage" placeholder="%"/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="tds_percentage"
|
||||||
|
name="tds_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
<label for="tds_amount">TDS Amount:</label>
|
<label for="tds_amount">TDS Amount:</label>
|
||||||
<input type="number" step="0.01" id="tds_amount" name="tds_amount" placeholder="₹ - 00.00" readonly required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="tds_amount"
|
||||||
|
name="tds_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row3">
|
<div class="row3">
|
||||||
<div class="percentage-field">
|
<div class="percentage-field">
|
||||||
<label for="sd_percentage">SD %:</label>
|
<label for="sd_percentage">SD %:</label>
|
||||||
<input type="number" step="0.01" id="sd_percentage" name="sd_percentage" placeholder="%"/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="sd_percentage"
|
||||||
|
name="sd_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
<label for="sd_amount">SD Amount:</label>
|
<label for="sd_amount">SD Amount:</label>
|
||||||
<input type="number" step="0.01" id="sd_amount" name="sd_amount" placeholder="₹ - 00.00" readonly required>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="sd_amount"
|
||||||
|
name="sd_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="percentage-field">
|
<div class="percentage-field">
|
||||||
<label for="commission_percentage">On Commission %:</label>
|
<label for="commission_percentage">On Commission %:</label>
|
||||||
<input type="number" step="0.01" id="commission_percentage" name="commission_percentage" placeholder="%"/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="commission_percentage"
|
||||||
|
name="commission_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
<label for="on_commission">On Commission:</label>
|
<label for="on_commission">On Commission:</label>
|
||||||
<input type="number" step="0.01" id="on_commission" name="on_commission" placeholder="₹ - 00.00" readonly required>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="on_commission"
|
||||||
|
name="on_commission"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="percentage-field">
|
<div class="percentage-field">
|
||||||
<label for="hydro_percentage">Hydro Testing %:</label>
|
<label for="hydro_percentage">Hydro Testing %:</label>
|
||||||
<input type="number" step="0.01" id="hydro_percentage" name="hydro_percentage" placeholder="%"/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="hydro_percentage"
|
||||||
|
name="hydro_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
<label for="hydro_testing">Hydro Testing:</label>
|
<label for="hydro_testing">Hydro Testing:</label>
|
||||||
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" placeholder="₹ - 00.00" readonly required>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="hydro_testing"
|
||||||
|
name="hydro_testing"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="hold-row">
|
<div class="hold-row">
|
||||||
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button>
|
<button type="button" id="add_hold_amount" class="button">
|
||||||
|
+ Add Hold Amount
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dynamically added hold amount fields -->
|
<!-- Dynamically added hold amount fields -->
|
||||||
@@ -150,11 +277,25 @@
|
|||||||
<div class="row2">
|
<div class="row2">
|
||||||
<div>
|
<div>
|
||||||
<label for="gst_sd_amount">GST SD Amount:</label>
|
<label for="gst_sd_amount">GST SD Amount:</label>
|
||||||
<input type="number" step="0.01" id="gst_sd_amount" name="gst_sd_amount" placeholder="₹ - 00.00" required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="gst_sd_amount"
|
||||||
|
name="gst_sd_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="final_amount">Final Amount:</label>
|
<label for="final_amount">Final Amount:</label>
|
||||||
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required/>
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="final_amount"
|
||||||
|
name="final_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -162,11 +303,16 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addTable" style="display: none;">
|
<div id="addTable" style="display: none">
|
||||||
<!-- Invoice Table Section -->
|
<!-- Invoice Table Section -->
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<h2>Invoice List</h2>
|
<h2>Invoice List</h2>
|
||||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
<input
|
||||||
|
type="text"
|
||||||
|
id="searchBar"
|
||||||
|
placeholder="Searching..."
|
||||||
|
onkeyup="searchTable()"
|
||||||
|
/>
|
||||||
{% if invoices %}
|
{% if invoices %}
|
||||||
<table class="invoice-table">
|
<table class="invoice-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -218,14 +364,27 @@
|
|||||||
<td>{{ invoice.Final_Amount }}</td>
|
<td>{{ invoice.Final_Amount }}</td>
|
||||||
<td>
|
<td>
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<a href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}">
|
<a
|
||||||
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon">
|
href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
|
||||||
|
alt="Edit"
|
||||||
|
class="icon edit-btn"
|
||||||
|
data-id="{{ invoice.Invoice_Id }}"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<!-- Delete -->
|
<a
|
||||||
<a href="{{ url_for('invoice.delete_invoice_route', invoice_id=invoice.Invoice_Id) }}" onclick="return confirm('Are you sure?')">
|
href="javascript:void(0);"
|
||||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
|
onclick="deleteInvoice({{ invoice.Invoice_Id }}, this)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
|
||||||
|
alt="Delete"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -238,26 +397,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Get all the input fields
|
// Get all the input fields
|
||||||
const basicAmount = document.getElementById('basic_amount');
|
const basicAmount = document.getElementById("basic_amount");
|
||||||
const debitAmount = document.getElementById('debit_amount');
|
const debitAmount = document.getElementById("debit_amount");
|
||||||
const afterDebitAmount = document.getElementById('after_debit_amount');
|
const afterDebitAmount = document.getElementById("after_debit_amount");
|
||||||
const amount = document.getElementById('amount');
|
const amount = document.getElementById("amount");
|
||||||
|
|
||||||
// Percentage fields
|
// Percentage fields
|
||||||
const gstPercentage = document.getElementById('gst_percentage');
|
const gstPercentage = document.getElementById("gst_percentage");
|
||||||
const gstAmount = document.getElementById('gst_amount');
|
const gstAmount = document.getElementById("gst_amount");
|
||||||
const tdsPercentage = document.getElementById('tds_percentage');
|
const tdsPercentage = document.getElementById("tds_percentage");
|
||||||
const tdsAmount = document.getElementById('tds_amount');
|
const tdsAmount = document.getElementById("tds_amount");
|
||||||
const sdPercentage = document.getElementById('sd_percentage');
|
const sdPercentage = document.getElementById("sd_percentage");
|
||||||
const sdAmountInput = document.getElementById('sd_amount');
|
const sdAmountInput = document.getElementById("sd_amount");
|
||||||
const commissionPercentage = document.getElementById('commission_percentage');
|
const commissionPercentage = document.getElementById(
|
||||||
const onCommission = document.getElementById('on_commission');
|
"commission_percentage",
|
||||||
const hydroPercentage = document.getElementById('hydro_percentage');
|
);
|
||||||
const hydroTesting = document.getElementById('hydro_testing');
|
const onCommission = document.getElementById("on_commission");
|
||||||
const gstSdAmount = document.getElementById('gst_sd_amount');
|
const hydroPercentage = document.getElementById("hydro_percentage");
|
||||||
const finalAmount = document.getElementById('final_amount');
|
const hydroTesting = document.getElementById("hydro_testing");
|
||||||
|
const gstSdAmount = document.getElementById("gst_sd_amount");
|
||||||
|
const finalAmount = document.getElementById("final_amount");
|
||||||
|
|
||||||
// Calculate after debit amount when basic or debit amount changes
|
// Calculate after debit amount when basic or debit amount changes
|
||||||
function calculateAfterDebitAmount() {
|
function calculateAfterDebitAmount() {
|
||||||
@@ -333,7 +494,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Get hold amounts
|
// Get hold amounts
|
||||||
let totalHold = 0;
|
let totalHold = 0;
|
||||||
document.querySelectorAll('input[name="hold_amount[]"]').forEach(input => {
|
document
|
||||||
|
.querySelectorAll('input[name="hold_amount[]"]')
|
||||||
|
.forEach((input) => {
|
||||||
totalHold += parseFloat(input.value) || 0;
|
totalHold += parseFloat(input.value) || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -343,27 +506,30 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add event listeners
|
// Add event listeners
|
||||||
basicAmount.addEventListener('input', calculateAfterDebitAmount);
|
basicAmount.addEventListener("input", calculateAfterDebitAmount);
|
||||||
debitAmount.addEventListener('input', calculateAfterDebitAmount);
|
debitAmount.addEventListener("input", calculateAfterDebitAmount);
|
||||||
|
|
||||||
// Percentage fields
|
// Percentage fields
|
||||||
gstPercentage.addEventListener('input', calculateGST);
|
gstPercentage.addEventListener("input", calculateGST);
|
||||||
tdsPercentage.addEventListener('input', calculateOtherDeductions);
|
tdsPercentage.addEventListener("input", calculateOtherDeductions);
|
||||||
sdPercentage.addEventListener('input', calculateOtherDeductions);
|
sdPercentage.addEventListener("input", calculateOtherDeductions);
|
||||||
commissionPercentage.addEventListener('input', calculateOtherDeductions);
|
commissionPercentage.addEventListener(
|
||||||
hydroPercentage.addEventListener('input', calculateOtherDeductions);
|
"input",
|
||||||
|
calculateOtherDeductions,
|
||||||
|
);
|
||||||
|
hydroPercentage.addEventListener("input", calculateOtherDeductions);
|
||||||
|
|
||||||
// Listen for changes in hold amounts
|
// Listen for changes in hold amounts
|
||||||
document.addEventListener('holdAmountChanged', calculateFinalAmount);
|
document.addEventListener("holdAmountChanged", calculateFinalAmount);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Optional JS for auto-hiding flash
|
// Optional JS for auto-hiding flash
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.querySelectorAll('.alert').forEach(el => el.style.display = 'none');
|
document
|
||||||
|
.querySelectorAll(".alert")
|
||||||
|
.forEach((el) => (el.style.display = "none"));
|
||||||
}, 5000);
|
}, 5000);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -9,37 +9,37 @@
|
|||||||
<body>
|
<body>
|
||||||
<h2>Edit GST Release</h2>
|
<h2>Edit GST Release</h2>
|
||||||
|
|
||||||
<form action="/edit_gst_release/{{ gst_release_data[0] }}" method="POST">
|
<form action="/edit_gst_release/{{ gst_release_data.gst_release_id }}" method="POST">
|
||||||
<!-- <label for="invoice_id">Invoice Id:</label><br>-->
|
|
||||||
<!-- <input type="number" id="invoice_id" name="invoice_id" value="{{ gst_release_data[0] }}" required><br><br>-->
|
|
||||||
|
|
||||||
|
<!-- PMC Number -->
|
||||||
<label for="PMC_No">PMC No :</label><br>
|
<label for="PMC_No">PMC No :</label><br>
|
||||||
<input type="number" id="PMC_No" name="PMC_No" value="{{ gst_release_data[1] }}" required><br><br>
|
<input type="text" id="PMC_No" name="PMC_No" value="{{ gst_release_data.pmc_no }}" required><br><br>
|
||||||
|
|
||||||
<label for="invoice_No">Invoice No:</label><br>
|
<!-- Invoice Number -->
|
||||||
<input type="number" step="0.01" id="invoice_No" name="invoice_No" value="{{ gst_release_data[2] }}"
|
<label for="invoice_no">Invoice No:</label><br>
|
||||||
required><br><br>
|
<input type="text" id="invoice_no" name="invoice_no" value="{{ gst_release_data.invoice_no }}" required><br><br>
|
||||||
|
|
||||||
|
<!-- Basic Amount -->
|
||||||
|
<label for="Basic_Amount">Basic Amount:</label><br>
|
||||||
|
<input type="number" step="0.01" id="Basic_Amount" name="Basic_Amount" value="{{ gst_release_data.basic_amount }}" required><br><br>
|
||||||
|
|
||||||
<label for="basic_amount">Basic Amount:</label><br>
|
<!-- Final Amount -->
|
||||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" value="{{ gst_release_data[3] }}"
|
<label for="Final_Amount">Final Amount:</label><br>
|
||||||
required><br><br>
|
<input type="number" step="0.01" id="Final_Amount" name="Final_Amount" value="{{ gst_release_data.final_amount }}" required><br><br>
|
||||||
|
|
||||||
<label for="final_amount">Final Amount:</label><br>
|
<!-- Total Amount -->
|
||||||
<input type="number" step="0.01" id="final_amount" name="final_amount" value="{{ gst_release_data[4] }}"
|
<label for="Total_Amount">Total Amount:</label><br>
|
||||||
required><br><br>
|
<input type="number" step="0.01" id="Total_Amount" name="Total_Amount" value="{{ gst_release_data.total_amount }}" required><br><br>
|
||||||
|
|
||||||
<label for="total_amount">Total Amount:</label><br>
|
<!-- UTR -->
|
||||||
<input type="number" step="0.01" id="total_amount" name="total_amount" value="{{ gst_release_data[5] }}"
|
<label for="UTR">UTR:</label><br>
|
||||||
required><br><br>
|
<input type="text" id="UTR" name="UTR" value="{{ gst_release_data.utr }}" readonly required><br><br>
|
||||||
|
|
||||||
<label for="utr">UTR:</label><br>
|
<!-- Hidden Contractor ID -->
|
||||||
<input type="text" id="utr" name="utr" value="{{ gst_release_data[6] }}"
|
<input type="hidden" id="Contractor_ID" name="Contractor_ID" value="{{ gst_release_data.contractor_id }}">
|
||||||
required readonly><br><br>
|
|
||||||
|
|
||||||
<button type="submit">Update GST Release</button>
|
<button type="submit">Update GST Release</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<title>Edit Invoice</title>
|
<title>Edit Invoice</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style1.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style1.css') }}">
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -204,15 +205,17 @@
|
|||||||
type: "POST",
|
type: "POST",
|
||||||
url: $(this).attr("action"),
|
url: $(this).attr("action"),
|
||||||
data: $(this).serialize(),
|
data: $(this).serialize(),
|
||||||
|
dataType: 'json', // ensure JSON is returned
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
if (response.status === "success") {
|
if (response.status === "success") {
|
||||||
$("#invoiceSuccessAlert").fadeIn().delay(3000).fadeOut();
|
|
||||||
alert("Invoice updated successfully!"); // <-- Popup alert
|
alert("Invoice updated successfully!"); // <-- Popup alert
|
||||||
}
|
|
||||||
|
|
||||||
|
// ✅ Redirect to Add Invoice page (table part visible)
|
||||||
|
window.location.href = "{{ url_for('invoice.add_invoice') }}#addTable";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
alert("Error: " + xhr.responseJSON.message);
|
alert("Error: " + xhr.responseJSON?.message || "Something went wrong!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user