diff --git a/AppCode/Auth.py b/AppCode/Auth.py new file mode 100644 index 0000000..cf92480 --- /dev/null +++ b/AppCode/Auth.py @@ -0,0 +1,54 @@ +from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json +from flask_login import LoginManager, UserMixin + +from logging.handlers import RotatingFileHandler +from ldap3 import Server, Connection, ALL, SUBTREE + +from ldap3 import Server, Connection, ALL +from ldap3.core.exceptions import LDAPBindError + + + +class DefaultCredentials: + username = 'admin' + password = 'admin123' + +class LoginLDAP: + def __init__(self, request): + self.username = request.form['username'].strip() + self.password = request.form['password'] + self.isDefaultCredentials = False + self.isValidLogin = False + self.errorMessage = "" + ldap_user_dn = f"uid={self.username},ou=users,dc=lcepl,dc=org" + ldap_server = 'ldap://localhost:389' + #Need to re-factor further + + + # Static fallback user + if self.username == DefaultCredentials.username and self.password == DefaultCredentials.password: + self.isDefaultCredentials = True + self.isValidLogin = True + return + + try: + # LDAP authentication + conn = Connection( + Server(self.ldap_server, get_info=ALL), + user=self.ldap_user_dn, + password=self.password, + auto_bind=True + ) + + self.isValidLogin = True + return + + except LDAPBindError: + self.errorMessage = "Invalid credentials." + except Exception as e: + self.errorMessage = str(e) + + +class User(UserMixin): + def __init__(self, id): + self.id = id diff --git a/AppCode/Block.py b/AppCode/Block.py new file mode 100644 index 0000000..84e7a58 --- /dev/null +++ b/AppCode/Block.py @@ -0,0 +1,108 @@ +from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json +from flask import current_app + +from datetime import datetime +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + +from AppCode.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType +from AppCode.Log import LogData, LogHelper + +import os +import config +import re + +import mysql.connector +from mysql.connector import Error + +from AppCode.ItemCRUD import ItemCRUD, itemCRUDMapping + + +class Block: + + isSuccess = False + resultMessage = "" + + def __init__(self): + self.isSuccess = False + self.resultMessage = "" + + # ---------------------------------------------------------- + # Add Block + # ---------------------------------------------------------- + def AddBlock(self, request): + + block = ItemCRUD(itemType=ItemCRUDType.Block) + + district_id = request.form.get('district_Id') + block_name = request.form.get('block_Name', '').strip() + + block.AddItem(request=request, parentid=district_id, childname=block_name, storedprocfetch="GetVillageByNameAndBlock", storedprocadd="SaveVillage" ) + self.isSuccess = block.isSuccess + self.resultMessage = block.resultMessage + return + + # ---------------------------------------------------------- + # Get All Blocks + # ---------------------------------------------------------- + def GetAllBlocks(self): + + block = ItemCRUD(itemType=ItemCRUDType.Block) + blocksdata = block.GetAllData(request=request, storedproc="GetAllBlock") + self.isSuccess = block.isSuccess + self.resultMessage = block.resultMessage + return blocksdata + + + + # ---------------------------------------------------------- + # Check Block Exists + # ---------------------------------------------------------- + + def CheckBlock(self, request): + block = ItemCRUD(itemType=ItemCRUDType.Block) + block_name = request.json.get('block_Name', '').strip() + district_id = request.json.get('district_Id') + result = block.CheckItem(request=request, parentid=district_id, childname=block_name, storedprocfetch="GetBlockByNameAndDistrict") + self.isSuccess = block.isSuccess + self.resultMessage = block.resultMessage + return result + + + # ---------------------------------------------------------- + # Get Block By ID + # ---------------------------------------------------------- + def GetBlockByID(self, id): + + block = ItemCRUD(itemType=ItemCRUDType.Village) + blockdata = block.GetAllData("GetBlockDataByID") + self.isSuccess = block.isSuccess + self.resultMessage = block.resultMessage + return blockdata + + + # ---------------------------------------------------------- + # Update Block + # ---------------------------------------------------------- + def EditBlock(self, request, block_id): + + block = ItemCRUD(itemType=ItemCRUDType.Block) + + district_id = request.form.get('district_Id') + block_name = request.form.get('block_Name', '').strip() + + block.EditItem(request=request, childid=block_id, parentid=district_id, childname=block_name, storedprocadd="UpdateBlockById" ) + self.isSuccess = block.isSuccess + self.resultMessage = block.resultMessage + return + + + # ---------------------------------------------------------- + # Delete Block + # ---------------------------------------------------------- + def DeleteBlock(self, id): + block = ItemCRUD(itemType=ItemCRUDType.Block) + + block.DeleteItem(request=request, itemID=id, storedprocDelete="DeleteBlock" ) + self.isSuccess = block.isSuccess + self.resultMessage = block.resultMessage + return \ No newline at end of file diff --git a/AppCode/ContractorInfo.py b/AppCode/ContractorInfo.py new file mode 100644 index 0000000..88fa7f5 --- /dev/null +++ b/AppCode/ContractorInfo.py @@ -0,0 +1,76 @@ +import mysql.connector +from mysql.connector import Error +import config +import openpyxl +import os +import re +import ast +from datetime import datetime + + +class ContractorInfo: + ID = "" + contInfo = None + def __init__(self, id): + self.ID = id + print(id) + self.fetchData() + + def fetchData(self): + try: + connection = config.get_db_connection() + cursor = connection.cursor(dictionary=True, buffered=True) + print("here", flush=True) + + cursor.callproc('GetContractorInfoById', [self.contInfo]) + #self.contInfo = next(cursor.stored_results()).fetchone() + self.contInfo = cursor.fetchone() + + print(self.contInfo,flush=True) + finally: + cursor.close() + connection.close() + + def fetchalldata(self): + + try: + connection = config.get_db_connection() + cursor = connection.cursor(dictionary=True, buffered=True) + print("here", flush=True) + + + # ---------------- Hold Types ---------------- + cursor.execute(""" + SELECT DISTINCT ht.hold_type_id, ht.hold_type + FROM invoice_subcontractor_hold_join h + JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id + WHERE h.Contractor_Id = %s + """, (self.contractor_id,)) + hold_types = cursor.fetchall() + hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} + + # ---------------- Invoices ---------------- + cursor.execute(""" + SELECT i.*, v.Village_Name + FROM assign_subcontractors asg + INNER JOIN invoice i ON i.PMC_No = asg.PMC_No AND i.Village_Id = asg.Village_Id + LEFT JOIN villages v ON i.Village_Id = v.Village_Id + WHERE asg.Contractor_Id = %s + ORDER BY i.PMC_No, i.Invoice_No + """, (self.contractor_id,)) + invoices = cursor.fetchall() + + # Remove duplicate invoices + invoice_ids_seen = set() + unique_invoices = [] + for inv in invoices: + if inv["Invoice_Id"] not in invoice_ids_seen: + invoice_ids_seen.add(inv["Invoice_Id"]) + unique_invoices.append(inv) + invoices = unique_invoices + + finally: + cursor.close() + connection.close() + + diff --git a/AppCode/District.py b/AppCode/District.py new file mode 100644 index 0000000..4402b3e --- /dev/null +++ b/AppCode/District.py @@ -0,0 +1,79 @@ +from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json +from flask import current_app + +from datetime import datetime +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + +from AppCode.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType +from AppCode.Log import LogData, LogHelper + +import os +import config +import re + +import mysql.connector +from mysql.connector import Error + +from AppCode.ItemCRUD import ItemCRUD +class District: + + isSuccess = False + resultMessage = "" + + def __init__(self): + self.isSuccess = False + self.resultMessage = "" + + + def EditDistrict(self, request, district_id): + district = ItemCRUD(itemType=ItemCRUDType.District) + + district_name = request.form['district_Name'].strip() + state_id = request.form['state_Id'] + + district.EditItem(request=request, childid=district_id, parentid=state_id, childname=district_name, storedprocadd="UpdateBlockById" ) + self.isSuccess = district.isSuccess + self.resultMessage = district.resultMessage + return + + + def AddDistrict(self, request): + + district = ItemCRUD(ItemCRUDType.District) + + district_name = request.form['district_Name'].strip() + state_id = request.form['state_Id'] + + district.AddItem(request=request, parentid=state_id, childname=district_name, storedprocfetch="GetDistrictByNameAndState", storedprocadd="SaveDistrict" ) + self.isSuccess = district.isSuccess + self.resultMessage = district.resultMessage + return + + + + def GetAllDistricts(self, request): + district = ItemCRUD(itemType=ItemCRUDType.District) + districtsdata = district.GetAllData(request=request, storedproc="GetAllDistricts") + self.isSuccess = district.isSuccess + self.resultMessage = district.resultMessage + return districtsdata + + + def CheckDistrict(self, request): + district = ItemCRUD(itemType=ItemCRUDType.District) + district_name = request.json.get('district_Name', '').strip() + state_id = request.json.get('state_Id', '') + result = district.CheckItem(request=request, parentid=state_id, childname=district_name, storedprocfetch="GetDistrictByNameAndState") + self.isSuccess = district.isSuccess + self.resultMessage = district.resultMessage + return result + + + def GetDistrictByID(self, request, id): + district = ItemCRUD(itemType=ItemCRUDType.Village) + districtdata = district.GetAllData("GetDistrictDataByID") + self.isSuccess = district.isSuccess + self.resultMessage = district.resultMessage + return districtdata + + \ No newline at end of file diff --git a/AppCode/ItemCRUD.py b/AppCode/ItemCRUD.py new file mode 100644 index 0000000..91b3db9 --- /dev/null +++ b/AppCode/ItemCRUD.py @@ -0,0 +1,248 @@ + +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + +from AppCode.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType +from AppCode.Log import LogData, LogHelper + +import os +import config +import re + +import mysql.connector +from mysql.connector import Error + + +class itemCRUDMapping: + name = "" + + def __init__(self, itemType): + if itemType is ItemCRUDType.Village: + self.name = "Village" + elif itemType is ItemCRUDType.Block: + self.name = "Block" + elif itemType is ItemCRUDType.State: + self.name = "State" + + + +class ItemCRUD: + isSuccess = False + resultMessage = "" + itemCRUDType = ItemCRUDType.Village + itemCRUDMapping = ItemCRUDType(itemCRUDType) + #itemCRUDMapping itemCRUDMapping + + + def __init__(self, itemType): + self.isSuccess = False + self.resultMessage = "" + self.itemCRUDType = itemType + self.itemCRUDMapping = ItemCRUDType(self.itemCRUDType) + + + def DeleteItem(self, request, itemID, storedprocDelete): + self.isSuccess = False + self.resultMessage = "" + connection = config.get_db_connection() + cursor = connection.cursor() + LogHelper.log_action("Delete Village", f"User {current_user.id} deleted village '{itemID}'") + + try: + cursor.callproc(storedprocDelete, (itemID,)) + connection.commit() + self.resultMessage = ResponseHandler.delete_success(self.itemCRUDMapping.name) # Simple message, route will handle redirect + self.isSuccess = True + except mysql.connector.Error as e: + print(f"Error deleting village: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.delete_failure(self.itemCRUDMapping.name), 500) + finally: + cursor.close() + connection.close() + + #return self.resultMessage + + + + def AddItem(self, request, parentid, childname, storedprocfetch, storedprocadd): + + connection = config.get_db_connection() + if not connection: + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.db_connection_failure(), 500) + return + + cursor = connection.cursor() + + + LogHelper.log_action(f"Add '{self.itemCRUDMapping.name}'", + f"User {current_user.id} adding '{self.itemCRUDMapping.name}' '{childname}' to block '{parentid}'") + + if not parentid: + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.add_failure("block"), + 400) # Assuming this is a valid response + return + + if not re.match(RegEx.patternAlphabetOnly, childname): + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400) + return + + try: + # Check if the village already exists in the block + cursor.callproc(storedprocfetch, (childname, parentid,)) + for rs in cursor.stored_results(): + existing_item = rs.fetchone() + + if existing_item: + print("Existing ", self.itemCRUDMapping.name) + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.already_exists(self.itemCRUDMapping.name), 409) + return + + # Insert new village + cursor.callproc(storedprocadd, (childname, parentid)) + connection.commit() + self.isSuccess = True + self.resultMessage = HtmlHelper.json_response(ResponseHandler.add_success(self.itemCRUDMapping.name), 200) + return + + except mysql.connector.Error as e: + print(f"Database Error: {e}") + print("DatabaseError") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.add_failure("village"), 500) + return + finally: + cursor.close() + connection.close() + + + + def EditItem(self, request, childid, parentid, childname, storedprocupdate): + """Handles the POST logic for updating a district.""" + self.isSuccess = False + self.resultMessage = "" + connection = config.get_db_connection() + cursor = connection.cursor() + + #district_name = request.form['district_Name'].strip() + #state_id = request.form['state_Id'] + LogHelper.log_action("Edit District", f"User {current_user.id} Edited District '{childid}'") + + #Need to add validation to see if item exits + + # Added validation consistent with your other Edit methods + if not re.match(RegEx.patternAlphabetOnly, childname): + self.isSuccess = False + self.resultMessage = ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400 + return self.resultMessage + + try: + cursor.callproc(storedprocupdate, (childid, parentid, childname,)) + connection.commit() + self.isSuccess = True + self.resultMessage = "Successfully Edited" + except mysql.connector.Error as e: + print(f"Error updating district: {e}") + self.isSuccess = False + self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name), 500 + finally: + cursor.close() + connection.close() + + return self.resultMessage + + + + def GetAllData(self, request, storedproc): + + data = [] + connection = config.get_db_connection() + + self.isSuccess = False + self.resultMessage = "" + + if not connection: + return [] + + cursor = connection.cursor() + + try: + cursor.callproc(storedproc) + for result in cursor.stored_results(): + data = result.fetchall() + self.isSuccess = True + + except mysql.connector.Error as e: + print(f"Error fetching villages: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500) + return [] + + finally: + cursor.close() + connection.close() + + return data + + + + def GetDataByID(self, id, storedproc): + data = None + connection = config.get_db_connection() + cursor = connection.cursor() + + try: + cursor.callproc(storedproc, (id,)) + for rs in cursor.stored_results(): + data = rs.fetchone() + + except mysql.connector.Error as e: + print(f"Error fetching block data: {e}") + return None + finally: + cursor.close() + connection.close() + + return data + + + + def CheckItem(self, request, parentid, childname, storedprocfetch): + self.isSuccess = False + self.resultMessage = "" + connection = config.get_db_connection() + cursor = connection.cursor() + + LogHelper.log_action("Check Block", f"User {current_user.id} Checked block '{childname}'") + + if not re.match(RegEx.patternAlphabetOnly, childname): + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400) + return HtmlHelper.json_response(ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400) + + try: + cursor.callproc(storedprocfetch, (childname, parentid)) + for rs in cursor.stored_results(): + existing_item = rs.fetchone() + + if existing_item: + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.already_exists(self.itemCRUDMapping.name), 409) + return HtmlHelper.json_response(ResponseHandler.already_exists(self.itemCRUDMapping.name), 409) + + self.isSuccess = True + self.resultMessage = HtmlHelper.json_response(ResponseHandler.is_available(self.itemCRUDMapping.name), 200) + return HtmlHelper.json_response(ResponseHandler.is_available(self.itemCRUDMapping.name), 200) + + except mysql.connector.Error as e: + print(f"Error checking block: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500) + + finally: + cursor.close() + connection.close() diff --git a/AppCode/Log.py b/AppCode/Log.py new file mode 100644 index 0000000..d84215b --- /dev/null +++ b/AppCode/Log.py @@ -0,0 +1,87 @@ +from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json +from flask import current_app + +from datetime import datetime +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + +import os + +class LogHelper: + @staticmethod + def log_action(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.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + if hasattr(current_user, "cn") and current_user.cn: + self.user = current_user.cn + elif hasattr(current_user, "username") and current_user.username: + self.user = current_user.username + elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName: + self.user = current_user.sAMAccountName + else: + self.user = "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} | " + f"User: {self.user} | " + f"Action: {action} | " + f"Details: {details}\n" + ) + + + def GetActivitiesLog(self): + logs = [] + + if os.path.exists(self.filepath): + with open(self.filepath, 'r') as f: + for line in f: + parts = line.strip().split(" | ") + if len(parts) == 4: + logs.append({ + "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 GetFilteredActivitiesLog(self, startDate, endDate, userName): + + filtered_logs = self.GetActivitiesLog() + + # 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 + + + 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) + #Why catching all exceptions? Need to handle specific exceptions + + # Username filter + if userName: + filtered_logs = [log for log in filtered_logs if userName.lower() in log["user"].lower()] + + return filtered_logs \ No newline at end of file diff --git a/AppCode/ReportGenerator.py b/AppCode/ReportGenerator.py new file mode 100644 index 0000000..e69de29 diff --git a/AppCode/State.py b/AppCode/State.py new file mode 100644 index 0000000..8646119 --- /dev/null +++ b/AppCode/State.py @@ -0,0 +1,246 @@ +from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json +from flask import current_app + +from datetime import datetime +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + +from AppCode.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType +from AppCode.Log import LogData, LogHelper + +import os +import config +import re + +import mysql.connector +from mysql.connector import Error + +class State: + + isSuccess = False + resultMessage = "" + + def __init__(self): + self.isSuccess = False + self.resultMessage = "" + + def AddState(self, request): + """Log user actions with timestamp, user, action, and details.""" + statedata = [] + + connection = config.get_db_connection() + + if connection: + cursor = connection.cursor() + state_name = request.form['state_Name'].strip() + LogHelper.log_action("Add State", f"User {current_user.id} added state '{state_name}'") + + if not re.match(RegEx.patternAlphabetOnly, state_name): + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.invalid_name("state"), 400) + return + + try: + + cursor.callproc("CheckStateExists", (state_name,)) + for data in cursor.stored_results(): + existing_state = data.fetchone() + + if existing_state: + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.already_exists("state"), 409) + return + + # cursor.execute("call SaveState (%s)", (state_name,)) + cursor.callproc("SaveState", (state_name,)) + connection.commit() + self.isSuccess = True + self.resultMessage = HtmlHelper.json_response(ResponseHandler.add_success("state"), 200) + return + + except mysql.connector.Error as e: + print(f"Error inserting state: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.add_failure("state"), 500) + return + + #Need to make this seperate + + + + def GetAllStates(self, request): + """Log user actions with timestamp, user, action, and details.""" + statedata = [] + connection = config.get_db_connection() + + self.isSuccess = False + self.resultMessage = "" + + if not connection: + return [] + + cursor = connection.cursor() + + try: + cursor.callproc("GetAllStates") + for res in cursor.stored_results(): + statedata = res.fetchall() + self.isSuccess = True + + + except mysql.connector.Error as e: + print(f"Error fetching states: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.fetch_failure("state"), 500) + return [] + + finally: + cursor.close() + connection.close() + + return statedata + + + + def CheckState(self, request): + self.isSuccess = False + self.resultMessage = "" + + connection = config.get_db_connection() + #connection closing needs to be verified + if connection: + cursor = connection.cursor() + state_name = request.json.get('state_Name', '').strip() + LogHelper.log_action("Check State", f"User {current_user.id} Checked state '{state_name}'") + if not re.match(RegEx.patternAlphabetOnly, state_name): + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.invalid_name("state"), 400) + return HtmlHelper.json_response(ResponseHandler.invalid_name("state"), 400) + + try: + # cursor.execute("SELECT * FROM states WHERE State_Name = %s", (state_name,)) + # existing_state = cursor.fetchone() + + cursor.callproc("CheckStateExists", (state_name,)) + for data in cursor.stored_results(): + existing_state = data.fetchone() + + if existing_state: + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.already_exists("state"), 409) + return HtmlHelper.json_response(ResponseHandler.already_exists("state"), 409) + else: + self.isSuccess = True + self.resultMessage = HtmlHelper.json_response(ResponseHandler.is_available("state"), 200) + return HtmlHelper.json_response(ResponseHandler.is_available("state"), 200) + + except mysql.connector.Error as e: + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.add_failure("state"), 500) + + print(f"Error checking state: {e}") + return HtmlHelper.json_response(ResponseHandler.add_failure("state"), 500) + finally: + cursor.close() + connection.close() + + + + def DeleteState(self, request, id): + self.isSuccess = False + self.resultMessage = "" + + connection = config.get_db_connection() + cursor = connection.cursor() + LogHelper.log_action("Delete State", f"User {current_user.id} Deleted state '{id}'") + try: + cursor.callproc('DeleteState', (id,)) + connection.commit() + + self.resultMessage = "Successfully Deleted" + self.isSuccess = True + except mysql.connector.Error as e: + print(f"Error deleting data: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.delete_failure("state"), 500) + return HtmlHelper.json_response(ResponseHandler.delete_failure("state"), 500) + + finally: + cursor.close() + connection.close() + return self.resultMessage + + + def EditState(self, request, id): + self.isSuccess = False + self.resultMessage = "" + + connection = config.get_db_connection() + cursor = connection.cursor() + # str_pattern_reg = r"^[A-Za-z\s]+$" + + state_name = request.form['state_Name'].strip() + LogHelper.log_action("Edit State", f"User {current_user.id} Edited state '{state_name}'") + if not re.match(RegEx.patternAlphabetOnly, state_name): + self.isSuccess = False + self.resultMessage = ResponseHandler.invalid_name("state"), 400 + return ResponseHandler.invalid_name("state"), 400 + + try: + # cursor.execute("UPDATE states SET State_Name = %s WHERE State_ID = %s", (state_name, id)) + cursor.callproc("UpdateStateById", (id, state_name)) + connection.commit() + self.isSuccess = True + self.resultMessage = "Successfully Edited" + return redirect(url_for('add_state')) + except mysql.connector.Error as e: + print(f"Error updating data: {e}") + self.isSuccess = True + self.resultMessage = ResponseHandler.add_failure("state"), 500 + + return ResponseHandler.add_failure("state"), 500 + finally: + cursor.close() + connection.close() + + + + + + def GetStateByID(self, request, id): + """Log user actions with timestamp, user, action, and details.""" + statedata = [] + + self.isSuccess = False + self.resultMessage = "" + + connection = config.get_db_connection() + if not connection: + return [] + cursor = connection.cursor() + + try: + cursor.callproc("GetStateByID", (id,)) + for res in cursor.stored_results(): + statedata = res.fetchone() + + if statedata: + self.isSuccess = True + self.resultMessage = "Success in Fetching" + else: + self.isSuccess = False + self.resultMessage = "State Not Found" + + + except mysql.connector.Error as e: + print(f"Error fetching states: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.fetch_failure("state"), 500) + return [] + + finally: + cursor.close() + connection.close() + + return statedata + + diff --git a/AppCode/Utilities.py b/AppCode/Utilities.py new file mode 100644 index 0000000..f6228b7 --- /dev/null +++ b/AppCode/Utilities.py @@ -0,0 +1,64 @@ +from flask import flash, jsonify, json +from enum import Enum + +class ItemCRUDType(Enum): + Village = 1 + Block = 2 + District = 3 + State = 4 + + +class RegEx: + patternAlphabetOnly = "^[A-Za-z ]+$" + + +class ResponseHandler: + @staticmethod + def invalid_name(entity): + return {'status': 'error', 'message': f'Invalid {entity} name. Only letters are allowed!'} + + @staticmethod + def already_exists(entity): + return {'status': 'exists', 'message': f'{entity.capitalize()} already exists!'} + + @staticmethod + def add_success(entity): + return {'status': 'success', 'message': f'{entity.capitalize()} added successfully!'} + + @staticmethod + def add_failure(entity): + return {'status': 'error', 'message': f'Failed to add {entity}.'} + + @staticmethod + def is_available(entity): + return {'status': 'available', 'message': f'{entity.capitalize()} name is available!'} + + @staticmethod + def delete_success(entity): + return {'status': 'success', 'message': f'{entity.capitalize()} deleted successfully!'} + + @staticmethod + def delete_failure(entity): + return {'status': 'error', 'message': f'Failed to delete {entity}.'} + + @staticmethod + def update_success(entity): + return {'status': 'success', 'message': f'{entity.capitalize()} updated successfully!'} + + @staticmethod + def update_failure(entity): + return {'status': 'error', 'message': f'Failed to update {entity}.'} + + @staticmethod + def fetch_failure(entity): + return {'status': 'error', 'message': f'Failed to fetch {entity}.'} + + +class HtmlHelper: + # Helper: JSON Response Formatter + + @staticmethod + def json_response(message_obj, status_code): + return jsonify(message_obj), status_code + #May need to refactor further + diff --git a/AppCode/Village.py b/AppCode/Village.py new file mode 100644 index 0000000..79b98db --- /dev/null +++ b/AppCode/Village.py @@ -0,0 +1,110 @@ + +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + +from AppCode.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType +from AppCode.Log import LogData, LogHelper + +import config + +import mysql.connector +from mysql.connector import Error + +from AppCode.ItemCRUD import ItemCRUD, itemCRUDMapping + + +class Village: + isSuccess = False + resultMessage = "" + + def __init__(self): + self.isSuccess = False + self.resultMessage = "" + + def AddVillage(self, request): + village = ItemCRUD(itemType=ItemCRUDType.Village) + + block_id = request.form.get('block_Id') + village_name = request.form.get('Village_Name', '').strip() + + village.AddItem(request=request, parentid=block_id, childname=village_name, storedprocfetch="GetVillageByNameAndBlock", storedprocadd="SaveVillage" ) + self.isSuccess = village.isSuccess + self.resultMessage = village.resultMessage + return + #self.isSuccess = False + + def GetAllVillages(self, request): + village = ItemCRUD(itemType=ItemCRUDType.Village) + villagesdata = village.GetAllData(request=request, storedproc="GetAllVillages") + self.isSuccess = village.isSuccess + self.resultMessage = village.resultMessage + return villagesdata + + + def CheckVillage(self, request): + village = ItemCRUD(itemType=ItemCRUDType.Village) + block_id = request.form.get('block_Id') + village_name = request.form.get('Village_Name', '').strip() + result = village.CheckItem(request=request, parentid=block_id, childname=village_name, storedprocfetch="GetVillageByNameAndBlocks") + self.isSuccess = village.isSuccess + self.resultMessage = village.resultMessage + return result + + + def DeleteVillage(self, request, village_id): + + village = ItemCRUD(itemType=ItemCRUDType.Village) + + village.DeleteItem(request=request, itemID=village_id, storedprocDelete="DeleteVillage" ) + self.isSuccess = village.isSuccess + self.resultMessage = village.resultMessage + return + + def EditVillage(self, request, village_id): + + village = ItemCRUD(itemType=ItemCRUDType.Village) + + block_id = request.form.get('block_Id') + village_name = request.form.get('Village_Name', '').strip() + + village.EditItem(request=request, childid=village_id, parentid=block_id, childname=village_name, storedprocadd="UpdateVillage" ) + self.isSuccess = village.isSuccess + self.resultMessage = village.resultMessage + return + + def GetVillageByID(self, request, id): + + village = ItemCRUD(itemType=ItemCRUDType.Village) + villagedetailsdata = village.GetAllData(request=request, storedproc="GetVillageDetailsById") + self.isSuccess = village.isSuccess + self.resultMessage = village.resultMessage + return villagedetailsdata + + + + def GetAllBlocks(self, request): + + blocks = [] + self.isSuccess = False + self.resultMessage = "" + connection = config.get_db_connection() + + if not connection: + return [] + + cursor = connection.cursor() + + try: + cursor.callproc('GetAllBlocks') + for result in cursor.stored_results(): + blocks = result.fetchall() + self.isSuccess = True + + except mysql.connector.Error as e: + print(f"Error fetching blocks: {e}") + self.isSuccess = False + self.resultMessage = HtmlHelper.json_response(ResponseHandler.fetch_failure("block"), 500) + finally: + cursor.close() + connection.close() + + return blocks \ No newline at end of file diff --git a/AppRoutes/StateRoute.py b/AppRoutes/StateRoute.py new file mode 100644 index 0000000..e69de29 diff --git a/Version-1.code-workspace b/Version-1.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/Version-1.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/activity.log b/activity.log index 2ee7a1d..e7d0566 100644 --- a/activity.log +++ b/activity.log @@ -4506,512 +4506,590 @@ Timestamp: 2025-10-04 17:27:24 | User: Unknown | Action: Search contractor | Det Timestamp: 2025-10-04 17:27:24 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' Timestamp: 2025-10-04 17:27:24 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aad' Timestamp: 2025-10-04 17:27:25 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aad' -Timestamp: 2025-10-31 11:24:34 | User: Unknown | Action: Login | Details: User admin logged in (static user) -Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' -Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aj' -Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aja' -Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ajay' -Timestamp: 2025-10-31 11:25:21 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ajay' -Timestamp: 2025-10-31 11:26:31 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 11:26:52 | User: Unknown | Action: Check State | Details: User admin Checked state 'U' -Timestamp: 2025-10-31 11:26:54 | User: Unknown | Action: Check State | Details: User admin Checked state 'Uttar Pradesh' -Timestamp: 2025-10-31 11:26:55 | User: Unknown | Action: Add State | Details: User admin added state 'Uttar Pradesh' -Timestamp: 2025-10-31 11:27:04 | User: Unknown | Action: Check District | Details: User admin Checked District 'Shamli' -Timestamp: 2025-10-31 11:27:04 | User: Unknown | Action: Check District | Details: User admin Checked District 'Shamli' -Timestamp: 2025-10-31 11:27:05 | User: Unknown | Action: Add District | Details: User admin Added District 'Shamli' -Timestamp: 2025-10-31 11:27:12 | User: Unknown | Action: Get District | Details: User admin Get District '5' -Timestamp: 2025-10-31 11:27:15 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Shamli' -Timestamp: 2025-10-31 11:27:15 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Shamli' -Timestamp: 2025-10-31 11:27:16 | User: Unknown | Action: Add Block | Details: User admin Added block 'Shamli' -Timestamp: 2025-10-31 11:27:37 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:33 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:00:54 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 12:00:56 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 12:05:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' -Timestamp: 2025-10-31 12:05:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aa' -Timestamp: 2025-10-31 12:05:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aar' -Timestamp: 2025-10-31 12:05:21 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aarh' -Timestamp: 2025-10-31 12:05:23 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aarh' -Timestamp: 2025-10-31 13:35:19 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:19 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:52:13 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 14:52:35 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 14:52:46 | User: Unknown | Action: Check State | Details: User admin Checked state 'm' -Timestamp: 2025-10-31 14:52:48 | User: Unknown | Action: Check State | Details: User admin Checked state 'Madhya Pradesh' -Timestamp: 2025-10-31 14:52:50 | User: Unknown | Action: Add State | Details: User admin added state 'Madhya Pradesh' -Timestamp: 2025-10-31 14:52:58 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' -Timestamp: 2025-10-31 14:52:58 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' -Timestamp: 2025-10-31 14:52:58 | User: Unknown | Action: Add District | Details: User admin Added District 'Chhindawara' -Timestamp: 2025-10-31 14:53:06 | User: Unknown | Action: Get District | Details: User admin Get District '7' -Timestamp: 2025-10-31 14:53:09 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' -Timestamp: 2025-10-31 14:53:09 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' -Timestamp: 2025-10-31 14:53:09 | User: Unknown | Action: Add Block | Details: User admin Added block 'Chhindawara' -Timestamp: 2025-10-31 14:53:25 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 14:53:38 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' -Timestamp: 2025-10-31 14:53:38 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' -Timestamp: 2025-10-31 14:53:43 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chindawara' -Timestamp: 2025-10-31 14:53:44 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chindawara' -Timestamp: 2025-10-31 14:53:45 | User: Unknown | Action: Add District | Details: User admin Added District 'Chindawara' -Timestamp: 2025-10-31 14:53:51 | User: Unknown | Action: Get District | Details: User admin Get District '7' -Timestamp: 2025-10-31 14:53:54 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' -Timestamp: 2025-10-31 14:53:54 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' -Timestamp: 2025-10-31 14:53:56 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chindawara' -Timestamp: 2025-10-31 14:53:56 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chindawara' -Timestamp: 2025-10-31 14:53:57 | User: Unknown | Action: Add Block | Details: User admin Added block 'Chindawara' -Timestamp: 2025-10-31 14:54:07 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:54:18 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'b' -Timestamp: 2025-10-31 14:54:18 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'bg' -Timestamp: 2025-10-31 14:54:20 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'bg' -Timestamp: 2025-10-31 14:57:08 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 14:57:25 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 14:57:52 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'j' -Timestamp: 2025-10-31 14:57:52 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ja' -Timestamp: 2025-10-31 14:57:54 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ja' -Timestamp: 2025-10-31 16:17:29 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' -Timestamp: 2025-10-31 16:17:32 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 16:17:32 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 16:17:32 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' -Timestamp: 2025-10-31 16:17:41 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' -Timestamp: 2025-10-31 16:17:41 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aj' -Timestamp: 2025-10-31 16:17:44 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aj' -Timestamp: 2025-11-03 11:03:32 | User: admin | Action: Logout | Details: User admin logged out -Timestamp: 2025-11-03 11:03:39 | User: admin | Action: Login | Details: Static admin logged in: admin +Timestamp: 2025-10-06 13:03:06 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:03:06 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:03:06 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:03:07 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:08:38 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:08:38 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:08:38 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:08:40 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:08:40 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:08:41 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'' +Timestamp: 2025-10-06 13:08:43 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-06 13:08:43 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ak' +Timestamp: 2025-10-06 13:08:44 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'akr' +Timestamp: 2025-10-06 13:08:44 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'akra' +Timestamp: 2025-10-06 13:08:45 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'akr' +Timestamp: 2025-10-06 13:08:45 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ak' +Timestamp: 2025-10-06 13:08:47 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ak' +Timestamp: 2025-10-06 13:09:20 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:09:20 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:09:21 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:09:22 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:15:23 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:15:23 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:15:24 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:15:26 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:15:26 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:15:26 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'' +Timestamp: 2025-10-06 13:15:41 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:45 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:46 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:15:51 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:15:51 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:15:52 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:15:53 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:25:20 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:22 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 13:25:25 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 13:25:26 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 13:25:26 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 13:25:27 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 14:58:02 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 14:58:02 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 14:58:05 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 15:00:04 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 15:00:04 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 15:00:06 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 15:00:21 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:24 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-06 15:00:33 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'd' +Timestamp: 2025-10-06 15:00:33 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'da' +Timestamp: 2025-10-06 15:00:33 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 15:00:34 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'day' +Timestamp: 2025-10-06 17:30:50 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' + +Timestamp: 2025-10-06 17:30:50 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aad' +Timestamp: 2025-10-06 17:30:53 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-06 17:30:53 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'' +Timestamp: 2025-10-08 12:09:50 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:09:50 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:32:42 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:32:42 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ms' +Timestamp: 2025-10-08 12:32:43 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:32:44 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:58:25 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:58:25 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:59:22 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-08 12:59:23 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-09 10:20:50 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-09 10:21:30 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-09 10:21:31 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-09 10:31:00 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-09 10:31:02 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-09 10:31:50 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:53 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:54 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-09 10:31:59 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-09 10:32:00 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'm' +Timestamp: 2025-10-14 10:25:59 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-14 10:25:59 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-15 10:27:20 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-15 10:27:29 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-15 10:27:30 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-15 15:44:05 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-15 15:44:05 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-15 15:44:24 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:27 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:28 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-15 15:44:31 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-15 15:44:32 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 14:48:46 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-23 18:28:12 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 18:28:16 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 18:31:12 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-23 18:31:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 18:31:18 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 18:37:04 | User: Unknown | Action: Logout | Details: User admin logged out +Timestamp: 2025-10-23 18:37:09 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-23 20:13:02 | User: Unknown | Action: Logout | Details: User admin logged out +Timestamp: 2025-10-23 20:13:10 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-23 20:13:36 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 20:13:36 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aa' +Timestamp: 2025-10-23 20:13:37 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aa' +Timestamp: 2025-10-23 20:13:41 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-23 20:13:43 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-11-12 15:51:31 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-11-12 15:51:35 | User: Unknown | Action: Logout | Details: User admin logged out +Timestamp: 2025-11-14 17:51:17 | User: Unknown | Action: Login | Details: +Timestamp: 2025-11-14 17:54:56 | User: Unknown | Action: Login | Details: +Timestamp: 2025-11-14 17:57:06 | User: Unknown | Action: Logout | Details: +Timestamp: 2025-11-14 17:57:11 | User: Unknown | Action: Login | Details: +Timestamp: 2025-11-14 18:14:25 | User: Unknown | Action: Search contractor | Details: +Timestamp: 2025-11-14 19:38:45 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:45 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:45 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:45 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:46 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:46 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:46 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:48 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:48 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:50 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:52 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:38:54 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-14 19:43:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:36 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:38 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:43:39 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-14 19:44:24 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:44:24 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:44:24 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:44:24 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:44:30 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:44:30 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-14 19:44:32 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:13:57 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:13:57 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:13:57 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:13:57 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:13:59 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:28:01 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:29:05 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:29:24 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:30:12 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:33:03 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:33:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:33:37 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:33:38 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:33:38 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:33:40 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:33:41 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:33:48 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:34:55 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:55 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:56 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:56 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:56 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:58 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:58 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:34:59 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:35:00 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:35:02 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:35:08 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:35:26 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:35:27 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:35:27 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:35:29 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:35:34 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:36:43 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:36:43 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:36:43 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:36:45 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:36:52 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:39:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:39:35 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:39:36 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:39:36 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:39:38 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:39:46 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:44:28 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:28 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:28 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:28 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:29 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:29 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:29 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:30 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:30 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:30 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:44:31 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:44:42 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:46:23 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:46:23 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:46:23 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:46:25 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:46:59 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:47:38 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:47:38 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:47:39 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:47:40 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:47:45 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 13:48:54 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:48:55 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:48:55 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 13:48:56 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 13:49:02 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 16:09:12 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:12 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:13 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:16 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:16 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:17 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:17 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:29 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 16:09:35 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 16:09:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:51 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:54 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:54 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:54 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:54 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:55 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:55 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:56 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:56 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:56 | User: Unknown | Action: Check State | Details: +Timestamp: 2025-11-15 16:09:57 | User: Unknown | Action: Add State | Details: +Timestamp: 2025-11-15 16:10:08 | User: Unknown | Action: Delete State | Details: +Timestamp: 2025-11-15 16:18:38 | User: Unknown | Action: Edit State | Details: +Timestamp: 2025-11-15 16:18:53 | User: Unknown | Action: Edit State | Details: +Timestamp: 2025-11-15 16:43:27 | User: Unknown | Action: Edit State | Details: +Timestamp: 2025-11-15 18:42:45 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-15 18:42:49 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-15 18:42:49 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-15 18:42:49 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-15 18:42:49 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-15 18:42:50 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-15 18:42:52 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-15 18:42:55 | User: Unknown | Action: Add Block | Details: +Timestamp: 2025-11-16 10:50:37 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 10:51:40 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 10:52:00 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 10:52:06 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-16 10:52:07 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-16 10:52:08 | User: Unknown | Action: Add Block | Details: +Timestamp: 2025-11-16 10:52:22 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 10:59:26 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 11:13:51 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 11:18:30 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 11:18:44 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:44 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:44 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:44 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:44 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:45 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:45 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:46 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:49 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:49 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:18:50 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:21:31 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:21:31 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:21:31 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:21:33 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:21:33 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:24:16 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:24:16 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:24:16 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:24:16 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:24:18 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:24:18 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:26:44 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:27:23 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:27:34 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:27:34 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:27:35 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:27:35 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:27:36 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:27:36 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:28:04 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:29:03 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-16 11:32:07 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:07 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:07 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:09 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:09 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:09 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:09 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:11 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-16 11:32:11 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-16 11:44:09 | User: Unknown | Action: Check District | Details: +Timestamp: 2025-11-16 11:44:10 | User: Unknown | Action: Check District | Details: +Timestamp: 2025-11-16 11:44:10 | User: Unknown | Action: Check District | Details: +Timestamp: 2025-11-16 11:44:10 | User: Unknown | Action: Check District | Details: +Timestamp: 2025-11-16 11:44:13 | User: Unknown | Action: Check District | Details: +Timestamp: 2025-11-16 11:44:13 | User: Unknown | Action: Add Village | Details: +Timestamp: 2025-11-17 13:42:52 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-17 13:42:56 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:57 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:57 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:57 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:57 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:58 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:59 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:42:59 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:43:01 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:43:01 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:43:01 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:43:03 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:07 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:07 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:08 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:09 | User: Unknown | Action: Check Village | Details: +Timestamp: 2025-11-17 13:46:09 | User: Unknown | Action: Add 'Village' | Details: +Timestamp: 2025-11-17 22:37:38 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-17 22:37:42 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:42 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:42 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:43 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:43 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:43 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:44 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:37:44 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-17 22:37:53 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-17 22:38:29 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:38:30 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:38:30 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:38:32 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:38:32 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-17 22:38:56 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-17 22:39:08 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:39:09 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:39:09 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:39:10 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:39:10 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-17 22:42:02 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-17 22:42:10 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:42:10 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:42:11 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-17 22:42:15 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:42:15 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-17 22:42:17 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-18 00:47:33 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-18 00:47:39 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:47:39 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:47:39 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:47:40 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:47:42 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:47:42 | User: Unknown | Action: Add 'Block' | Details: +Timestamp: 2025-11-18 00:48:49 | User: Unknown | Action: Get District | Details: +Timestamp: 2025-11-18 00:48:55 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:48:57 | User: Unknown | Action: Check Block | Details: +Timestamp: 2025-11-18 00:48:57 | User: Unknown | Action: Add 'Village' | Details: +Timestamp: 2025-11-18 09:58:53 | User: Unknown | Action: Login | Details: +Timestamp: 2025-11-24 12:57:24 | User: Unknown | Action: Login | Details: +Timestamp: 2025-11-25 14:12:56 | User: Unknown | Action: Login | Details: diff --git a/config.py b/config.py index 73e490f..5d13397 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ import os # Get MySQL credentials from environment variables MYSQL_HOST = os.getenv("MYSQL_HOST", "127.0.0.1") MYSQL_USER = os.getenv("MYSQL_USER", "root") -MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "tiger") +MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "root") MYSQL_DB = os.getenv("MYSQL_DB", "test") # Connect to MySQL diff --git a/main.py b/main.py index 19be04e..870a7b9 100644 --- a/main.py +++ b/main.py @@ -1,52 +1,43 @@ from decimal import Decimal +from AppCode import ContractorInfo, Auth, Utilities, Log +from AppCode.Utilities import RegEx, ResponseHandler, HtmlHelper +from AppCode.Auth import LoginLDAP, User +from AppCode.Log import LogData, LogHelper +from AppCode.State import State +from AppCode.District import District +from AppCode.Block import Block +from AppCode.Village import Village +# need to optimize above import lines + from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json +from flask import current_app, session, send_file + +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user + import mysql.connector from mysql.connector import Error + import config -import openpyxl import os import re import ast from datetime import datetime -import pandas as pd -from openpyxl.styles import Font -from flask import Flask, render_template, request, redirect, url_for, flash, session -from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user -from ldap3 import Server, Connection, ALL + + + +import openpyxl +from openpyxl import load_workbook +from openpyxl.styles import Font, PatternFill, Alignment +from openpyxl.utils import get_column_letter + + import logging -from logging.handlers import RotatingFileHandler -from flask import Flask, request, render_template -from ldap3 import Server, Connection, ALL, SUBTREE + +import pandas as pd - - -from flask import request, session -0 -def log_action(action, details=""): - """Log user actions with timestamp, user, action, and details.""" - log_file = os.path.join(current_app.root_path, 'activity.log') - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - - # Prefer LDAP common name (cn), fallback to username, else Unknown - if hasattr(current_user, "cn") and current_user.cn: - user = current_user.cn - elif hasattr(current_user, "username") and current_user.username: - user = current_user.username - elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName: - user = current_user.sAMAccountName - else: - user = "Unknown" - - with open(log_file, "a", encoding="utf-8") as f: - f.write( - f"Timestamp: {timestamp} | " - f"User: {user} | " - f"Action: {action} | " - f"Details: {details}\n" - ) - +#import AppRoutes.StateRoute # this is server @@ -55,64 +46,16 @@ login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'login' -class User(UserMixin): - def __init__(self, id): - self.id = id @login_manager.user_loader def load_user(user_id): return User(user_id) +#need to check and understand above function app.secret_key = '9f2a1b8c4d6e7f0123456789abcdef01' - -str_pattern_reg = "^[A-Za-z ]+$" +#Shouldnt be hardcoded -class ResponseHandler: - @staticmethod - def invalid_name(entity): - return {'status': 'error', 'message': f'Invalid {entity} name. Only letters are allowed!'} - - @staticmethod - def already_exists(entity): - return {'status': 'exists', 'message': f'{entity.capitalize()} already exists!'} - - @staticmethod - def add_success(entity): - return {'status': 'success', 'message': f'{entity.capitalize()} added successfully!'} - - @staticmethod - def add_failure(entity): - return {'status': 'error', 'message': f'Failed to add {entity}.'} - - @staticmethod - def is_available(entity): - return {'status': 'available', 'message': f'{entity.capitalize()} name is available!'} - - @staticmethod - def delete_success(entity): - return {'status': 'success', 'message': f'{entity.capitalize()} deleted successfully!'} - - @staticmethod - def delete_failure(entity): - return {'status': 'error', 'message': f'Failed to delete {entity}.'} - - @staticmethod - def update_success(entity): - return {'status': 'success', 'message': f'{entity.capitalize()} updated successfully!'} - - @staticmethod - def update_failure(entity): - return {'status': 'error', 'message': f'Failed to update {entity}.'} - - @staticmethod - def fetch_failure(entity): - return f"Failed to fetch {entity}" - - -# Helper: JSON Response Formatter -def json_response(message_obj, status_code): - return jsonify(message_obj), status_code # this is Index page OR Home page.. @@ -122,40 +65,6 @@ def index(): return render_template('index.html') -from ldap3 import Server, Connection, ALL -from ldap3.core.exceptions import LDAPBindError - - -# ---------------- LDAP SEARCH ---------------- -@app.route('/ldap_search', methods=['GET', 'POST']) -def ldap_search(): - results = [] - if request.method == 'POST': - username = request.form['username'] - password = request.form['password'] - ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org" - - try: - # Connect to OpenLDAP using Docker service name - server = Server('openldap', port=389, get_info=ALL) - conn = Connection(server, user=ldap_user_dn, password=password, auto_bind=True) - - # Search for the user - conn.search( - 'dc=lcepl,dc=org', - f'(uid={username})', - search_scope=SUBTREE, - attributes=['cn', 'uid', 'mail'] - ) - - results = conn.entries - conn.unbind() - except LDAPBindError: - flash('Invalid LDAP credentials.', 'danger') - except Exception as e: - flash(f'LDAP error: {str(e)}', 'danger') - - return render_template('ldap_search.html', results=results) @@ -164,37 +73,20 @@ def ldap_search(): @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - username = request.form['username'].strip() - password = request.form['password'] - - # Static fallback user - if username == 'admin' and password == 'admin123': - session['username'] = username - log_action('Login', f"User {username} logged in (static user)") - login_user(User(username)) + loginData = LoginLDAP(request) + # If bind successful → set session and log + + if loginData.isValidLogin: + if loginData.isDefaultCredentials: + LogHelper.log_action('Login', f"User {loginData.username} logged in (static user)") + else: + LogHelper.log_action('Login', f"User {loginData.username} logged in (LDAP)") + + session['username'] = loginData.username + login_user(User(loginData.username)) return redirect(url_for('index', login='success')) - - ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org" - - try: - # LDAP authentication - conn = Connection( - Server('ldap://localhost:389', get_info=ALL), - user=ldap_user_dn, - password=password, - auto_bind=True - ) - - # If bind successful → set session and log - session['username'] = username - log_action('Login', f"User {username} logged in (LDAP)") - login_user(User(username)) - return redirect(url_for('index', login='success')) - - except LDAPBindError: - flash('Invalid credentials.', 'danger') - except Exception as e: - flash(f'LDAP error: {str(e)}', 'danger') + else: + flash(loginData.errorMessage, 'danger') return render_template('login.html') @@ -204,188 +96,69 @@ def login(): @app.route('/logout') @login_required def logout(): - log_action('Logout', f"User {current_user.id} logged out") # log the event + LogHelper.log_action('Logout', f"User {current_user.id} logged out") # log the event logout_user() flash('You have been logged out.', 'info') return redirect(url_for('login')) -import os -from datetime import datetime -from flask import current_app -from flask_login import current_user -import os -from flask import request, render_template, current_app -from datetime import datetime -from flask_login import login_required -from dateutil import parser # pip install python-dateutil - @app.route('/activity_log', methods=['GET', 'POST']) @login_required def activity_log(): - logs = [] - log_file = os.path.join(current_app.root_path, 'activity.log') - - if os.path.exists(log_file): - with open(log_file, 'r') as f: - for line in f: - parts = line.strip().split(" | ") - if len(parts) == 4: - logs.append({ - "timestamp": parts[0].replace("Timestamp:", "").strip(), - "user": parts[1].replace("User:", "").strip(), - "action": parts[2].replace("Action:", "").strip(), - "details": parts[3].replace("Details:", "").strip() - }) - + # Filters (GET or POST) start_date = request.values.get("start_date") end_date = request.values.get("end_date") - username = request.values.get("username") - - filtered_logs = logs - - # Date filter - if start_date or end_date: - try: - 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 - end_dt = end_dt.replace(hour=23, minute=59, second=59) - - 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()] + user_name = request.values.get("username") + logData = LogData() + filtered_logs = logData.GetFilteredActivitiesLog(start_date,end_date,user_name) + return render_template( "activity_log.html", logs=filtered_logs, start_date=start_date, end_date=end_date, - username=username + username=user_name ) - # ------------------------- State controller ------------------------------------------ @app.route('/add_state', methods=['GET', 'POST']) @login_required def add_state(): - connection = config.get_db_connection() - statedata = [] - - if connection: - cursor = connection.cursor() - if request.method == 'POST': - state_name = request.form['state_Name'].strip() - log_action("Add State", f"User {current_user.id} added state '{state_name}'") - - if not re.match(str_pattern_reg, state_name): - return json_response(ResponseHandler.invalid_name("state"), 400) - - try: - # cursor.execute("SELECT * FROM states WHERE State_Name = %s", (state_name,)) - # if cursor.fetchone(): - # return json_response(ResponseHandler.already_exists("state"), 409) - - cursor.callproc("CheckStateExists", (state_name,)) - for data in cursor.stored_results(): - existing_state = data.fetchone() - - if existing_state: - return json_response(ResponseHandler.already_exists("state"), 409) - - # cursor.execute("call SaveState (%s)", (state_name,)) - cursor.callproc("SaveState", (state_name,)) - connection.commit() - return json_response(ResponseHandler.add_success("state"), 200) - - except mysql.connector.Error as e: - print(f"Error inserting state: {e}") - return json_response(ResponseHandler.add_failure("state"), 500) - - try: - # cursor.execute("SELECT State_ID, State_Name FROM states") - # statedata = cursor.fetchall() - cursor.callproc("GetAllStates") - for res in cursor.stored_results(): - statedata = res.fetchall() - except mysql.connector.Error as e: - print(f"Error fetching states: {e}") - return ResponseHandler.fetch_failure("states"), 500 - finally: - cursor.close() - connection.close() - + state = State() + if request.method == 'POST': + state.AddState(request=request) + return state.resultMessage + + statedata = state.GetAllStates(request=request) return render_template('add_state.html', statedata=statedata) + # AJAX route to check state existence @app.route('/check_state', methods=['POST']) @login_required def check_state(): - connection = config.get_db_connection() - - if connection: - cursor = connection.cursor() - state_name = request.json.get('state_Name', '').strip() - log_action("Check State", f"User {current_user.id} Checked state '{state_name}'") - if not re.match(str_pattern_reg, state_name): - return json_response(ResponseHandler.invalid_name("state"), 400) - - try: - # cursor.execute("SELECT * FROM states WHERE State_Name = %s", (state_name,)) - # existing_state = cursor.fetchone() - - cursor.callproc("CheckStateExists", (state_name,)) - for data in cursor.stored_results(): - existing_state = data.fetchone() - - if existing_state: - return json_response(ResponseHandler.already_exists("state"), 409) - else: - return json_response(ResponseHandler.is_available("state"), 200) - - except mysql.connector.Error as e: - print(f"Error checking state: {e}") - return json_response(ResponseHandler.add_failure("state"), 500) - finally: - cursor.close() - connection.close() - + state = State() + return state.CheckState(request=request) + # Delete State @app.route('/delete_state/', methods=['GET']) @login_required def deleteState(id): - connection = config.get_db_connection() - cursor = connection.cursor() - log_action("Delete State", f"User {current_user.id} Deleted state '{id}'") - try: - # cursor.execute("DELETE FROM states WHERE State_ID = %s", (id,)) - cursor.callproc('DeleteState', (id,)) - connection.commit() - # For API response - # return json_response(ResponseHandler.delete_success("state"), 200) + state = State() + msg = state.DeleteState(request=request, id=id) - except mysql.connector.Error as e: - print(f"Error deleting data: {e}") - return json_response(ResponseHandler.delete_failure("state"), 500) - - finally: - cursor.close() - connection.close() - - return redirect(url_for('add_state')) + if not state.isSuccess: + return state.resultMessage + else: + return redirect(url_for('add_state')) # Edit State @@ -394,42 +167,27 @@ def deleteState(id): def editState(id): connection = config.get_db_connection() cursor = connection.cursor() - # str_pattern_reg = r"^[A-Za-z\s]+$" + + state = State() + statedata = [] if request.method == 'POST': - state_name = request.form['state_Name'].strip() - log_action("Edit State", f"User {current_user.id} Edited state '{state_name}'") - if not re.match(str_pattern_reg, state_name): - return ResponseHandler.invalid_name("state"), 400 + state.EditState(request=request, id=id) + if state.isSuccess: + return redirect(url_for('add_state')) + else: + return state.resultMessage + else: + statedata = state.GetStateByID(request=request, id=id) + + if not state.isSuccess: + return state.resultMessage + - try: - # cursor.execute("UPDATE states SET State_Name = %s WHERE State_ID = %s", (state_name, id)) - cursor.callproc("UpdateStateById", (id, state_name)) - connection.commit() - return redirect(url_for('add_state')) - except mysql.connector.Error as e: - print(f"Error updating data: {e}") - return ResponseHandler.add_failure("state"), 500 - finally: - cursor.close() - connection.close() + if statedata is None: + statedata = [] - try: - # cursor.execute("SELECT * FROM states WHERE State_ID = %s", (id,)) - # state = cursor.fetchone() - cursor.callproc("GetStateByID", (id,)) - for result in cursor.stored_results(): - state = result.fetchone() - if state is None: - return "State not found", 404 - except mysql.connector.Error as e: - print(f"Error retrieving data: {e}") - return ResponseHandler.fetch_failure("state"), 500 - finally: - cursor.close() - connection.close() - - return render_template('edit_state.html', state=state) + return render_template('edit_state.html', state=statedata) # -------- end State controller ----------- @@ -438,62 +196,16 @@ def editState(id): @app.route('/add_district', methods=['GET', 'POST']) @login_required def add_district(): - connection = config.get_db_connection() - districtdata = [] - states = [] - - if connection: - cursor = connection.cursor() - - try: - # cursor.execute("SELECT State_ID, State_Name FROM states") - # states = cursor.fetchall() - cursor.callproc("GetAllStates") - for res in cursor.stored_results(): - states = res.fetchall() - - except mysql.connector.Error as e: - print(f"Error fetching states: {e}") - return ResponseHandler.fetch_failure("states"), 500 - - if request.method == 'POST': - district_name = request.form['district_Name'].strip() - state_id = request.form['state_Id'] - log_action("Add District", f"User {current_user.id} Added District '{district_name}'") - if not re.match(str_pattern_reg, district_name): - return json_response(ResponseHandler.invalid_name("district"), 400) - - try: - # cursor.execute("SELECT * FROM districts WHERE District_Name = %s AND State_Id = %s", - # (district_name, state_id)) - cursor.callproc("GetDistrictByNameAndState", (district_name, state_id)) - for data in cursor.stored_results(): - rs = data.fetchone() - if rs: - return json_response(ResponseHandler.already_exists("district"), 409) - - cursor.callproc('SaveDistrict', (district_name, state_id)) - connection.commit() - - return json_response(ResponseHandler.add_success("district"), 200) - - except mysql.connector.Error as e: - print(f"Error inserting district: {e}") - return json_response(ResponseHandler.add_failure("district"), 500) - - try: - # cursor.execute("SELECT d.District_id, d.District_Name, s.State_Name, s.State_Id FROM districts d JOIN states s ON d.State_Id = s.State_ID") - # districtdata = cursor.fetchall() - cursor.callproc("GetAllDistricts") - for dis in cursor.stored_results(): - districtdata = dis.fetchall() - except mysql.connector.Error as e: - print(f"Error fetching districts: {e}") - return ResponseHandler.fetch_failure("districts"), 500 - finally: - cursor.close() - connection.close() - + district = District() + + if request.method == 'POST': + district.AddDistrict(request=request) + return district.resultMessage + state = State() + states = state.GetAllStates(request=request) + + districtdata = district.GetAllDistricts(request=request) + return render_template('add_district.html', districtdata=districtdata, states=states) @@ -501,110 +213,58 @@ def add_district(): @app.route('/check_district', methods=['POST']) @login_required def check_district(): - connection = config.get_db_connection() + district = District() + return district.CheckDistrict(request=request) + - if connection: - cursor = connection.cursor() - district_name = request.json.get('district_Name', '').strip() - state_id = request.json.get('state_Id', '') - log_action("Check District", f"User {current_user.id} Checked District '{district_name}'") - if not re.match(str_pattern_reg, district_name): - return json_response(ResponseHandler.invalid_name("district"), 400) - - try: - # cursor.execute("SELECT * FROM districts WHERE District_Name = %s AND State_Id = %s", - # (district_name, state_id)) - # existing_district = cursor.fetchone() - cursor.callproc("GetDistrictByNameAndState", (district_name, state_id,)) - for result in cursor.stored_results(): - existing_district = result.fetchone() - - if existing_district: - return json_response(ResponseHandler.already_exists("district"), 409) - else: - return json_response(ResponseHandler.is_available("district"), 200) - - except mysql.connector.Error as e: - print(f"Error checking district: {e}") - return json_response(ResponseHandler.add_failure("district"), 500) - finally: - cursor.close() - connection.close() - - -# this is delete District method by id.. -@app.route('/delete_district/', methods=['GET', 'POST']) +# Delete District +@app.route('/delete_district/', methods=['GET']) @login_required def delete_district(district_id): - connection = config.get_db_connection() + district = District() + district.DeleteDistrict(request=request, id=district_id) - if connection: - cursor = connection.cursor() - try: - # cursor.execute("DELETE FROM districts WHERE District_id = %s", (district_id,)) - cursor.callproc("DeleteDistrict", (district_id,)) - connection.commit() - log_action("Delete District", f"User {current_user.id} Deleted District '{district_id}'") - except mysql.connector.Error as e: - print(f"Error deleting district: {e}") - return json_response(ResponseHandler.delete_failure("district"), 500) - finally: - cursor.close() - connection.close() - - return redirect('/add_district') + if not district.isSuccess: + return district.resultMessage + else: + return redirect(url_for('add_district')) -# this is update District page by id .. +# Edit District @app.route('/edit_district/', methods=['GET', 'POST']) @login_required def edit_district(district_id): - connection = config.get_db_connection() - districtdata = [] - states = [] + district = District() - if connection: - cursor = connection.cursor() - - # Retrieve all states for dropdown - try: - # cursor.execute("SELECT State_ID, State_Name FROM states") - # states = cursor.fetchall() - cursor.callproc("GetAllStates") - for res in cursor.stored_results(): - states = res.fetchall() - - except mysql.connector.Error as e: - print(f"Error fetching states: {e}") - return ResponseHandler.fetch_failure("states"), 500 - - # Retrieve district info - try: - # cursor.execute("SELECT District_Name, State_Id FROM districts WHERE District_id = %s", (district_id,)) - # districtdata = cursor.fetchone() - cursor.callproc("GetDistrictDataByID", (district_id,)) - for rs in cursor.stored_results(): - districtdata = rs.fetchone() - log_action("Edit District", f"User {current_user.id} Edited District '{district_id}'") - except mysql.connector.Error as e: - print(f"Error fetching district data: {e}") - return ResponseHandler.fetch_failure("district"), 500 - - # Handle update - if request.method == 'POST': - district_name = request.form['district_Name'] - state_id = request.form['state_Id'] - - try: - # cursor.execute( "UPDATE districts SET District_Name = %s, State_Id = %s WHERE District_id = %s", - # (district_name, state_id, district_id) ) - - cursor.callproc("UpdateDistrict", (district_id, state_id, district_name,)) - connection.commit() - except mysql.connector.Error as e: - print(f"Error updating district: {e}") - return ResponseHandler.update_failure("district"), 500 - return redirect('/add_district') + if request.method == 'POST': + district.EditDistrict(request=request, id=district_id) + if district.isSuccess: + return redirect(url_for('add_district')) + else: + + flash(district.resultMessage, "error") + + districtdata = district.GetDistrictByID(request=request, id=district_id) + state = State() + states = state.GetAllStates(request=request) + return render_template('edit_district.html', districtdata=districtdata, states=states) + + # GET Request + else: + districtdata = district.GetDistrictByID(request=request, id=district_id) + + if not district.isSuccess: + flash(district.resultMessage, "error") + return redirect(url_for('add_district')) + + + state = State() + states = state.GetAllStates(request=request) + + if districtdata is None: + districtdata = [] + if states is None: + states = [] return render_template('edit_district.html', districtdata=districtdata, states=states) @@ -615,169 +275,92 @@ def edit_district(district_id): @app.route('/add_block', methods=['GET', 'POST']) @login_required def add_block(): + block = Block() + district = District() + + # form submission + if request.method == 'POST': + block.AddBlock(request) + return block.resultMessage + + # Fetch all states connection = config.get_db_connection() - block_data = [] - states = [] + cursor = connection.cursor() + cursor.callproc("GetAllStates") + for rs in cursor.stored_results(): + states = rs.fetchall() - if connection: - cursor = connection.cursor() - try: - # cursor.execute("SELECT State_ID, State_Name FROM states") - # states = cursor.fetchall() - cursor.callproc("GetAllStates") - for res in cursor.stored_results(): - states = res.fetchall() + # Fetch all blocks + block_data = block.GetAllBlocks() - except mysql.connector.Error as e: - print(f"Error fetching states: {e}") - return json_response(ResponseHandler.fetch_failure("states"), 500) - - if request.method == 'POST': - block_name = request.form['block_Name'].strip() - district_id = request.form['district_Id'] - log_action("Add Block", f"User {current_user.id} Added block '{block_name}'") - if not re.match(str_pattern_reg, block_name): - return json_response(ResponseHandler.invalid_name("block"), 400) - - try: - # cursor.execute("SELECT * FROM blocks WHERE Block_Name = %s AND District_id = %s", - # (block_name, district_id)) - # existing_block = cursor.fetchone() - cursor.callproc("GetBlockByNameAndDistrict", (block_name, district_id,)) - for rs in cursor.stored_results(): - existing_block = rs.fetchone() - - if existing_block: - return json_response(ResponseHandler.already_exists("block"), 409) - - cursor.callproc('SaveBlock', (block_name, district_id)) - connection.commit() - - return json_response(ResponseHandler.add_success("block"), 200) - - except mysql.connector.Error as e: - print(f"Error adding block: {e}") - return json_response(ResponseHandler.add_failure("block"), 500) - - # Fetch all blocks to display - try: - - cursor.callproc("GetAllBlocks") - for blocks in cursor.stored_results(): - block_data = blocks.fetchall() - - except mysql.connector.Error as e: - print(f"Error fetching blocks: {e}") - return json_response(ResponseHandler.fetch_failure("blocks"), 500) - finally: - cursor.close() - connection.close() - return render_template('add_block.html', block_data=block_data, states=states) + return render_template('add_block.html', states=states, block_data=block_data) -# check block @app.route('/check_block', methods=['POST']) @login_required def check_block(): - connection = config.get_db_connection() - cursor = connection.cursor() - block_name = request.json.get('block_Name', '').strip() - district_id = request.json.get('district_Id', '') - log_action("Check Block", f"User {current_user.id} Checked block '{block_name}'") - if not re.match(str_pattern_reg, block_name): - return json_response(ResponseHandler.invalid_name("block"), 400) - - - cursor.callproc("GetBlockByNameAndDistrict", (block_name, district_id)) - for rs in cursor.stored_results(): - existing_block = rs.fetchone() - - if existing_block: - return json_response(ResponseHandler.already_exists("block"), 409) - - return json_response(ResponseHandler.is_available("block"), 200) - - + block = Block() + return block.CheckBlock(request) @app.route('/edit_block/', methods=['GET', 'POST']) @login_required def edit_block(block_id): + block = Block() + + if request.method == 'POST': + return block.EditBlock(request, block_id) + + # Load all states connection = config.get_db_connection() - block_data = [] - states = [] - districts = [] + cursor = connection.cursor() + cursor.callproc("GetAllStates") + for rs in cursor.stored_results(): + states = rs.fetchall() - if connection: - cursor = connection.cursor() - # Retrieve all states - try: - - cursor.callproc("GetAllStates") - for rs in cursor.stored_results(): - states = rs.fetchall() - except mysql.connector.Error as e: - print(f"Error fetching states: {e}") - return "Failed to fetch states", 500 + # Load all districts + cursor.callproc("GetAllDistrictsData") + for rs in cursor.stored_results(): + districts = rs.fetchall() - # Retrieve block data - try: - - cursor.callproc("GetBlockDataByID", (block_id,)) - for rs in cursor.stored_results(): - block_data = rs.fetchone() - log_action("Edit Block", f"User {current_user.id} Edited block '{block_id}'") - except mysql.connector.Error as e: - print(f"Error fetching block data: {e}") - return "Failed to fetch block data", 500 + block_data = block.GetBlockByID(block_id) - # Handle POST request - if request.method == 'POST': - block_name = request.form['block_Name'] - district_id = request.form['district_Id'] - try: - - cursor.callproc("UpdateBlockById", (block_name, district_id, block_id,)) - connection.commit() - flash("Block updated successfully!", "success") - return redirect(url_for('add_block', block_id=block_id)) - except mysql.connector.Error as e: - print(f"Error updating blocks: {e}") - return "Failed to update blocks", 500 + return render_template('edit_block.html', block_data=block_data, states=states, districts=districts) - # Retrieve districts for the dropdown - try: - - cursor.callproc("GetAllDistrictsData") - for rs in cursor.stored_results(): - districts = rs.fetchall() - except mysql.connector.Error as e: - print(f"Error fetching districts: {e}") - return "Failed to fetch districts", 500 - return render_template('edit_block.html', block_data=block_data, states=states, districts=districts) - -# delete block by id -@app.route('/delete_block/', methods=['GET', 'POST']) +@app.route('/delete_block/') @login_required def delete_block(block_id): + block = Block() + block.DeleteBlock(block_id) + return redirect(url_for('add_block')) + + +# get block by district id +@app.route('/get_blocks/', methods=['GET']) +@login_required +def get_blocks(district_id): connection = config.get_db_connection() + cursor = connection.cursor() + blocks = [] + + try: + # cursor.execute("SELECT Block_Id, Block_Name FROM blocks WHERE District_id = %s", (district_id,)) + # blocks = cursor.fetchall() + cursor.callproc("GetBlocksByDistrict", (district_id,)) + for rs in cursor.stored_results(): + blocks = rs.fetchall() + # log_action("Get blocks", f"User {current_user.id} Get Blocks '{district_id}'") + except mysql.connector.Error as e: + print(f"Error fetching blocks: {e}") + return HtmlHelper.json_response({"error": "Failed to fetch blocks"}, 500) + finally: + cursor.close() + connection.close() + + return jsonify({"blocks": [{"Block_Id": block[0], "Block_Name": block[1]} for block in blocks]}) - if connection: - cursor = connection.cursor() - try: - # cursor.execute("DELETE FROM blocks WHERE Block_Id = %s", (block_id,)) - cursor.callproc("DeleteBlock", (block_id,)) - log_action("Delete Block", f"User {current_user.id} Deleted block '{block_id}'") - connection.commit() - except mysql.connector.Error as e: - print(f"Error deleting block: {e}") - return json_response(ResponseHandler.add_failure("block"), 500) - finally: - cursor.close() - connection.close() - return redirect('/add_block') # this is get district all data by using state id .. @@ -790,15 +373,16 @@ def get_districts(state_id): if connection: cursor = connection.cursor() try: - + # cursor.execute("SELECT District_id, District_Name FROM districts WHERE State_Id = %s", (state_id,)) + # districts = cursor.fetchall() cursor.callproc("GetDistrictsByStateId", (state_id,)) for dis in cursor.stored_results(): districts = dis.fetchall() - log_action("Get District", f"User {current_user.id} Get District '{state_id}'") + LogHelper.log_action("Get District", f"User {current_user.id} Get District '{state_id}'") except mysql.connector.Error as e: print(f"Error fetching districts: {e}") - return json_response(ResponseHandler.fetch_failure("districts"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("districts"), 500) finally: cursor.close() connection.close() @@ -815,181 +399,78 @@ def get_districts(state_id): @app.route('/add_village', methods=['GET', 'POST']) @login_required def add_village(): - connection = config.get_db_connection() - cursor = connection.cursor() - states = [] - villages = [] + village = Village() - try: - + if request.method == 'POST': + village.AddVillage(request=request) + return village.resultMessage - cursor.callproc("GetAllStates") - for res in cursor.stored_results(): - states = res.fetchall() - - cursor.callproc("GetAllVillages") - for result in cursor.stored_results(): - villages = result.fetchall() + state = State() # Use the State class to get states + states = state.GetAllStates(request=request) - if request.method == 'POST': - block_id = request.form.get('block_Id') - village_name = request.form.get('Village_Name', '').strip() - log_action("Add Villages", f"User {current_user.id} Added Villages '{village_name}'") - if not block_id: - return json_response(ResponseHandler.add_failure("block"), 400) - - if not re.match(str_pattern_reg, village_name): - return json_response(ResponseHandler.invalid_name("village"), 400) - - - cursor.callproc("GetVillageByNameAndBlock", (village_name, block_id,)) - for rs in cursor.stored_results(): - existing_village = rs.fetchone() - if existing_village: - return json_response(ResponseHandler.already_exists("village"), 409) - - # Insert new village - cursor.callproc('SaveVillage', (village_name, block_id)) - connection.commit() - - return json_response(ResponseHandler.add_success("village"), 200) - - except mysql.connector.Error as e: - print(f"Database Error: {e}") - return json_response(ResponseHandler.add_failure("village"), 500) - finally: - cursor.close() - connection.close() + villages = village.GetAllVillages(request=request) return render_template('add_village.html', states=states, villages=villages) - -# get block by district id -@app.route('/get_blocks/', methods=['GET']) -@login_required -def get_blocks(district_id): - connection = config.get_db_connection() - cursor = connection.cursor() - blocks = [] - - try: - - cursor.callproc("GetBlocksByDistrict", (district_id,)) - for rs in cursor.stored_results(): - blocks = rs.fetchall() - log_action("Get blocks", f"User {current_user.id} Get Blocks '{district_id}'") - except mysql.connector.Error as e: - print(f"Error fetching blocks: {e}") - return json_response({"error": "Failed to fetch blocks"}, 500) - finally: - cursor.close() - connection.close() - - return jsonify({"blocks": [{"Block_Id": block[0], "Block_Name": block[1]} for block in blocks]}) - - -# check village @app.route('/check_village', methods=['POST']) @login_required def check_village(): - connection = config.get_db_connection() - cursor = connection.cursor() + village = Village() + return village.CheckVillage(request=request) - block_id = request.form.get('block_Id') - village_name = request.form.get('Village_Name', '').strip() - log_action("Check villages", f"User {current_user.id} Check villages '{village_name}'") - # Validate village name - if not re.match(str_pattern_reg, village_name): - return json_response(ResponseHandler.invalid_name("village"), 400) - if not block_id or not village_name: - return json_response({'status': 'error', 'message': 'Block and Village Name are required!'}, 400) +# Delete Village +@app.route('/delete_village/', methods=['GET']) +@login_required +def delete_village(village_id): + village = Village() + village.DeleteVillage(request=request, village_id=village_id) - # cursor.execute("SELECT * FROM villages WHERE Village_Name = %s AND Block_Id = %s", (village_name, block_id)) - # existing_village = cursor.fetchone() - cursor.callproc("GetVillageByNameAndBlocks", (village_name, block_id)) - for rs in cursor.stored_results(): - existing_village = rs.fetchone() - - cursor.close() - connection.close() - - if existing_village: - return json_response(ResponseHandler.already_exists("village"), 409) + if not village.isSuccess: + flash(village.resultMessage, "error") + return redirect(url_for('add_village')) else: - return json_response(ResponseHandler.is_available("village"), 200) - - + + return redirect(url_for('add_village')) +# Edit Village @app.route('/edit_village/', methods=['GET', 'POST']) @login_required def edit_village(village_id): - connection = config.get_db_connection() - village_data = None - blocks = [] + village = Village() - try: - cursor = connection.cursor() - - cursor.callproc("GetVillageDetailsById", (village_id,)) - for rs in cursor.stored_results(): - village_data = rs.fetchone() - - cursor.callproc('GetAllBlocks') - for result in cursor.stored_results(): - blocks = result.fetchall() + if request.method == 'POST': + village.EditVillage(request=request, village_id=village_id) + if village.isSuccess: + flash(village.resultMessage, "success") + return redirect(url_for('add_village')) + else: - if request.method == 'POST': - village_name = request.form['Village_Name'] - block_id = request.form['block_Id'] - log_action("Edit villages", f"User {current_user.id} Edit villages '{village_name}'") - if not re.match("^[A-Za-z ]+$", village_name): - flash("Invalid village name! Only letters and spaces allowed.", "error") - return redirect(url_for('edit_village', village_id=village_id)) + flash(village.resultMessage, "error") - cursor.execute("UPDATE villages SET Village_Name = %s, Block_Id = %s WHERE Village_Id = %s", - (village_name, block_id, village_id)) - connection.commit() - flash("Village updated successfully!", "success") - return redirect(url_for('edit_village', village_id=village_id)) - - except mysql.connector.Error as e: - print(f"Error: {e}") - return "Failed to process request", 500 - finally: - if cursor: - cursor.close() - if connection: - connection.close() - - return render_template('edit_village.html', village_data=village_data, blocks=blocks) + village_data = village.GetVillageByID(request=request, id=village_id) + blocks = village.GetAllBlocks(request=request) + return render_template('edit_village.html', village_data=village_data, blocks=blocks) -# delete village -@app.route('/delete_village/', methods=['GET', 'POST']) -@login_required -def delete_village(village_id): - connection = config.get_db_connection() - cursor = connection.cursor() + else: + village_data = village.GetVillageByID(request=request, id=village_id) - try: - # cursor.execute("DELETE FROM villages WHERE Village_Id = %s", (village_id,)) - cursor.callproc("DeleteVillage", (village_id,)) - log_action("Delete villages", f"User {current_user.id} Deletedvillages '{village_id}'") - connection.commit() - # return json_response(ResponseHandler.delete_success("village"), 200) - except mysql.connector.Error as e: - print(f"Error: {e}") - return json_response(ResponseHandler.add_failure("village"), 500) - finally: - if cursor: - cursor.close() - if connection: - connection.close() + if not village.isSuccess: + flash(village.resultMessage, "error") + return redirect(url_for('add_village')) + + blocks = village.GetAllBlocks(request=request) + + if village_data is None: + village_data = [] # Ensure it's iterable in template + if blocks is None: + blocks = [] + + return render_template('edit_village.html', village_data=village_data, blocks=blocks) - return redirect(url_for('add_village')) # ---- end Village controller --------------------- @@ -1012,7 +493,9 @@ def add_invoice(): print("village name", village_name) - + # Query the database to get the corresponding Village_Id based on the village name + # cursor.execute("SELECT Village_Id FROM villages WHERE Village_Name = %s", (village_name,)) + # village_result = cursor.fetchone() cursor.callproc("GetVillageIdByName", (village_name,)) for rs in cursor.stored_results(): village_result = rs.fetchone() @@ -1051,19 +534,40 @@ def add_invoice(): final_amount = request.form.get('final_amount') final_amount=float(final_amount) if final_amount else 0.0 - + # insert_invoice_query = ''' + # INSERT INTO invoice ( + # PMC_No, Village_Id, Work_Type, Invoice_Details, Invoice_Date, Invoice_No, + # Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount, + # SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount + # ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + # ''' + # invoice_values = ( + # pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, + # basic_amount, debit_amount, after_debit_amount, amount, gst_amount, tds_amount, + # sd_amount, on_commission, hydro_testing, gst_sd_amount, final_amount + # ) + # cursor.execute(insert_invoice_query, invoice_values) + # connection.commit() + # invoice_id = cursor.lastrowid cursor.callproc('InsertInvoice', [ pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, basic_amount, debit_amount, after_debit_amount, amount, gst_amount, tds_amount, sd_amount, on_commission, hydro_testing, gst_sd_amount, final_amount]) - log_action("Add invoice", f"User {current_user.id} Added invoice '{ pmc_no}'") + LogHelper.log_action("Add invoice", f"User {current_user.id} Added invoice '{ pmc_no}'") for result in cursor.stored_results(): invoice_id = result.fetchone()['invoice_id'] connection.commit() print("This is the invocie id from the invoice table ", invoice_id) - + # Insert into assign_subcontractors table + # subcontractor_id = request.form.get('subcontractor_id') + # insert_assign_query = ''' + # INSERT INTO assign_subcontractors (PMC_no, Contractor_Id, Village_Id) + # VALUES (%s, %s, %s) + # ''' + # cursor.execute(insert_assign_query, (pmc_no, subcontractor_id, village_id)) + # connection.commit() subcontractor_id = request.form.get('subcontractor_id') cursor.callproc('AssignSubcontractor', [pmc_no, subcontractor_id, village_id]) connection.commit() @@ -1083,7 +587,14 @@ def add_invoice(): if not hold_type_result: return jsonify({"status": "error", "message": f"Invalid Hold Type: {hold_type}"}), 400 hold_type_id = hold_type_result['hold_type_id'] - + # insert_hold_query = ''' + # INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount) + # VALUES (%s, %s, %s, %s) + # ''' + # cursor.execute(insert_hold_query, (subcontractor_id, invoice_id, hold_type_id, hold_amount)) + # hold_count += 1 + + # connection.commit() cursor.callproc('InsertInvoiceSubcontractorHold', [ subcontractor_id, invoice_id, hold_type_id, hold_amount ]) @@ -1104,7 +615,8 @@ def add_invoice(): # GET request: fetch and display all invoices (all fields) along with the form try: cursor = connection.cursor(dictionary=True) - + # cursor.execute("SELECT * FROM view_invoice_details") + # invoices = cursor.fetchall() cursor.callproc('GetAllInvoiceDetails') for result in cursor.stored_results(): invoices = result.fetchall() @@ -1130,12 +642,16 @@ def add_invoice(): def search_subcontractor(): connection = config.get_db_connection() if not connection: - return json_response(ResponseHandler.fetch_failure("database connection"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("database connection"), 500) sub_query = request.form.get("query") try: cursor = connection.cursor(dictionary=True) - + # cursor.execute( + # "SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name LIKE %s", + # (f"%{sub_query}%",) + # ) + # results = cursor.fetchall() cursor.callproc('SearchContractorsByName', [sub_query]) for result in cursor.stored_results(): results = result.fetchall() @@ -1152,7 +668,7 @@ def search_subcontractor(): return output except mysql.connector.Error as e: - return json_response(ResponseHandler.fetch_failure(f"Search failed: {str(e)}"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure(f"Search failed: {str(e)}"), 500) finally: cursor.close() @@ -1166,12 +682,13 @@ def get_hold_types(): connection = config.get_db_connection() try: cursor = connection.cursor(dictionary=True) - + # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types") + # hold_types = cursor.fetchall() cursor.callproc("GetAllHoldTypes") for hold in cursor.stored_results(): hold_types = hold.fetchall() - log_action("Get hold type", f"User {current_user.id} Get hold type'{ hold_types}'") + LogHelper.log_action("Get hold type", f"User {current_user.id} Get hold type'{ hold_types}'") return jsonify(hold_types) except mysql.connector.Error as e: return ResponseHandler.fetch_failure({str(e)}), 500 @@ -1199,7 +716,8 @@ def edit_invoice(invoice_id): subcontractor_id = int(subcontractor_id) if subcontractor_id else None village_name = request.form.get('village') - + # cursor.execute("SELECT Village_Id FROM villages WHERE Village_Name = %s", (village_name,)) + # village_result = cursor.fetchone() cursor.callproc("GetVillageIdByName", (village_name,)) for rs in cursor.stored_results(): village_result = rs.fetchone() @@ -1212,7 +730,7 @@ def edit_invoice(invoice_id): invoice_details = request.form.get('invoice_details') invoice_date = request.form.get('invoice_date') invoice_no = request.form.get('invoice_no') - log_action("Edit invoice", f"User {current_user.id} Edit invoice'{ invoice_id}'") + LogHelper.log_action("Edit invoice", f"User {current_user.id} Edit invoice'{ invoice_id}'") # Convert numeric fields properly numeric_fields = { "basic_amount": request.form.get('basic_amount'), @@ -1229,7 +747,21 @@ def edit_invoice(invoice_id): } numeric_fields = {k: float(v) if v else 0 for k, v in numeric_fields.items()} - + # # Update invoice + # update_invoice_query = ''' + # UPDATE invoice + # SET PMC_No=%s, Village_Id=%s, Work_Type=%s, Invoice_Details=%s, Invoice_Date=%s, + # Invoice_No=%s, Basic_Amount=%s, Debit_Amount=%s, After_Debit_Amount=%s, + # Amount=%s, GST_Amount=%s, TDS_Amount=%s, SD_Amount=%s, On_Commission=%s, + # Hydro_Testing=%s, GST_SD_Amount=%s, Final_Amount=%s + # WHERE Invoice_Id=%s + # ''' + # invoice_values = ( + # pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, + # *numeric_fields.values(), invoice_id + # ) + # cursor.execute(update_invoice_query, invoice_values) + # connection.commit() cursor.callproc('UpdateInvoice', [ pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, *numeric_fields.values(), invoice_id @@ -1244,12 +776,19 @@ def edit_invoice(invoice_id): if not hold_type: continue # skip empty hold types - + # Get or insert hold type + # cursor.execute("SELECT hold_type_id FROM hold_types WHERE hold_type = %s", (hold_type,)) + # hold_type_result = cursor.fetchone() cursor.callproc('GetHoldTypeIdByName', [hold_type]) for result in cursor.stored_results(): hold_type_result = result.fetchone() - + # if not hold_type_result: + # cursor.execute("INSERT INTO hold_types (hold_type) VALUES (%s)", (hold_type,)) + # connection.commit() + # hold_type_id = cursor.lastrowid + # else: + # hold_type_id = hold_type_result['hold_type_id'] if not hold_type_result: # Call stored procedure to insert and return new ID @@ -1265,18 +804,30 @@ def edit_invoice(invoice_id): hold_amount = float(hold_amount) if hold_amount else 0 - + # Check if join exists + # cursor.execute(""" + # SELECT join_id FROM invoice_subcontractor_hold_join + # WHERE Invoice_Id = %s AND Contractor_Id = %s AND hold_type_id = %s + # """, (invoice_id, subcontractor_id, hold_type_id)) + # join_result = cursor.fetchone() cursor.callproc('GetHoldJoinId', [invoice_id, subcontractor_id, hold_type_id]) for result in cursor.stored_results(): join_result = result.fetchone() if join_result: - + # cursor.execute(""" + # UPDATE invoice_subcontractor_hold_join + # SET hold_amount = %s + # WHERE join_id = %s + # """, (hold_amount, join_result['join_id'])) cursor.callproc('UpdateHoldAmountByJoinId', [hold_amount, join_result['join_id']]) connection.commit() else: - + # cursor.execute(""" + # INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount) + # VALUES (%s, %s, %s, %s) + # """, (subcontractor_id, invoice_id, hold_type_id, hold_amount)) cursor.callproc('InsertInvoiceSubcontractorHold', [ subcontractor_id, invoice_id, hold_type_id, hold_amount ]) @@ -1295,7 +846,16 @@ def edit_invoice(invoice_id): # ------------------ GET Request ------------------ try: - + # Fetch invoice data + # cursor.execute( + # """SELECT i.*, s.Contractor_Name, v.Village_Name + # FROM invoice i + # LEFT JOIN assign_subcontractors a ON i.PMC_No = a.PMC_no AND i.Village_Id = a.Village_Id + # LEFT JOIN subcontractors s ON a.Contractor_Id = s.Contractor_Id + # LEFT JOIN villages v ON i.Village_Id = v.Village_Id + # WHERE i.Invoice_Id = %s""", (invoice_id,) + # ) + # invoice = cursor.fetchone() cursor.callproc('GetInvoiceDetailsById', [invoice_id]) for result in cursor.stored_results(): invoice = result.fetchone() @@ -1307,7 +867,15 @@ def edit_invoice(invoice_id): while cursor.nextset(): pass - + # Fetch hold amounts + # cursor.execute( + # """SELECT h.hold_type, ihj.hold_amount + # FROM invoice_subcontractor_hold_join ihj + # JOIN hold_types h ON ihj.hold_type_id = h.hold_type_id + # WHERE ihj.Invoice_Id = %s""", (invoice_id,) + # ) + # hold_amounts = cursor.fetchall() + # invoice["hold_amounts"] = hold_amounts cursor.callproc('GetHoldAmountsByInvoiceId', [invoice_id]) for result in cursor.stored_results(): hold_amounts = result.fetchall() @@ -1330,33 +898,86 @@ def edit_invoice(invoice_id): def delete_invoice(invoice_id): connection = config.get_db_connection() if not connection: - return json_response(ResponseHandler.fetch_failure("invoice"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("invoice"), 500) try: cursor = connection.cursor() # cursor.execute("DELETE FROM invoice WHERE Invoice_Id = %s", (invoice_id,)) cursor.callproc("DeleteInvoice", (invoice_id,)) - log_action("Delete invoice", f"User {current_user.id} Delete invoice'{ invoice_id}'") + LogHelper.log_action("Delete invoice", f"User {current_user.id} Delete invoice'{ invoice_id}'") connection.commit() # Check if the invoice was actually deleted if cursor.rowcount == 0: - return json_response(ResponseHandler.fetch_failure("invoice"), 404) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("invoice"), 404) return redirect(url_for('add_invoice')) except mysql.connector.Error as e: print("Error deleting invoice:", e) - return json_response(ResponseHandler.delete_failure("invoice"), 500) + return HtmlHelper.json_response(ResponseHandler.delete_failure("invoice"), 500) finally: cursor.close() connection.close() +# ---------- end Invoice controller ------------------ +# ----------------------------- Payment controller ------------------------------------------ +# this is Payment Page to add data +# @app.route('/add_payment', methods=['GET', 'POST']) +# def add_payment(): +# connection = config.get_db_connection() +# payments = [] # List to hold payment history +# +# if not connection: +# return HtmlHelper.json_response(ResponseHandler.fetch_failure("payment"), 500) +# +# try: +# cursor = connection.cursor() +# +# # Retrieve payment history +# # cursor.execute( +# # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment" +# # ) +# # payments = cursor.fetchall() +# cursor.callproc("GetAllPayments") +# for result in cursor.stored_results(): +# payments = result.fetchall() +# +# except mysql.connector.Error as e: +# print(f"Error fetching payment history: {e}") +# return HtmlHelper.json_response(ResponseHandler.fetch_failure("payment"), 500) +# finally: +# cursor.close() +# +# if request.method == 'POST': +# pmc_no = request.form['PMC_No'] +# invoice_no = request.form['invoice_No'] +# amount = request.form['Payment_Amount'] +# tds_amount = request.form['TDS_Payment_Amount'] +# total_amount = request.form['total_amount'] +# utr = request.form['utr'] +# +# try: +# cursor = connection.cursor() +# cursor.callproc('SavePayment', ( +# pmc_no, invoice_no, amount, tds_amount, total_amount, utr +# )) +# connection.commit() +# return redirect(url_for('add_payment')) # Redirect to add_payment page to reload the form +# except mysql.connector.Error as e: +# print(f"Error inserting payment: {e}") +# return HtmlHelper.json_response(ResponseHandler.add_failure("payment"), 500) +# finally: +# cursor.close() +# connection.close() +# +# return render_template('add_payment.html', payments=payments) + @app.route('/add_payment', methods=['GET', 'POST']) @login_required def add_payment(): @@ -1367,7 +988,10 @@ def add_payment(): cursor = connection.cursor() try: - + # cursor.execute( + # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment" + # ) + # payments = cursor.fetchall() cursor.callproc('GetAllPayments') for result in cursor.stored_results(): payments = result.fetchall() @@ -1385,10 +1009,13 @@ def add_payment(): tds_amount = request.form['TDS_Payment_Amount'] total_amount = request.form['total_amount'] utr = request.form['utr'] - log_action("Add Payment", f"User {current_user.id} Add Payment'{ pmc_no}'") + LogHelper.log_action("Add Payment", f"User {current_user.id} Add Payment'{ pmc_no}'") try: cursor = connection.cursor() - + # cursor.execute('''INSERT INTO payment (PMC_No, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) + # VALUES (%s, %s, %s, %s, %s, %s)''', + # (pmc_no, invoice_no, amount, tds_amount, total_amount, utr)) + # connection.commit() cursor.callproc('InsertPayments', [ pmc_no, invoice_no, amount, tds_amount, total_amount, utr ]) @@ -1412,7 +1039,15 @@ def get_pmc_nos_by_subcontractor(subcontractorId): connection = config.get_db_connection() cur = connection.cursor() print(subcontractorId) - + # query = """ + # SELECT DISTINCT i.PMC_No + # FROM invoice i + # JOIN assign_subcontractors a ON i.PMC_No = a.PMC_no + # JOIN subcontractors s ON a.Contractor_Id = s.Contractor_Id + # WHERE s.Contractor_Id=%s; + # """ + # cur.execute(query, (subcontractorId,)) + # results = cur.fetchall() cur.callproc('GetDistinctPMCNoByContractorId', [subcontractorId]) for result in cur.stored_results(): results = result.fetchall() @@ -1431,12 +1066,17 @@ def edit_payment(payment_id): payment_data = {} # To hold the payment data for the given ID if not connection: - return json_response(ResponseHandler.fetch_failure("payment"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("payment"), 500) try: cursor = connection.cursor() - + # Fetch the existing payment data for the given payment_id + # cursor.execute( + # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment WHERE Payment_Id = %s", + # (payment_id,) + # ) + # payment_data = cursor.fetchone() cursor.callproc("GetPaymentById", (payment_id,)) for result in cursor.stored_results(): @@ -1450,9 +1090,11 @@ def edit_payment(payment_id): tds_amount = request.form['TDS_Payment_Amount'] total_amount = request.form['total_amount'] utr = request.form['utr'] - log_action("Edit Payment", f"User {current_user.id} Edit Payment'{ pmc_no}'") + LogHelper.log_action("Edit Payment", f"User {current_user.id} Edit Payment'{ pmc_no}'") try: - + # cursor.execute('''UPDATE payment SET PMC_No=%s, Invoice_No=%s, Payment_Amount=%s, TDS_Payment_Amount=%s, + # Total_Amount=%s, UTR=%s WHERE Payment_Id=%s''', + # (pmc_no, invoice_no, amount, tds_amount, total_amount, utr, payment_id)) cursor.callproc("UpdatePayment", (payment_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr,)) @@ -1461,11 +1103,11 @@ def edit_payment(payment_id): return redirect(url_for('add_payment')) # Redirect to add_payment page to view the updated list except mysql.connector.Error as e: print(f"Error updating payment: {e}") - return json_response(ResponseHandler.update_failure("payment"), 500) + return HtmlHelper.json_response(ResponseHandler.update_failure("payment"), 500) except mysql.connector.Error as e: print(f"Error fetching payment data: {e}") - return json_response(ResponseHandler.fetch_failure("payment"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("payment"), 500) finally: cursor.close() connection.close() @@ -1479,21 +1121,22 @@ def edit_payment(payment_id): def delete_payment(payment_id): connection = config.get_db_connection() if not connection: - return json_response(ResponseHandler.fetch_failure("payment"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("payment"), 500) try: cursor = connection.cursor() - + # cursor.execute("DELETE FROM payment WHERE Payment_Id = %s", (payment_id,)) + cursor.callproc("DeletePayment", (payment_id,)) - log_action("Delete Payment", f"User {current_user.id} Delete Payment'{ payment_id}'") + LogHelper.log_action("Delete Payment", f"User {current_user.id} Delete Payment'{ payment_id}'") connection.commit() # Check if any rows were deleted if cursor.rowcount == 0: - return json_response(ResponseHandler.fetch_failure("payment"), 404) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("payment"), 404) return redirect(url_for('add_payment')) # Redirect back to the add_payment page except mysql.connector.Error as e: print(f"Error deleting payment: {e}") - return json_response(ResponseHandler.delete_failure("payment"), 500) + return HtmlHelper.json_response(ResponseHandler.delete_failure("payment"), 500) finally: cursor.close() connection.close() @@ -1510,7 +1153,7 @@ def add_gst_release(): invoices = [] # List to hold invoices for the dropdown if not connection: - return json_response(ResponseHandler.fetch_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("GST Release"), 500) try: cursor = connection.cursor() @@ -1519,7 +1162,9 @@ def add_gst_release(): cursor.execute("SELECT GST_Release_Id, PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR FROM gst_release") gst_releases = cursor.fetchall() - + # cursor.callproc("GetAllGSTReleases") + # for result in cursor.stored_results(): + # gst_releases = result.fetchall() if request.method == 'POST': pmc_no = request.form['PMC_No'] @@ -1529,8 +1174,11 @@ def add_gst_release(): total_amount = request.form['total_amount'] utr = request.form['utr'] contractor_id = request.form['subcontractor_id'] - log_action("Add gst_release", f"User {current_user.id} Add gst_release'{ pmc_no}'") - + LogHelper.log_action("Add gst_release", f"User {current_user.id} Add gst_release'{ pmc_no}'") + # cursor.callproc('SaveGSTRelease', ( + # pmc_no, invoice_no, basic_amount, final_amount,total_amount, utr + # )) + # connection.commit() cursor.execute(""" INSERT INTO gst_release (PMC_No, invoice_no, @@ -1555,7 +1203,7 @@ def add_gst_release(): except mysql.connector.Error as e: print(f"Error: {e}") - return json_response(ResponseHandler.add_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.add_failure("GST Release"), 500) finally: cursor.close() @@ -1573,7 +1221,7 @@ def edit_gst_release(gst_release_id): invoices = [] # List to hold invoices for the dropdown if not connection: - return json_response(ResponseHandler.fetch_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("GST Release"), 500) try: cursor = connection.cursor() @@ -1585,6 +1233,9 @@ def edit_gst_release(gst_release_id): ) gst_release_data = cursor.fetchone() + # cursor.callproc("GetGSTReleaseById", (gst_release_id,)) + # for result in cursor.stored_results(): + # gst_release_data = result.fetchone() if request.method == 'POST': pmc_no = request.form['PMC_No'] @@ -1593,7 +1244,7 @@ def edit_gst_release(gst_release_id): final_amount = request.form['final_amount'] total_amount = request.form['total_amount'] utr = request.form['utr'] - log_action("Edit gst_release", f"User {current_user.id} Edit gst_release'{ pmc_no}'") + LogHelper.log_action("Edit gst_release", f"User {current_user.id} Edit gst_release'{ pmc_no}'") try: cursor.execute(""" UPDATE gst_release @@ -1614,17 +1265,19 @@ def edit_gst_release(gst_release_id): gst_release_id )) - + # cursor.callproc("UpdateGSTRelease", (gst_release_id, pmc_id, invoice_no, basic_amount, final_amount)) + # + # connection.commit() return redirect(url_for('add_gst_release')) # Redirect to the page to view the updated list except mysql.connector.Error as e: print(f"Error updating GST Release: {e}") - return json_response(ResponseHandler.update_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.update_failure("GST Release"), 500) except mysql.connector.Error as e: print(f"Error fetching GST Release data: {e}") - return json_response(ResponseHandler.fetch_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("GST Release"), 500) finally: cursor.close() @@ -1639,20 +1292,20 @@ def edit_gst_release(gst_release_id): def delete_gst_release(gst_release_id): connection = config.get_db_connection() if not connection: - return json_response(ResponseHandler.fetch_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("GST Release"), 500) try: cursor = connection.cursor() - + # cursor.execute("DELETE FROM gst_release WHERE GST_Release_Id = %s", (gst_release_id,)) cursor.callproc("DeleteGSTRelease", (gst_release_id,)) - log_action("delete gst_release", f"User {current_user.id} delete gst_release'{ gst_release_id}'") + LogHelper.log_action("delete gst_release", f"User {current_user.id} delete gst_release'{ gst_release_id}'") connection.commit() # Check if any rows were deleted if cursor.rowcount == 0: - return json_response(ResponseHandler.fetch_failure("GST Release"), 404) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("GST Release"), 404) return redirect(url_for('add_gst_release')) # Redirect to the add_gst_release page except mysql.connector.Error as e: print(f"Error deleting GST Release: {e}") - return json_response(ResponseHandler.delete_failure("GST Release"), 500) + return HtmlHelper.json_response(ResponseHandler.delete_failure("GST Release"), 500) finally: cursor.close() connection.close() @@ -1668,21 +1321,23 @@ def subcontract(): subcontractor = [] if not connection: - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) try: cursor = connection.cursor() if request.method == 'GET': try: - + # cursor.execute('SELECT * FROM subcontractors;') + # subcontractor = cursor.fetchall() # Fetch the current subcontractor list + # connection.commit() cursor.callproc('GetAllSubcontractors') for result in cursor.stored_results(): subcontractor = result.fetchall() except Error as e: print(f"Error fetching data: {e}") - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) if request.method == 'POST': contractor_data = { @@ -1711,7 +1366,9 @@ def subcontract(): )) connection.commit() - + # Re-fetch subcontractors after inserting the new one + # cursor.execute('SELECT * FROM subcontractors') + # subcontractor = cursor.fetchall() cursor.callproc('GetAllSubcontractors') for result in cursor.stored_results(): subcontractor = result.fetchall() @@ -1719,11 +1376,11 @@ def subcontract(): except Error as e: print(f"Error inserting data: {e}") - return json_response(ResponseHandler.add_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.add_failure("Subcontractor"), 500) except Error as e: print(f"Error handling subcontractor data: {e}") - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) finally: cursor.close() @@ -1739,20 +1396,22 @@ def edit_subcontractor(id): connection = config.get_db_connection() if not connection: - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) try: cursor = connection.cursor() subcontractor = None - + # Fetch existing subcontractor data by ID + # cursor.execute('SELECT * FROM subcontractors WHERE Contractor_Id = %s', (id,)) + # subcontractor = cursor.fetchone() cursor.callproc("GetSubcontractorById", (id,)) for contractors in cursor.stored_results(): subcontractor = contractors.fetchone() if not subcontractor: - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 404) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 404) if request.method == 'POST': updated_data = { @@ -1767,9 +1426,19 @@ def edit_subcontractor(id): 'Contractor_password': request.form['Contractor_password'], 'id': id } - log_action("Edit Subcontractor", f"User {current_user.id}Edit Subcontractor'{ id}'") + LogHelper.log_action("Edit Subcontractor", f"User {current_user.id}Edit Subcontractor'{ id}'") try: - + # cursor.execute("""UPDATE subcontractors SET + # Contractor_Name=%(Contractor_Name)s, + # Address=%(Address)s, + # Mobile_No=%(Mobile_No)s, + # PAN_No=%(PAN_No)s, + # Email=%(Email)s, + # Gender=%(Gender)s, + # GST_Registration_Type=%(GST_Registration_Type)s, + # GST_No=%(GST_No)s, + # Contractor_password=%(Contractor_password)s + # WHERE Contractor_Id=%(id)s""", updated_data) cursor.callproc("UpdateSubcontractor", ( id, @@ -1788,11 +1457,11 @@ def edit_subcontractor(id): except Error as e: print(f"Error updating subcontractor: {e}") - return json_response(ResponseHandler.update_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.update_failure("Subcontractor"), 500) except Error as e: print(f"Error fetching subcontractor data: {e}") - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) finally: cursor.close() @@ -1801,14 +1470,41 @@ def edit_subcontractor(id): return render_template('edit_subcontractor.html', subcontractor=subcontractor) +# delete Sub-Contractor methods by id .. +# @app.route('/deleteSubContractor/', methods=['GET', 'POST']) +# def deleteSubContractor(id): +# connection = config.get_db_connection() +# if not connection: +# return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + +# try: +# cursor = connection.cursor() + +# # cursor.execute("DELETE FROM subcontractors WHERE Contractor_Id = %s", (id,)) +# cursor.callproc("DeleteSubcontractor", (id,)) +# connection.commit() + +# # Check if any row was deleted (subcontractor found) +# if cursor.rowcount == 0: +# return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 404) + +# except Error as e: +# print(f"Error deleting subcontractor: {e}") +# return HtmlHelper.json_response(ResponseHandler.delete_failure("Subcontractor"), 500) + +# finally: +# cursor.close() +# connection.close() + +# return redirect(url_for('subcontract')) @app.route('/deleteSubContractor/', methods=['GET', 'POST']) @login_required def deleteSubContractor(id): connection = config.get_db_connection() if not connection: - return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) try: cursor = connection.cursor() @@ -1816,7 +1512,7 @@ def deleteSubContractor(id): # Optional: check if subcontractor exists before attempting delete cursor.execute("SELECT 1 FROM subcontractors WHERE Contractor_Id = %s", (id,)) if cursor.fetchone() is None: - return json_response(ResponseHandler.fetch_failure("Subcontractor not found"), 404) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor not found"), 404) # Call stored procedure to delete subcontractor and related records cursor.callproc("DeleteSubcontractor", (id,)) @@ -1827,13 +1523,13 @@ def deleteSubContractor(id): for result in cursor.stored_results(): row = result.fetchone() affected_rows = row[0] if row else 0 - log_action("Delete Subcontractor", f"User {current_user.id}Delete Subcontractor'{ id}'") + LogHelper.log_action("Delete Subcontractor", f"User {current_user.id}Delete Subcontractor'{ id}'") if affected_rows == 0: - return json_response(ResponseHandler.fetch_failure("Subcontractor not deleted"), 404) + return HtmlHelper.json_response(ResponseHandler.fetch_failure("Subcontractor not deleted"), 404) except Error as e: print(f"Error deleting subcontractor: {e}") - return json_response(ResponseHandler.delete_failure("Subcontractor"), 500) + return HtmlHelper.json_response(ResponseHandler.delete_failure("Subcontractor"), 500) finally: cursor.close() @@ -1859,12 +1555,188 @@ def upload(): if file and file.filename.endswith('.xlsx'): filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) file.save(filepath) - log_action("Upload Excel File", f"User {current_user.id}Upload Excel File'{file}'") + LogHelper.log_action("Upload Excel File", f"User {current_user.id}Upload Excel File'{file}'") return redirect(url_for('show_table', filename=file.filename)) return render_template('uploadExcelFile.html') - +# Show excel data in tables6 +# @app.route('/show_table/') +# def show_table(filename): +# global data +# data = [] +# +# filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) +# wb = openpyxl.load_workbook(filepath, data_only=True) +# sheet = wb.active +# +# # Extract key file information from the first 4 rows +# file_info = { +# "Subcontractor": sheet.cell(row=1, column=2).value, +# "State": sheet.cell(row=2, column=2).value, +# "District": sheet.cell(row=3, column=2).value, +# "Block": sheet.cell(row=4, column=2).value, +# } +# +# errors = [] +# subcontractor_data = None +# state_data = None +# district_data = None +# block_data = None +# +# # Database connection +# connection = config.get_db_connection() +# if connection: +# try: +# cursor = connection.cursor(dictionary=True) +# +# # Validate State +# # cursor.execute("SELECT State_ID, State_Name FROM states WHERE State_Name = %s", (file_info['State'],)) +# # state_data = cursor.fetchone() +# cursor.callproc('GetStateByName', [file_info['State']]) +# for result in cursor.stored_results(): +# state_data = result.fetchone() +# +# if not state_data: +# errors.append(f"State '{file_info['State']}' is not valid. Please add it.") +# +# # Validate District +# if state_data: +# # cursor.execute( +# # "SELECT District_ID, District_Name FROM districts WHERE District_Name = %s AND State_ID = %s", +# # (file_info['District'], state_data['State_ID']) +# # ) +# # district_data = cursor.fetchone() +# cursor.callproc('GetDistrictByNameAndStates', [file_info['District'], state_data['State_ID']]) +# for result in cursor.stored_results(): +# district_data = result.fetchone() +# +# if not district_data: +# errors.append( +# f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.") +# +# # Validate Block +# if district_data: +# # cursor.execute( +# # "SELECT Block_Id, Block_Name FROM blocks WHERE Block_Name = %s AND District_ID = %s", +# # (file_info['Block'], district_data['District_ID']) +# # ) +# # block_data = cursor.fetchone() +# cursor.callproc('GetBlockByNameAndDistricts', [file_info['Block'], district_data['District_ID']]) +# for result in cursor.stored_results(): +# block_data = result.fetchone() +# +# if not block_data: +# errors.append( +# f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.") +# +# # old code +# # # Validate Subcontractor +# # cursor.execute("SELECT Contractor_Id, Contractor_Name FROM SubContractors WHERE Contractor_Name = %s", +# # (file_info['Subcontractor'],)) +# # subcontractor_data = cursor.fetchone() +# cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']]) +# for result in cursor.stored_results(): +# subcontractor_data = result.fetchone() +# +# if not subcontractor_data: +# # cursor.execute("INSERT INTO subcontractors (Contractor_Name) VALUES (%s)", +# # (file_info['Subcontractor'],)) +# # connection.commit() +# cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']]) +# connection.commit() +# +# # cursor.execute("SELECT Contractor_Id, Contractor_Name FROM SubContractors WHERE Contractor_Name = %s", +# # (file_info['Subcontractor'],)) +# # subcontractor_data = cursor.fetchone() +# cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']]) +# for result in cursor.stored_results(): +# subcontractor_data = result.fetchone() +# +# # new code +# # cursor.callproc('ValidateAndInsertSubcontractor', (file_info['Subcontractor'], 0, '')) +# # +# # for con in cursor.stored_results(): +# # subcontractor_data = con.fetchone() +# # print("subcon:",subcontractor_data) +# # +# # print("subcontractor_data",subcontractor_data) +# +# # Get hold types data from database (for faster lookup) +# # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types") +# # hold_types_data = cursor.fetchall() +# +# cursor.callproc("GetAllHoldTypes") +# for ht in cursor.stored_results(): +# hold_types_data = ht.fetchall() +# +# hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if +# row['hold_type']} +# +# cursor.close() +# except mysql.connector.Error as e: +# print(f"Database error: {e}") +# return "Database operation failed", 500 +# finally: +# connection.close() +# +# # Extract dynamic variable names from row 5 and detect "hold" columns +# variables = {} +# hold_columns = [] +# hold_counter = 0 +# +# for j in range(1, sheet.max_column + 1): +# col_value = sheet.cell(row=5, column=j).value +# if col_value: +# variables[col_value] = j # Store column name with its position +# +# # Check if the column header contains the word 'hold' +# if 'hold' in str(col_value).lower(): +# hold_counter += 1 +# # Lookup hold type id from database +# hold_type_key = str(col_value).lower().strip() +# hold_type_id = hold_types_lookup.get(hold_type_key, None) +# hold_columns.append({ +# 'column_name': col_value, +# 'column_number': j, +# 'hold_type_id': hold_type_id +# }) +# +# # Extract data dynamically based on row numbers +# for i in range(6, sheet.max_row + 1): +# row_data = {} +# if sheet.cell(row=i, column=1).value: +# row_data["Row Number"] = i # Store row number +# for var_name, col_num in variables.items(): +# row_data[var_name] = sheet.cell(row=i, column=col_num).value +# # Check if at least 4 non-empty cells exist in the row +# if sum(1 for value in row_data.values() if value) >= 4: +# data.append(row_data) +# +# # For debugging or console output, you can print the hold columns info +# for hold in hold_columns: +# if hold['hold_type_id']: +# print( +# f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") +# else: +# errors.append( +# f"Hold Type not added ! Column name '{hold['column_name']}'.") +# print( +# f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") +# +# return render_template( +# 'show_excel_file.html', +# file_info=file_info, +# variables=variables, +# data=data, +# subcontractor_data=subcontractor_data, +# state_data=state_data, +# district_data=district_data, +# block_data=block_data, +# errors=errors, +# hold_columns=hold_columns, +# hold_counter=hold_counter +# ) @app.route('/show_table/') def show_table(filename): @@ -1996,7 +1868,166 @@ def show_table(filename): hold_counter=hold_counter ) - +# Show excel data in tables6 +# @app.route('/show_table/') +# def show_table(filename): +# global data +# data = [] +# +# filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) +# wb = openpyxl.load_workbook(filepath, data_only=True) +# sheet = wb.active +# +# # Extract key file information from the first 4 rows +# file_info = { +# "Subcontractor": sheet.cell(row=1, column=2).value, +# "State": sheet.cell(row=2, column=2).value, +# "District": sheet.cell(row=3, column=2).value, +# "Block": sheet.cell(row=4, column=2).value, +# } +# +# errors = [] +# subcontractor_data = None +# state_data = None +# district_data = None +# block_data = None +# +# # Database connection +# connection = config.get_db_connection() +# if connection: +# try: +# cursor = connection.cursor(dictionary=True) +# +# # Validate State +# cursor.execute("SELECT State_ID, State_Name FROM states WHERE State_Name = %s", (file_info['State'],)) +# state_data = cursor.fetchone() +# if not state_data: +# errors.append(f"State '{file_info['State']}' is not valid. Please add it.") +# +# # Validate District +# if state_data: +# cursor.execute( +# "SELECT District_ID, District_Name FROM districts WHERE District_Name = %s AND State_ID = %s", +# (file_info['District'], state_data['State_ID']) +# ) +# district_data = cursor.fetchone() +# if not district_data: +# errors.append( +# f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.") +# +# # Validate Block +# if district_data: +# cursor.execute( +# "SELECT Block_Id, Block_Name FROM blocks WHERE Block_Name = %s AND District_ID = %s", +# (file_info['Block'], district_data['District_ID']) +# ) +# block_data = cursor.fetchone() +# if not block_data: +# errors.append( +# f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.") +# +# +# # old code +# # # Validate Subcontractor +# cursor.execute("SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name = %s", +# (file_info['Subcontractor'],)) +# subcontractor_data = cursor.fetchone() +# +# if not subcontractor_data: +# cursor.execute("INSERT INTO subcontractors (Contractor_Name) VALUES (%s)", +# (file_info['Subcontractor'],)) +# connection.commit() +# cursor.execute("SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name = %s", +# (file_info['Subcontractor'],)) +# subcontractor_data = cursor.fetchone() +# +# # new code +# # cursor.callproc('ValidateAndInsertSubcontractor', (file_info['Subcontractor'], 0, '')) +# # +# # for con in cursor.stored_results(): +# # subcontractor_data = con.fetchone() +# # print("subcon:",subcontractor_data) +# # +# # print("subcontractor_data",subcontractor_data) +# +# # Get hold types data from database (for faster lookup) +# # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types") +# # hold_types_data = cursor.fetchall() +# +# cursor.callproc("GetAllHoldTypes") +# for ht in cursor.stored_results(): +# hold_types_data = ht.fetchall() +# +# +# hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if row['hold_type']} +# +# +# cursor.close() +# except mysql.connector.Error as e: +# print(f"Database error: {e}") +# +# # return "Database operation failed", 500 +# return f"{e}",500 +# finally: +# connection.close() +# +# # Extract dynamic variable names from row 5 and detect "hold" columns +# variables = {} +# hold_columns = [] +# hold_counter = 0 +# +# for j in range(1, sheet.max_column + 1): +# col_value = sheet.cell(row=5, column=j).value +# if col_value: +# variables[col_value] = j # Store column name with its position +# +# # Check if the column header contains the word 'hold' +# if 'hold' in str(col_value).lower(): +# hold_counter += 1 +# # Lookup hold type id from database +# hold_type_key = str(col_value).lower().strip() +# hold_type_id = hold_types_lookup.get(hold_type_key, None) +# hold_columns.append({ +# 'column_name': col_value, +# 'column_number': j, +# 'hold_type_id': hold_type_id +# }) +# +# # Extract data dynamically based on row numbers +# for i in range(6, sheet.max_row + 1): +# row_data = {} +# if sheet.cell(row=i, column=1).value: +# row_data["Row Number"] = i # Store row number +# for var_name, col_num in variables.items(): +# row_data[var_name] = sheet.cell(row=i, column=col_num).value +# # Check if at least 4 non-empty cells exist in the row +# if sum(1 for value in row_data.values() if value) >= 4: +# data.append(row_data) +# +# # For debugging or console output, you can print the hold columns info +# for hold in hold_columns: +# if hold['hold_type_id']: +# print( +# f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") +# else: +# errors.append( +# f"Hold Type not added ! Column name '{hold['column_name']}'.") +# print( +# f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") +# +# return render_template( +# 'show_excel_file.html', +# file_info=file_info, +# variables=variables, +# data=data, +# subcontractor_data=subcontractor_data, +# state_data=state_data, +# district_data=district_data, +# block_data=block_data, +# errors=errors, +# hold_columns=hold_columns, +# hold_counter=hold_counter +# ) # save Excel data @app.route('/save_data', methods=['POST']) @@ -2011,13 +2042,13 @@ def save_data(): hold_columns = request.form.get("hold_columns") hold_counter = request.form.get("hold_counter") - + # print("Info: ", subcontractor_id, state_id, district_id, block_id) if not data: return jsonify({"error": "No data provided to save"}), 400 if data: - + # print("Total number of entries in data:", len(data)) connection = config.get_db_connection() cursor = connection.cursor() @@ -2051,7 +2082,7 @@ def save_data(): village_name, work_type = None, None village_id = 0 - log_action("Data saved", f"User {current_user.id} Data saved'{ village_name}'") + LogHelper.log_action("Data saved", f"User {current_user.id} Data saved'{ village_name}'") PMC_No = save_data.get('PMC_No') Invoice_Details = save_data.get('Invoice_Details') Invoice_Date = save_data.get('Invoice_Date') @@ -2089,7 +2120,8 @@ def save_data(): print("village_name ::", village_name, "|| work_type ::", work_type) if block_id and village_name: village_id = None - + # cursor.execute("SELECT Village_Id FROM villages WHERE Block_Id = %s AND Village_Name = %s",(block_id, village_name)) + # result = cursor.fetchone() cursor.callproc("GetVillageId", (block_id, village_name)) for result in cursor.stored_results(): result = result.fetchone() @@ -2099,7 +2131,8 @@ def save_data(): # cursor.execute("INSERT INTO villages (Village_Name, Block_Id) VALUES (%s, %s)", (village_name, block_id)) cursor.callproc("SaveVillage", (village_name, block_id)) - + # cursor.execute("SELECT Village_Id FROM villages WHERE Block_Id = %s AND Village_Name = %s",(block_id, village_name)) + # result = cursor.fetchone() cursor.callproc("GetVillageId", (block_id, village_name)) for result in cursor.stored_results(): result = result.fetchone() @@ -2110,7 +2143,16 @@ def save_data(): print("invoice :", PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No, Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount, SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount) - + # + # cursor.execute("SET @p_invoice_id = 0") + # cursor.callproc("SaveInvoice", ( + # PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No, + # Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount, + # SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount, + # subcontractor_id, "@p_invoice_id" + # )) + # cursor.execute("SELECT @p_invoice_id") + # invoice_id = cursor.fetchone()[0] args = ( PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No, @@ -2122,7 +2164,8 @@ def save_data(): # invoice_id = result.fetchone()['invoice_id'] print("All invoice Details ",args) results = cursor.callproc('SaveInvoice', args) - + # cursor.callproc("SaveInvoice",args) + # for re in cursor.stored_results(): invoice_id = results[-1] print("invoice id from the excel ", invoice_id) @@ -2151,7 +2194,11 @@ def save_data(): "hold_amount": hold_amount } - + # insert_hold_query = """INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount) + # VALUES (%(Contractor_Id)s, %(Invoice_Id)s, %(hold_type_id)s, %(hold_amount)s); + # """ + # cursor.execute(insert_hold_query, hold_join_data) + # print(f"Inserted hold join data: {hold_join_data}") cursor.callproc('InsertHoldJoinData', [ hold_join_data['Contractor_Id'], hold_join_data['Invoice_Id'], hold_join_data['hold_type_id'], hold_join_data['hold_amount'] @@ -2172,7 +2219,6 @@ def save_data(): """INSERT INTO credit_note (PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Contractor_Id, invoice_no) VALUES (%s,%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,%s)""", ( PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, subcontractor_id, Invoice_No)) #-----------------------------------------------Hold Amount---------------------------------------------------------------------- - import re # Step 1: Normalize Invoice_Details: lowercase, trim, remove extra spaces normalized_details = re.sub(r'\s+', ' ', Invoice_Details.strip()).lower() @@ -2212,11 +2258,15 @@ def save_data(): """INSERT INTO gst_release (PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR, Contractor_Id) VALUES (%s,%s, %s, %s, %s, %s, %s)""", (PMC_No, Invoice_No, Basic_Amount, Final_Amount, Total_Amount, UTR, subcontractor_id)) - + # insert_payment = """INSERT INTO payment (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) VALUES (%s, %s, %s, %s, %s, %s)""" + # cursor.execute(insert_payment, + # (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR)) if PMC_No and Total_Amount and UTR: print("Payment :", PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR ) - + # insert_payment = """INSERT INTO payment (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) VALUES (%s, %s, %s, %s, %s, %s)""" + # cursor.execute(insert_payment, + # (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR)) cursor.callproc("SavePayment", (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR )) @@ -2259,7 +2309,7 @@ def search_contractor(): conditions = [] params = [] - log_action("Search contractor", f"User {current_user.id} Search contractor'{ subcontractor_name}'") + LogHelper.log_action("Search contractor", f"User {current_user.id} Search contractor'{ subcontractor_name}'") if subcontractor_name: conditions.append("LOWER(s.Contractor_Name) LIKE LOWER(%s)") params.append(f"%{subcontractor_name}%") @@ -2286,7 +2336,21 @@ def search_contractor(): if not conditions: return jsonify({"error": "At least one field is required for search."}), 400 - + # query = f""" + # SELECT DISTINCT s.Contractor_Id, s.Contractor_Name, i.PMC_No, st.State_Name, + # d.District_Name, b.Block_Name, v.Village_Name + # FROM subcontractors s + # INNER JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id + # INNER JOIN villages v ON asg.Village_Id = v.Village_Id + # INNER JOIN invoice i ON i.Village_Id = asg.Village_Id AND i.PMC_No = asg.PMC_No + # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id + # LEFT JOIN districts d ON b.District_id = d.District_id + # LEFT JOIN states st ON d.State_Id = st.State_Id + # WHERE {' AND '.join(conditions)} + # ORDER BY s.Contractor_Name ASC, i.PMC_No ASC + # """ + # cursor.execute(query, tuple(params)) + # data = cursor.fetchall() cursor.callproc("search_contractor_info", [ subcontractor_name or None, pmc_no or None, @@ -2305,9 +2369,6 @@ def search_contractor(): -from flask import render_template -from datetime import datetime -import config @app.route('/contractor_report/') def contractor_report(contractor_id): @@ -2451,65 +2512,31 @@ def contractor_report(contractor_id): current_date=current_date) -from openpyxl import Workbook # # Download report by contractor id # # Download report by contractor id -from flask import send_from_directory - -import config +class FilePathData: + downloadReportFolder = "static/download" -from flask import send_from_directory -import os -import openpyxl -from openpyxl.styles import Font, PatternFill -from decimal import Decimal -from datetime import datetime - -from flask import send_from_directory -import os -import openpyxl -from openpyxl.styles import Font, PatternFill -from decimal import Decimal -from datetime import datetime - -from flask import send_from_directory -from decimal import Decimal -from datetime import datetime -import os -import openpyxl -from openpyxl.styles import Font, PatternFill -import config - - + @app.route('/download_report/') def download_report(contractor_id): connection = config.get_db_connection() cursor = connection.cursor(dictionary=True, buffered=True) - output_folder = "static/download" + output_folder = FilePathData.downloadReportFolder os.makedirs(output_folder, exist_ok=True) output_file = os.path.join(output_folder, f"Contractor_Report_{contractor_id}.xlsx") try: # ---------------- Contractor Info ---------------- - cursor.execute(""" - SELECT s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name, - s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, - s.Email, s.Address - FROM subcontractors s - LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id - LEFT JOIN villages v ON asg.Village_Id = v.Village_Id - LEFT JOIN blocks b ON v.Block_Id = b.Block_Id - LEFT JOIN districts d ON b.District_id = d.District_id - LEFT JOIN states st ON d.State_Id = st.State_Id - WHERE s.Contractor_Id = %s - """, (contractor_id,)) - contInfo = cursor.fetchone() - if not contInfo: + contractor = ContractorInfo(contractor_id) + contInfo = contractor.contInfo + + if not contractor.contInfo: return "No contractor found", 404 # ---------------- Hold Types ---------------- @@ -2663,17 +2690,8 @@ def download_report(contractor_id): for ht_id in hold_type_map: row.append(invoice_holds.get(ht_id, "")) - - - payment_list = payments_map.get(key, []) - - # Pop the first payment if available - payment = payment_list.pop(0) if payment_list else None - - # If the list becomes empty, remove the key - if not payment_list and key in payments_map: - del payments_map[key] - + # Payment values + payment = payments_map.get(key, [None])[0] row += [ inv["Final_Amount"], payment["Payment_Amount"] if payment else "", @@ -2681,7 +2699,6 @@ def download_report(contractor_id): payment["Total_amount"] if payment else "", payment["utr"] if payment and payment.get("utr") else "" ] - sheet.append(row) # ---------------- Extra Payments for this PMC ---------------- @@ -2775,7 +2792,21 @@ def pmc_report(pmc_no): cursor = connection.cursor(dictionary=True, buffered=True) try: - + # 1. Fetch PMC info using stored procedure + # cursor.execute(""" + # SELECT DISTINCT a.PMC_No, a.Village_Id, v.Village_Name, b.Block_Name, + # d.District_Name, s.State_Name, sc.Contractor_Id, sc.Contractor_Name, + # sc.Address, sc.Mobile_No, sc.PAN_No, sc.Email, sc.Gender, + # sc.GST_Registration_Type, sc.GST_No + # FROM assign_subcontractors a + # INNER JOIN villages v ON a.Village_Id = v.Village_Id + # INNER JOIN blocks b ON v.Block_Id = b.Block_Id + # INNER JOIN districts d ON b.District_id = d.District_id + # INNER JOIN states s ON d.State_Id = s.State_Id + # INNER JOIN subcontractors sc ON a.Contractor_Id = sc.Contractor_Id + # WHERE a.pmc_no = %s + # """, (pmc_no,)) + # pmc_info = cursor.fetchone() cursor.callproc("GetContractorInfoByPmcNo", (pmc_no,)) pmc_info = next(cursor.stored_results()).fetchone() @@ -2783,7 +2814,16 @@ def pmc_report(pmc_no): if not pmc_info: return "No PMC found with this number", 404 - + # 2. Fetch hold types using stored procedure + # cursor.execute(""" + # SELECT DISTINCT ht.hold_type_id, ht.hold_type + # FROM invoice_subcontractor_hold_join h + # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id + # JOIN invoice i ON h.Invoice_Id = i.Invoice_Id + # JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No + # WHERE a.PMC_No = %s AND a.Contractor_Id = %s + # """, (pmc_no, pmc_info["Contractor_Id"])) + # hold_types = cursor.fetchall() cursor.callproc("Get_pmc_hold_types", (pmc_no, pmc_info["Contractor_Id"])) hold_types = next(cursor.stored_results()).fetchall() hold_type_ids = [ht['hold_type_id'] for ht in hold_types] @@ -2842,7 +2882,13 @@ def pmc_report(pmc_no): ORDER BY invoice_no ASC """, (pmc_no,)) gst_rel = cursor.fetchall() - + # gst_rel = cursor.fetchall() + # cursor.callproc('GetGSTReleaseByPMC', [pmc_no]) + # + # # Fetch results + # for result in cursor.stored_results(): + # gst_rel = result.fetchall() + total_gst_basic = sum(row.get('basic_amount', 0) or 0 for row in gst_rel) total_gst_final = sum(row.get('final_amount', 0) or 0 for row in gst_rel) @@ -2915,7 +2961,883 @@ def pmc_report(pmc_no): ) +# # Download report by PMC No +# @app.route('/download_pmc_report/') +# def download_pmc_report(pmc_no): +# connection = config.get_db_connection() +# output_folder = "static/download" +# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx") +# if not os.path.exists(output_folder): +# os.makedirs(output_folder) + +# cursor = connection.cursor(dictionary=True) + +# try: +# # # Fetch Contractor Details using PMC No +# # cursor.execute(""" +# # SELECT DISTINCT s.Contractor_Id, s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name, +# # s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address +# # FROM subcontractors s +# # LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id +# # LEFT JOIN villages v ON asg.Village_Id = v.Village_Id +# # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id +# # LEFT JOIN districts d ON b.District_id = d.District_id +# # LEFT JOIN states st ON d.State_Id = st.State_Id +# # WHERE asg.PMC_No = %s +# # """, (pmc_no,)) +# # contractor_info = cursor.fetchone() +# cursor.callproc('GetContractorDetailsByPMC', [pmc_no]) + +# # Now fetch the result: +# for result in cursor.stored_results(): +# contractor_info = result.fetchone() + +# if not contractor_info: +# return "No contractor found for this PMC No", 404 + +# # # Fetch distinct hold types present for the contractor +# # cursor.execute(""" +# # SELECT DISTINCT ht.hold_type_id, ht.hold_type +# # FROM invoice_subcontractor_hold_join h +# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id +# # WHERE h.Contractor_Id = %s +# # """, (contractor_info["Contractor_Id"],)) +# # hold_types = cursor.fetchall() +# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]]) + +# for result in cursor.stored_results(): +# hold_types = result.fetchall() + +# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} + +# # # # Fetch Invoices & GST Releases +# # cursor.execute(""" +# # SELECT DISTINCT i.Invoice_Id, i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details, +# # i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount, +# # i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount, +# # i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount, +# # g.pmc_no AS gst_pmc_no, g.invoice_no AS gst_invoice_no, +# # g.basic_amount AS gst_basic_amount, g.final_amount AS gst_final_amount +# # FROM invoice i +# # LEFT JOIN assign_subcontractors asg ON i.PMC_No = asg.PMC_No +# # LEFT JOIN villages v ON i.Village_Id = v.Village_Id +# # LEFT JOIN gst_release g ON i.PMC_No = g.pmc_no AND i.Invoice_No = g.invoice_no +# # WHERE asg.PMC_No = %s +# # ORDER BY i.Invoice_Date, i.Invoice_No +# # """, (pmc_no,)) +# # invoices = cursor.fetchall() + +# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no]) + +# for result in cursor.stored_results(): +# invoices = result.fetchall() +# print("pmc_report invoice data:",invoices) + +# # cursor.callproc('GetInvoicesAndGSTReleasesByPMC', [pmc_no]) + +# # for result in cursor.stored_results(): +# # invoices = result.fetchall() + +# # # Fetch Hold Amounts separately +# # cursor.execute(""" +# # SELECT h.Invoice_Id, ht.hold_type_id, h.hold_amount +# # FROM invoice_subcontractor_hold_join h +# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id +# # WHERE h.Contractor_Id = %s +# # """, (contractor_info["Contractor_Id"],)) +# # hold_amounts = cursor.fetchall() +# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]]) + +# for result in cursor.stored_results(): +# hold_amounts = result.fetchall() + +# # Create a mapping of invoice_id to hold amounts by type +# hold_data = {} +# for h in hold_amounts: +# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount'] + +# # # Fetch all Payments for the PMC number +# # cursor.execute(""" +# # SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR +# # FROM payment +# # WHERE pmc_no = %s +# # ORDER BY invoice_no +# # """, (pmc_no,)) +# # all_payments = cursor.fetchall() +# cursor.callproc('GetAllPaymentsByPMC', [pmc_no]) + +# for result in cursor.stored_results(): +# all_payments = result.fetchall() + +# # Organize payments by Invoice No (both regular and GST release notes) +# payments_map = {} +# extra_payments = [] +# for pay in all_payments: +# if pay['invoice_no']: +# key = pay['invoice_no'] +# if key not in payments_map: +# payments_map[key] = [] +# payments_map[key].append(pay) +# else: +# extra_payments.append(pay) + +# # Create Excel workbook +# workbook = openpyxl.Workbook() +# sheet = workbook.active +# sheet.title = "PMC Report" + +# # Write Contractor Details +# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""]) +# sheet.append( +# ["Contractor Name", contractor_info["Contractor_Name"], " ", "GST No", contractor_info["GST_No"], " ", +# "GST Type", contractor_info["GST_Registration_Type"]]) +# sheet.append(["State", contractor_info["State_Name"], " ", "PAN No", contractor_info["PAN_No"], " ", "Address", +# contractor_info["Address"]]) +# sheet.append(["District", contractor_info["District_Name"], " ", "Mobile No", contractor_info["Mobile_No"]]) +# sheet.append(["Block", contractor_info["Block_Name"], " ", "Email", contractor_info["Email"]]) +# sheet.append([]) + +# # Table Headers - include all hold types as separate columns +# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No", +# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)", +# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"] + +# hold_headers = [ht['hold_type'] for ht in hold_types] + +# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"] + +# sheet.append(base_headers + hold_headers + payment_headers) + +# seen_invoices = set() +# seen_gst_notes = set() +# processed_payments = set() + +# # Process invoices +# for inv in invoices: +# invoice_no = inv["Invoice_No"] +# payments = payments_map.get(invoice_no, []) + +# # Process invoice row with first payment (if exists) +# if invoice_no not in seen_invoices: +# seen_invoices.add(invoice_no) +# first_payment = payments[0] if len(payments) > 0 else None + +# # Base invoice data +# row = [ +# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"], +# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"], +# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"], +# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"] +# ] + +# # Add hold amounts for each hold type +# invoice_holds = hold_data.get(inv["Invoice_Id"], {}) +# for ht_id in hold_type_map.keys(): +# row.append(invoice_holds.get(ht_id, "")) + +# # Add payment information +# row += [ +# inv["Final_Amount"], +# first_payment["Payment_Amount"] if first_payment else "", +# first_payment["TDS_Payment_Amount"] if first_payment else "", +# first_payment["Total_amount"] if first_payment else "", +# first_payment["UTR"] if first_payment else "" +# ] + +# sheet.append(row) + +# if first_payment: +# payment_id = f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}" +# processed_payments.add(payment_id) + +# # Process GST release if exists (only if we have a matching GST record) +# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: +# seen_gst_notes.add(inv["gst_invoice_no"]) + +# # Find the payment that matches this GST release +# gst_payment = None +# for payment in payments[1:]: # Skip first payment (already used for invoice) +# if payment['invoice_no'] == inv["gst_invoice_no"]: +# gst_payment = payment +# break + +# # If no payment found in the invoice's payments, check all payments +# if not gst_payment: +# gst_payments = payments_map.get(inv["gst_invoice_no"], []) +# if gst_payments: +# gst_payment = gst_payments[0] + +# # GST release row +# row = [ +# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], +# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount +# ] + +# # Empty holds for GST release +# row += ["" for _ in hold_headers] + +# # Add payment information +# row += [ +# inv["gst_final_amount"], +# gst_payment["Payment_Amount"] if gst_payment else "", +# gst_payment["TDS_Payment_Amount"] if gst_payment else "", +# gst_payment["Total_amount"] if gst_payment else "", +# gst_payment["UTR"] if gst_payment else "" +# ] + +# sheet.append(row) + +# if gst_payment: +# payment_id = f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}" +# processed_payments.add(payment_id) + +# # Process remaining payments as extra payments +# for payment in payments[1:]: +# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" +# if payment_id not in processed_payments: +# row = [ +# pmc_no, "", "", "", "", payment['invoice_no'], +# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount +# ] + +# # Empty holds for extra payments +# row += ["" for _ in hold_headers] + +# # Add payment information +# row += [ +# "", +# payment["Payment_Amount"], +# payment["TDS_Payment_Amount"], +# payment["Total_amount"], +# payment["UTR"] +# ] + +# sheet.append(row) +# processed_payments.add(payment_id) + +# # Process extra payments (null invoice_no) +# for payment in extra_payments: +# payment_id = f"null-{payment['Payment_Amount']}-{payment.get('UTR', '')}" +# if payment_id not in processed_payments: +# row = [ +# pmc_no, "", "", "", "", "", +# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount +# ] + +# # Empty holds for null invoice payments +# row += ["" for _ in hold_headers] + +# # Add payment information +# row += [ +# "", +# payment["Payment_Amount"], +# payment["TDS_Payment_Amount"], +# payment["Total_amount"], +# payment["UTR"] +# ] + +# sheet.append(row) +# processed_payments.add(payment_id) + +# # Calculate totals +# total_basic_amount = 0 +# total_tds_amount = 0 +# total_sd_amount = 0 +# total_on_commission = 0 +# total_hold_amount = 0 +# total_final_amount = 0 +# total_payment_amount = 0 +# total_tds_payment_amount = 0 +# total_total_paid = 0 + +# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True): +# try: +# total_basic_amount += float(row[6] or 0) # Basic_Amount +# total_tds_amount += float(row[11] or 0) # TDS_Amount +# total_sd_amount += float(row[12] or 0) # SD_Amount +# total_on_commission += float(row[13] or 0) # On_Commission +# total_final_amount += float(row[-5] or 0) # Final_Amount +# total_payment_amount += float(row[-4] or 0) # Payment_Amount +# total_tds_payment_amount += float(row[-3] or 0) # TDS_Payment +# total_total_paid += float(row[-2] or 0) # Total_Paid + +# # Sum of hold amounts +# hold_start_col = len(base_headers) +# hold_end_col = hold_start_col + len(hold_headers) +# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col)) +# except (ValueError, IndexError, TypeError): +# continue + +# # Append totals row +# totals_row = [ +# "TOTAL", "", "", "", "", "", +# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, +# total_on_commission, "", "", # Empty GST SD Amount +# ] + +# # Add hold totals +# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1) + +# # Add payment totals +# totals_row += [ +# total_final_amount, +# total_payment_amount, +# total_tds_payment_amount, +# total_total_paid, +# "" # UTR column remains empty +# ] + +# sheet.append([]) +# sheet.append(totals_row) + +# # Make totals row bold +# for cell in sheet[sheet.max_row]: +# cell.font = Font(bold=True) + +# # Save Excel file +# workbook.save(output_file) +# workbook.close() + +# finally: +# cursor.close() +# connection.close() + +# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True) + + + +# @app.route('/download_pmc_report/') +# def download_pmc_report(pmc_no): +# connection = config.get_db_connection() +# output_folder = "static/download" +# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx") +# +# if not os.path.exists(output_folder): +# os.makedirs(output_folder) +# +# cursor = connection.cursor(dictionary=True) +# +# try: +# cursor.callproc('GetContractorDetailsByPMC', [pmc_no]) +# +# for result in cursor.stored_results(): +# contractor_info = result.fetchone() +# +# if not contractor_info: +# return "No contractor found for this PMC No", 404 +# +# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]]) +# +# for result in cursor.stored_results(): +# hold_types = result.fetchall() +# +# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} +# +# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no]) +# +# for result in cursor.stored_results(): +# invoices = result.fetchall() +# total_tds=Decimal('0.00') +# final_amount=Decimal('0.00') +# # total_hold_amount=Decimal('0.00') +# for data in invoices: +# total_tds=total_tds+data.get('TDS_Amount',Decimal('0.00')) +# final_amount=final_amount+data.get('Final_Amount',Decimal('0.00')) +# +# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]]) +# +# for result in cursor.stored_results(): +# hold_amounts = result.fetchall() +# +# hold_data = {} +# for h in hold_amounts: +# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount'] +# +# cursor.callproc('GetAllPaymentsByPMC', [pmc_no]) +# +# for result in cursor.stored_results(): +# all_payments = result.fetchall() +# total_amount=Decimal('0.00') +# for d in all_payments: +# total_amount=total_amount+ d.get('Total_Amount',Decimal('0.00')) +# total_amount_paid= final_amount- total_amount; +# payments_map = {} +# extra_payments = [] +# for pay in all_payments: +# if pay['invoice_no']: +# key = pay['invoice_no'] +# if key not in payments_map: +# payments_map[key] = [] +# payments_map[key].append(pay) +# else: +# extra_payments.append(pay) +# +# workbook = openpyxl.Workbook() +# sheet = workbook.active +# sheet.title = "PMC Report" +# +# # Write Contractor Details +# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""]) +# sheet.append( +# ["Contractor Name", contractor_info["Contractor_Name"], " ", "GST No", contractor_info["GST_No"], " ", +# "GST Type", contractor_info["GST_Registration_Type"]]) +# sheet.append(["State", contractor_info["State_Name"], " ", "PAN No", contractor_info["PAN_No"], " ", "Address", +# contractor_info["Address"]]) +# sheet.append(["District", contractor_info["District_Name"], " ", "Mobile No", contractor_info["Mobile_No"]]) +# sheet.append(["Block", contractor_info["Block_Name"], " ", "Email", contractor_info["Email"]]) +# sheet.append([]) +# +# # Table Headers - include all hold types as separate columns +# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No", +# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)", +# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"] +# +# hold_headers = [ht['hold_type'] for ht in hold_types] +# +# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"] +# +# sheet.append(base_headers + hold_headers + payment_headers) +# +# seen_invoices = set() +# seen_gst_notes = set() +# processed_payments = set() +# +# # Process invoices +# for inv in invoices: +# invoice_no = inv["Invoice_No"] +# payments = payments_map.get(invoice_no, []) +# +# # Process invoice row with first payment (if exists) +# if invoice_no not in seen_invoices: +# seen_invoices.add(invoice_no) +# first_payment = payments[0] if len(payments) > 0 else None +# +# # Base invoice data +# row = [ +# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"], +# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"], +# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"], +# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"] +# ] +# +# # Add hold amounts for each hold type +# invoice_holds = hold_data.get(inv["Invoice_Id"], {}) +# for ht_id in hold_type_map.keys(): +# row.append(invoice_holds.get(ht_id, "")) +# +# # Add payment information +# row += [ +# inv["Final_Amount"], +# first_payment["Payment_Amount"] if first_payment else "", +# first_payment["TDS_Payment_Amount"] if first_payment else "", +# first_payment["Total_amount"] if first_payment else "", +# first_payment["UTR"] if first_payment else "" +# ] +# +# sheet.append(row) +# +# if first_payment: +# payment_id = f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}" +# processed_payments.add(payment_id) +# +# # Process GST release if exists (only if we have a matching GST record) +# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: +# seen_gst_notes.add(inv["gst_invoice_no"]) +# +# # Find the payment that matches this GST release +# gst_payment = None +# for payment in payments[1:]: # Skip first payment (already used for invoice) +# if payment['invoice_no'] == inv["gst_invoice_no"]: +# gst_payment = payment +# break +# +# # If no payment found in the invoice's payments, check all payments +# if not gst_payment: +# gst_payments = payments_map.get(inv["gst_invoice_no"], []) +# if gst_payments: +# gst_payment = gst_payments[0] +# +# # GST release row (this will be in the same row, after the invoice information) +# gst_row = [ +# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], +# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount +# ] +# +# # Empty holds for GST release +# gst_row += ["" for _ in hold_headers] +# +# # Add GST payment information (same columns as invoice payment information) +# gst_row += [ +# inv["gst_final_amount"], +# gst_payment["Payment_Amount"] if gst_payment else "", +# gst_payment["TDS_Payment_Amount"] if gst_payment else "", +# gst_payment["Total_amount"] if gst_payment else "", +# gst_payment["UTR"] if gst_payment else "" +# ] +# +# sheet.append(gst_row) +# +# if gst_payment: +# payment_id = f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}" +# processed_payments.add(payment_id) +# +# # Process remaining payments as extra payments (if any) +# for payment in payments[1:]: +# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" +# if payment_id not in processed_payments: +# row = [ +# pmc_no, "", "", "", "", payment['invoice_no'], +# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount +# ] +# +# # Empty holds for extra payments +# row += ["" for _ in hold_headers] +# +# # Add payment information +# row += [ +# "", +# payment["Payment_Amount"], +# payment["TDS_Payment_Amount"], +# payment["Total_amount"], +# payment["UTR"] +# ] +# +# sheet.append(row) +# processed_payments.add(payment_id) +# +# # Process extra payments (null invoice_no) +# for payment in extra_payments: +# payment_id = f"null-{payment['Payment_Amount']}-{payment.get('UTR', '')}" +# if payment_id not in processed_payments: +# row = [ +# pmc_no, "", "", "", "", "", +# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount +# ] +# +# # Empty holds for null invoice payments +# row += ["" for _ in hold_headers] +# +# # Add payment information +# row += [ +# "", +# payment["Payment_Amount"], +# payment["TDS_Payment_Amount"], +# payment["Total_amount"], +# payment["UTR"] +# ] +# +# sheet.append(row) +# processed_payments.add(payment_id) +# +# # Calculate totals +# total_basic_amount = 0 +# total_tds_amount = 0 +# total_sd_amount = 0 +# total_on_commission = 0 +# total_hold_amount = 0 +# total_final_amount = 0 +# total_payment_amount = 0 +# total_tds_payment_amount = 0 +# total_total_paid = 0 +# +# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True): +# try: +# total_basic_amount += float(row[6] or 0) # Basic_Amount +# total_tds_amount += float(row[11] or 0) # TDS_Amount +# total_sd_amount += float(row[12] or 0) # SD_Amount +# total_on_commission += float(row[13] or 0) # On_Commission +# total_final_amount += float(row[-5] or 0) # Final_Amount +# total_payment_amount += float(row[-4] or 0) # Payment_Amount +# total_tds_payment_amount += float(row[-3] or 0) # TDS_Payment +# total_total_paid += float(row[-2] or 0) # Total_Paid +# +# # Sum of hold amounts +# hold_start_col = len(base_headers) +# hold_end_col = hold_start_col + len(hold_headers) +# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col)) +# except (ValueError, IndexError, TypeError): +# continue +# +# # Append totals row +# totals_row = [ +# "TOTAL", "", "", "", "", "", +# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, +# total_on_commission, "", "", # Empty GST SD Amount +# ] +# if hold_headers: +# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1) +# +# # Add payment totals +# totals_row += [ +# total_final_amount, +# total_payment_amount, +# total_tds_payment_amount, +# total_total_paid, +# "" # UTR column remains empty +# ] +# +# sheet.append([]) +# sheet.append(totals_row) +# #new code added for small chart---summary +# total_hold_amount=Decimal('0.00') +# for d in invoices: +# total_hold_amount = total_hold_amount + d.get('SD_Amount', Decimal('0.00')) + d.get('On_Commission', +# Decimal( +# '0.00')) + d.get( +# 'Hydro_Testing', Decimal('0.00')) +# for data in hold_amounts: +# total_hold_amount = total_hold_amount + data.get('hold_amount', Decimal('0.00')) +# print("Total Hold Amount after adding the hold amount ", total_hold_amount) +# +# # Add payment information +# # Get today's date +# today_date = datetime.today().strftime('%A,%Y-%m-%d') +# # Add headers (optional) +# sheet.append(["Contractor Name", contractor_info["Contractor_Name"]]) +# sheet.append(["Date", today_date]) +# sheet.append(["Description", "Amount"]) +# # Add your values +# sheet.append(["Advance/Surplus", str(total_final_amount-total_payment_amount)]) +# sheet.append(["Total Hold Amount", str(total_hold_amount)]) +# sheet.append(["Amount With TDS", str(total_tds_payment_amount)]) +# # new coded ended here for summary chart +# # Make totals row bold +# for cell in sheet[sheet.max_row]: +# cell.font = Font(bold=True) +# +# # Save Excel file +# workbook.save(output_file) +# workbook.close() +# +# finally: +# cursor.close() +# connection.close() +# +# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True) + +# @app.route('/download_pmc_report/') +# def download_pmc_report(pmc_no): +# connection = config.get_db_connection() +# output_folder = "static/download" +# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx") +# +# if not os.path.exists(output_folder): +# os.makedirs(output_folder) +# +# cursor = connection.cursor(dictionary=True) +# +# try: +# cursor.callproc('GetContractorDetailsByPMC', [pmc_no]) +# contractor_info = next(cursor.stored_results()).fetchone() +# +# if not contractor_info: +# return "No contractor found for this PMC No", 404 +# +# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]]) +# hold_types = next(cursor.stored_results()).fetchall() +# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} +# +# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no]) +# invoices = next(cursor.stored_results()).fetchall() +# +# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]]) +# hold_amounts = next(cursor.stored_results()).fetchall() +# hold_data = {} +# for h in hold_amounts: +# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount'] +# +# cursor.callproc('GetAllPaymentsByPMC', [pmc_no]) +# all_payments = next(cursor.stored_results()).fetchall() +# +# payments_map = {} +# extra_payments = [] +# for pay in all_payments: +# if pay['invoice_no']: +# payments_map.setdefault(pay['invoice_no'], []).append(pay) +# else: +# extra_payments.append(pay) +# +# workbook = openpyxl.Workbook() +# sheet = workbook.active +# sheet.title = "PMC Report" +# +# # Write contractor header +# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD."]) +# sheet.append(["Contractor Name", contractor_info["Contractor_Name"], "", "GST No", contractor_info["GST_No"], "", "GST Type", contractor_info["GST_Registration_Type"]]) +# sheet.append(["State", contractor_info["State_Name"], "", "PAN No", contractor_info["PAN_No"], "", "Address", contractor_info["Address"]]) +# sheet.append(["District", contractor_info["District_Name"], "", "Mobile No", contractor_info["Mobile_No"]]) +# sheet.append(["Block", contractor_info["Block_Name"], "", "Email", contractor_info["Email"]]) +# sheet.append([]) +# +# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No", +# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)", +# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"] +# +# hold_headers = [ht['hold_type'] for ht in hold_types] +# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"] +# sheet.append(base_headers + hold_headers + payment_headers) +# # Style the headers +# header_fill=PatternFill(start_color="ADD8E6",end_color="ADD8E6",fill_type="solid") +# header_font=Font(bold=True) +# for cell in sheet[sheet.max_row]: +# cell.font=header_font +# cell.fill=header_fill +# +# seen_invoices = set() +# seen_gst_notes = set() +# processed_payments = set() +# +# for inv in invoices: +# invoice_no = inv["Invoice_No"] +# payments = payments_map.get(invoice_no, []) +# +# if invoice_no not in seen_invoices: +# seen_invoices.add(invoice_no) +# first_payment = payments[0] if payments else None +# +# row = [ +# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"], +# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"], +# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"], +# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"] +# ] +# +# invoice_holds = hold_data.get(inv["Invoice_Id"], {}) +# for ht_id in hold_type_map.keys(): +# row.append(invoice_holds.get(ht_id, "")) +# +# row += [ +# inv["Final_Amount"], +# first_payment["Payment_Amount"] if first_payment else "", +# first_payment["TDS_Payment_Amount"] if first_payment else "", +# first_payment["Total_amount"] if first_payment else "", +# first_payment["UTR"] if first_payment else "" +# ] +# +# sheet.append(row) +# +# if first_payment: +# processed_payments.add(f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}") +# +# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: +# seen_gst_notes.add(inv["gst_invoice_no"]) +# gst_payment = None +# for payment in payments[1:]: +# if payment['invoice_no'] == inv["gst_invoice_no"]: +# gst_payment = payment +# break +# if not gst_payment: +# gst_payment = payments_map.get(inv["gst_invoice_no"], [None])[0] +# +# gst_row = [ +# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], +# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" +# ] +# gst_row += ["" for _ in hold_headers] +# gst_row += [ +# inv["gst_final_amount"], +# gst_payment["Payment_Amount"] if gst_payment else "", +# gst_payment["TDS_Payment_Amount"] if gst_payment else "", +# gst_payment["Total_amount"] if gst_payment else "", +# gst_payment["UTR"] if gst_payment else "" +# ] +# sheet.append(gst_row) +# if gst_payment: +# processed_payments.add(f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}") +# +# for payment in payments[1:]: +# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" +# if payment_id not in processed_payments: +# row = [pmc_no, "", "", "", "", payment['invoice_no']] + [""] * 10 +# row += ["" for _ in hold_headers] +# row += [ +# "", payment["Payment_Amount"], payment["TDS_Payment_Amount"], +# payment["Total_amount"], payment["UTR"] +# ] +# sheet.append(row) +# processed_payments.add(payment_id) +# +# for payment in extra_payments: +# row = [pmc_no, "", "", "", "", ""] + [""] * 10 +# row += ["" for _ in hold_headers] +# row += [ +# "", payment["Payment_Amount"], payment["TDS_Payment_Amount"], +# payment["Total_amount"], payment["UTR"] +# ] +# sheet.append(row) +# +# # Totals +# total_basic_amount = Decimal('0.00') +# total_tds_amount = Decimal('0.00') +# total_sd_amount = Decimal('0.00') +# total_on_commission = Decimal('0.00') +# total_final_amount = Decimal('0.00') +# total_payment_amount = Decimal('0.00') +# total_tds_payment_amount = Decimal('0.00') +# total_total_paid = Decimal('0.00') +# total_hold_amount_dynamic = Decimal('0.00') +# +# for row in sheet.iter_rows(min_row=8, max_row=sheet.max_row, values_only=True): +# try: +# total_basic_amount += Decimal(str(row[6] or 0)) +# total_tds_amount += Decimal(str(row[11] or 0)) +# total_sd_amount += Decimal(str(row[12] or 0)) +# total_on_commission += Decimal(str(row[13] or 0)) +# total_final_amount += Decimal(str(row[-5] or 0)) +# total_payment_amount += Decimal(str(row[-4] or 0)) +# total_tds_payment_amount += Decimal(str(row[-3] or 0)) +# total_total_paid += Decimal(str(row[-2] or 0)) +# +# for i in range(len(base_headers), len(base_headers) + len(hold_headers)): +# total_hold_amount_dynamic += Decimal(str(row[i] or 0)) +# except: +# continue +# +# totals_row = [ +# "TOTAL", "", "", "", "", "", +# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, +# total_on_commission, "", "" +# ] +# totals_row += [total_hold_amount_dynamic] + [""] * (len(hold_headers) - 1) +# totals_row += [ +# total_final_amount, +# total_payment_amount, +# total_tds_payment_amount, +# total_total_paid, +# "" +# ] +# +# sheet.append([]) +# sheet.append(totals_row) +# +# # Summary +# summary_hold = Decimal('0.00') +# for d in invoices: +# summary_hold += Decimal(str(d.get('SD_Amount', 0.00))) + Decimal(str(d.get('On_Commission', 0.00))) + Decimal(str(d.get('Hydro_Testing', 0.00))) +# for h in hold_amounts: +# summary_hold += Decimal(str(h.get('hold_amount', 0.00))) +# +# sheet.append([]) +# today = datetime.today().strftime('%A, %Y-%m-%d') +# sheet.append(["Contractor Name", contractor_info["Contractor_Name"]]) +# sheet.append(["Date", today]) +# sheet.append(["Description", "Amount"]) +# sheet.append(["Advance/Surplus", str(total_final_amount - total_payment_amount)]) +# sheet.append(["Total Hold Amount", str(summary_hold)]) +# sheet.append(["Amount With TDS", str(total_payment_amount + total_tds_payment_amount)]) +# +# for cell in sheet[sheet.max_row]: +# cell.font = Font(bold=True) +# +# workbook.save(output_file) +# workbook.close() +# +# finally: +# cursor.close() +# connection.close() +# +# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True) @app.route('/download_pmc_report/') @@ -3004,7 +3926,7 @@ def download_pmc_report(pmc_no): for inv_no in invoice_nos: gst_release_map.setdefault(inv_no, []).append(gr) - log_action("Download PMC Report", f"User {current_user.id} Download PMC Report'{ pmc_no}'") + LogHelper.log_action("Download PMC Report", f"User {current_user.id} Download PMC Report'{ pmc_no}'") workbook = openpyxl.Workbook() sheet = workbook.active sheet.title = "PMC Report" @@ -3067,7 +3989,29 @@ def download_pmc_report(pmc_no): if first_payment: processed_payments.add(f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}") - + # if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: + # seen_gst_notes.add(inv["gst_invoice_no"]) + # gst_payment = None + # for payment in payments[1:]: + # if payment['invoice_no'] == inv["gst_invoice_no"]: + # gst_payment = payment + # break + # if not gst_payment: + # gst_payment = payments_map.get(inv["gst_invoice_no"], [None])[0] + # + # gst_row = [ + # pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], + # inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" + # ] + # gst_row += ["" for _ in hold_headers] + # gst_row += [ + # inv["gst_final_amount"], + # gst_payment["Payment_Amount"] if gst_payment else "", + # gst_payment["TDS_Payment_Amount"] if gst_payment else "", + # gst_payment["Total_amount"] if gst_payment else "", + # gst_payment["UTR"] if gst_payment else "" + # ] + # sheet.append(gst_row) # Add GST Release Note(s) for this invoice if any if invoice_no in gst_release_map: for gr in gst_release_map[invoice_no]: @@ -3087,7 +4031,8 @@ def download_pmc_report(pmc_no): sheet.append(gst_row) - + # if gst_payment: + # processed_payments.add(f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}") for payment in payments[1:]: payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" @@ -3163,7 +4108,21 @@ def download_pmc_report(pmc_no): except: continue - + # totals_row = [ + # "TOTAL", "", "", "", "", "", + # total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, + # total_on_commission, "", "" + # ] + # if total_hold_amount_dynamic: + # totals_row += [total_hold_amount_dynamic] + [""] * (len(hold_headers) - 1) + # totals_row += [ + # total_final_amount, + # total_payment_amount, + # total_tds_payment_amount, + # total_total_paid, + # "" + # ] + # Prepare empty totals_row with length of base_headers totals_row = [""] * len(base_headers) # Fill in specific columns @@ -3191,7 +4150,12 @@ def download_pmc_report(pmc_no): sheet.append([]) sheet.append(totals_row) - + # Summary + # summary_hold = Decimal('0.00') + # for d in invoices: + # summary_hold += Decimal(str(d.get('SD_Amount', 0))) + Decimal(str(d.get('On_Commission', 0))) + Decimal(str(d.get('Hydro_Testing', 0))) + # for h in hold_amounts: + # summary_hold += Decimal(str(h.get('hold_amount', 0))) sheet.append([]) today = datetime.today().strftime('%A, %Y-%m-%d') @@ -3235,12 +4199,18 @@ def add_hold_type(): if not hold_type or not hold_type[0].isalpha(): return jsonify({"status": "error", "message": "Hold Type must start with a letter."}), 400 - + # Validation: Check if it already exists (case-insensitive) + # cursor.execute("SELECT COUNT(*) AS count FROM hold_types WHERE LOWER(hold_type) = LOWER(%s)", (hold_type,)) + # if cursor.fetchone()['count'] > 0: + # return jsonify({"status": "error", "message": "This Hold Type already exists."}), 400 + # Call the procedure to check if the hold_type exists cursor.callproc('CheckHoldTypeExists', [hold_type]) try: - + # Insert new hold type into the database + # cursor.execute("INSERT INTO hold_types (hold_type) VALUES (%s)", (hold_type,)) + # connection.commit() cursor.callproc('SaveHoldType', [hold_type]) connection.commit() @@ -3259,7 +4229,62 @@ def add_hold_type(): return render_template('add_hold_type.html', Hold_Types_data=hold_types) - +# Route to Update Hold Type +# @app.route('/update_hold_type/', methods=['POST', 'GET']) +# def update_hold_type(id): +# # GET request: Show the form with the current hold type +# if request.method == 'GET': +# connection = config.get_db_connection() +# cursor = connection.cursor() +# # cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,)) +# # hold_type = cursor.fetchone() +# +# cursor.callproc("GetHoldTypesById", (id,)) +# for hold in cursor.stored_results(): +# hold_type = hold.fetchone() +# +# cursor.close() +# connection.close() +# +# if not hold_type: +# return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404 +# +# return render_template('edit_hold_type.html', hold_type=hold_type) +# +# # POST request: Update the hold type +# if request.method == 'POST': +# new_hold_type = request.form.get('hold_type').strip() +# +# # Validation: Must start with a letter +# if not new_hold_type or not new_hold_type[0].isalpha(): +# return jsonify(ResponseHandler.invalid_name('Hold Type')), 400 +# +# connection = config.get_db_connection() +# cursor = connection.cursor() +# +# try: +# # Check if the hold type exists before updating +# # cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,)) +# # hold_type = cursor.fetchone() +# cursor.callproc("GetHoldTypesById", (id,)) +# for hold in cursor.stored_results(): +# hold_type = hold.fetchone() +# +# if not hold_type: +# return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404 +# +# # Update the hold type +# # cursor.execute("UPDATE hold_types SET hold_type = %s WHERE hold_type_id = %s", (new_hold_type, id)) +# cursor.callproc("UpdateHoldTypeById", (id,new_hold_type)) +# connection.commit() +# return jsonify(ResponseHandler.update_success('Hold Type')) +# +# except mysql.connector.Error as e: +# connection.rollback() +# return jsonify(ResponseHandler.update_failure('Hold Type')), 500 +# finally: +# cursor.close() +# connection.close() @app.route('/update_hold_type/', methods=['GET', 'POST']) @@ -3324,7 +4349,7 @@ def delete_hold_type(id): cursor.callproc("GetHoldTypesById", (id,)) for hold in cursor.stored_results(): hold_type = hold.fetchone() - log_action("Delete hold type", f"User {current_user.id} Delete hold type'{ hold_type}'") + LogHelper.log_action("Delete hold type", f"User {current_user.id} Delete hold type'{ hold_type}'") if not hold_type: return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404 @@ -3416,7 +4441,7 @@ def submit_work_order(): sd_against_gst = request.form['sd_against_gst'] final_total = request.form['final_total'] tds_of_gst = request.form['tds_of_gst'] - log_action("Submit Work Order", f"User {current_user.id} Submit Work Order'{ work_order_type}'") + LogHelper.log_action("Submit Work Order", f"User {current_user.id} Submit Work Order'{ work_order_type}'") # print("Good Morning How are U") connection = config.get_db_connection() cursor = connection.cursor(dictionary=True) @@ -3454,7 +4479,7 @@ def delete_work_order(id): connection.commit() cursor.close() connection.close() - log_action("delete Work Order", f"User {current_user.id} delete Work Order'{ id}'") + LogHelper.log_action("delete Work Order", f"User {current_user.id} delete Work Order'{ id}'") return jsonify({'success': True}) @@ -3511,7 +4536,6 @@ def success(): return "Work Order Submitted Successfully!" -import logging logging.basicConfig(level=logging.DEBUG) @@ -3534,7 +4558,7 @@ def add_purchase_order(): GST_Amount = request.form.get('GST_Amount') TDS = request.form.get('TDS') final_amount = request.form.get('final_amount') - log_action("Add purchase order", f"User {current_user.id} Added puirchase Order'{ purchase_order_no}'") + LogHelper.log_action("Add purchase order", f"User {current_user.id} Added puirchase Order'{ purchase_order_no}'") # Insert into database insert_query = """ INSERT INTO purchase_order ( @@ -3579,7 +4603,7 @@ def delete_purchase(id): connection.commit() cursor.close() connection.close() - log_action("Delete purchase order", f"User {current_user.id} Deleted puirchase Order'{ id}'") + LogHelper.log_action("Delete purchase order", f"User {current_user.id} Deleted puirchase Order'{ id}'") return render_template(('add_purchase_order.html')) @@ -3615,7 +4639,7 @@ def update_purchase(id): connection.commit() cursor.close() connection.close() - log_action("Delete purchase order", f"User {current_user.id} Deleted puirchase Order'{ id}'") + LogHelper.log_action("Delete purchase order", f"User {current_user.id} Deleted puirchase Order'{ id}'") return redirect(url_for('show_purchase_orders')) # Show edit form @@ -3730,10 +4754,6 @@ def work_order_report(): return render_template('work_order_report.html') -from flask import request, jsonify, send_file -import pandas as pd -import os -import config # make sure your DB connection is imported correctly # ✅ Vendor Name Search (for Select2) @@ -3796,12 +4816,6 @@ def get_work_order_data(): return jsonify(data) -from flask import request, send_file -import pandas as pd -import os -from openpyxl import load_workbook -from openpyxl.styles import Font, Alignment -from openpyxl.utils import get_column_letter @app.route('/download_work_order_report') @@ -3946,7 +4960,7 @@ def download_purchase_order_report(): if purchase_order_no == "null": purchase_order_no = None - log_action("Download purchase order", f"User {current_user.id} Download puirchase Order'{ purchase_order_no}'") + LogHelper.log_action("Download purchase order", f"User {current_user.id} Download puirchase Order'{ purchase_order_no}'") query = "SELECT * FROM purchase_order WHERE 1=1" params = [] diff --git a/templates/add_state.html b/templates/add_state.html index 11857c9..4e499c7 100644 --- a/templates/add_state.html +++ b/templates/add_state.html @@ -29,6 +29,9 @@

Display States

+ diff --git a/templates/base.html b/templates/base.html index aa8b235..bb52864 100644 --- a/templates/base.html +++ b/templates/base.html @@ -24,7 +24,7 @@ diff --git a/templates/index.html b/templates/index.html index dd31318..b6e633b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -41,7 +41,7 @@ diff --git a/templates/uploadExcelFile.html b/templates/uploadExcelFile.html index b8569b9..47c3a5b 100644 --- a/templates/uploadExcelFile.html +++ b/templates/uploadExcelFile.html @@ -11,7 +11,7 @@

Upload Excel File

-


State ID