From a0123a8f43717057c1850e6ecceffdb1860f0edb Mon Sep 17 00:00:00 2001 From: yoga-agoy Date: Mon, 6 Jul 2026 18:12:16 +0530 Subject: [PATCH] added updated by in all view options and added history --- .../gcc_manpower/config/DataSourceConfig.java | 3 +- .../controller/HomeController.java | 119 +++++++--- .../GccServicePaymentTrackerController.java | 49 +++- .../GccServicePaymentTrackerService.java | 60 ++++- .../gcc_manpower/service/AppUserService.java | 155 ++++++++++--- .../gccservicepaymenttracker/dashboard.html | 207 ++++++++++++++++- src/main/resources/templates/home/home.html | 210 +++++++++++++++--- .../templates/home/view-login-details.html | 75 ++++++- 8 files changed, 770 insertions(+), 108 deletions(-) diff --git a/src/main/java/com/gcc_manpower/config/DataSourceConfig.java b/src/main/java/com/gcc_manpower/config/DataSourceConfig.java index baa0aae..67ecdc4 100644 --- a/src/main/java/com/gcc_manpower/config/DataSourceConfig.java +++ b/src/main/java/com/gcc_manpower/config/DataSourceConfig.java @@ -14,8 +14,7 @@ public class DataSourceConfig { private static String dbpassword = "root"; // AWS - // private static String host = - // "gcc-facial-db-instance-1.cf48eqcciziq.ap-south-1.rds.amazonaws.com:3306"; + // private static String host = "gcc-facial-db-instance-1.cf48eqcciziq.ap-south-1.rds.amazonaws.com:3306"; // private static String dbpassword = "gcc-facial-password"; @Bean(name = "mysqlManpowerDataSource") diff --git a/src/main/java/com/gcc_manpower/controller/HomeController.java b/src/main/java/com/gcc_manpower/controller/HomeController.java index b6eedca..fd5f292 100644 --- a/src/main/java/com/gcc_manpower/controller/HomeController.java +++ b/src/main/java/com/gcc_manpower/controller/HomeController.java @@ -15,6 +15,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; @Controller @RequestMapping("/") @@ -23,6 +26,9 @@ public class HomeController { @Autowired private AppUserService appUserService; + @Value("${upload.path:gcc_manpower_uploads}") + private String uploadPathConfig; + private void addSummaryStats(Model model, List> vendorDetails) { int totalVendors = vendorDetails.size(); int totalTenderCount = appUserService.getOverallTenderCount(); @@ -247,7 +253,7 @@ public class HomeController { } try { - boolean success = appUserService.addEmployee(payload); + boolean success = appUserService.addEmployee(payload, usrName); if (success) { response.put("success", true); response.put("message", "Employee registered successfully!"); @@ -279,7 +285,7 @@ public class HomeController { } try { - boolean success = appUserService.updateEmployee(payload); + boolean success = appUserService.updateEmployee(payload, usrName); if (success) { response.put("success", true); response.put("message", "Employee updated successfully!"); @@ -297,7 +303,18 @@ public class HomeController { @PostMapping("/api/vendor/add-work-order") @ResponseBody public Map addVendorWorkOrder( - @RequestBody Map payload, + @RequestParam("vendorType") String vendorType, + @RequestParam(value = "vendorId", required = false) String vendorIdStr, + @RequestParam(value = "vendorName", required = false) String vendorName, + @RequestParam("description") String description, + @RequestParam("contractorName") String contractorName, + @RequestParam("fileNo") String fileNo, + @RequestParam("subject") String subject, + @RequestParam("issueDate") String issueDate, + @RequestParam("endDate") String endDate, + @RequestParam("totalEmployees") String totalEmployees, + @RequestParam(value = "orderby", required = false) Integer orderby, + @RequestParam(value = "file", required = false) MultipartFile file, HttpSession session) { Map response = new HashMap<>(); String usrName = (String) session.getAttribute("usrName"); @@ -311,25 +328,6 @@ public class HomeController { } try { - String vendorType = (String) payload.get("vendorType"); - String vendorIdStr = (String) payload.get("vendorId"); - String vendorName = (String) payload.get("vendorName"); - String description = (String) payload.get("description"); - String contractorName = (String) payload.get("contractorName"); - String fileNo = (String) payload.get("fileNo"); - String subject = (String) payload.get("subject"); - String issueDate = (String) payload.get("issueDate"); - String endDate = (String) payload.get("endDate"); - String totalEmployees = (String) payload.get("totalEmployees"); - - Integer orderby = null; - if (payload.get("orderby") != null) { - try { - orderby = ((Number) payload.get("orderby")).intValue(); - } catch (Exception ignored) { - } - } - // Convert to uppercase for Contractor Name and File No if (contractorName != null) { contractorName = contractorName.toUpperCase(); @@ -378,6 +376,35 @@ public class HomeController { } } + // Save upload file (mandatory validation) + if (file == null || file.isEmpty()) { + response.put("success", false); + response.put("error", "Work order PDF file is required."); + return response; + } + + File uploadDir = new File(uploadPathConfig).getAbsoluteFile(); + if (!uploadDir.exists()) { + uploadDir.mkdirs(); + } + String originalFilename = file.getOriginalFilename(); + String nameWithoutExt = ""; + String ext = ".pdf"; + if (originalFilename != null && originalFilename.contains(".")) { + int dotIdx = originalFilename.lastIndexOf("."); + nameWithoutExt = originalFilename.substring(0, dotIdx); + ext = originalFilename.substring(dotIdx); + } else { + nameWithoutExt = originalFilename != null ? originalFilename : "file"; + } + nameWithoutExt = nameWithoutExt.replaceAll("[^a-zA-Z0-9-_]", "_"); + String savedFilename = nameWithoutExt + "_" + System.currentTimeMillis() + ext; + + File destFile = new File(uploadDir, savedFilename).getAbsoluteFile(); + file.transferTo(destFile); + + String fileWebUrl = "/gcc_manpower_uploads/" + savedFilename; + // Insert into vendor_details_work_order boolean success = appUserService.addVendorWorkOrder( String.valueOf(finalVendorId), @@ -388,7 +415,9 @@ public class HomeController { issueDate, endDate, totalEmployees, - orderby); + orderby, + fileWebUrl, + usrName); if (success) { // If it's an existing vendor, add total_employees to vendor_details @@ -444,7 +473,7 @@ public class HomeController { return response; } - boolean success = appUserService.deleteEmployee(empCode); + boolean success = appUserService.deleteEmployee(empCode, usrName); if (success) { response.put("success", true); response.put("message", "Employee deleted successfully!"); @@ -499,6 +528,46 @@ public class HomeController { return response; } + @PostMapping("/api/vendor/delete-work-order") + @ResponseBody + public Map deleteWorkOrder( + @RequestBody Map payload, + HttpSession session) { + Map response = new HashMap<>(); + String usrName = (String) session.getAttribute("usrName"); + String usrRole = (String) session.getAttribute("usrRole"); + boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); + + if (usrName == null || !isAdmin) { + response.put("success", false); + response.put("error", "Unauthorized! Admin access required."); + return response; + } + + try { + Object idObj = payload.get("workOrderId"); + if (idObj == null) { + response.put("success", false); + response.put("error", "Work order ID is required."); + return response; + } + + String workOrderId = String.valueOf(idObj); + boolean success = appUserService.deleteWorkOrder(workOrderId, usrName); + if (success) { + response.put("success", true); + response.put("message", "Work order deleted successfully!"); + } else { + response.put("success", false); + response.put("error", "Failed to delete work order or record not found."); + } + } catch (Exception e) { + response.put("success", false); + response.put("error", "Error: " + e.getMessage()); + } + return response; + } + @GetMapping("/api/vendor-employees-all") @ResponseBody public List> getVendorEmployeesAll( @@ -530,7 +599,7 @@ public class HomeController { return response; } - boolean success = appUserService.restoreEmployee(empCode); + boolean success = appUserService.restoreEmployee(empCode, usrName); if (success) { response.put("success", true); response.put("message", "Employee restored successfully!"); diff --git a/src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java b/src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java index 671ddef..2a97c23 100644 --- a/src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java +++ b/src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java @@ -124,14 +124,20 @@ public class GccServicePaymentTrackerController { HttpSession session) { Map response = new HashMap<>(); - String username = (String) session.getAttribute("usrName"); + String username = (String) session.getAttribute("pay_usrName"); + if (username == null) { + username = (String) session.getAttribute("usrName"); + } if (username == null) { response.put("success", false); response.put("error", "Unauthorized access! Please log in."); return response; } - String usrRole = (String) session.getAttribute("usrRole"); + String usrRole = (String) session.getAttribute("pay_usrRole"); + if (usrRole == null) { + usrRole = (String) session.getAttribute("usrRole"); + } if (!"ADMIN".equalsIgnoreCase(usrRole)) { response.put("success", false); response.put("error", "Access denied! Only admins can save or update records."); @@ -139,7 +145,7 @@ public class GccServicePaymentTrackerController { } try { - boolean saved = paymentTrackerService.saveOrUpdateDetail(payload); + boolean saved = paymentTrackerService.saveOrUpdateDetail(payload, username); response.put("success", saved); if (!saved) { response.put("error", "Failed to save details to database."); @@ -159,14 +165,20 @@ public class GccServicePaymentTrackerController { HttpSession session) { Map response = new HashMap<>(); - String username = (String) session.getAttribute("usrName"); + String username = (String) session.getAttribute("pay_usrName"); + if (username == null) { + username = (String) session.getAttribute("usrName"); + } if (username == null) { response.put("success", false); response.put("error", "Unauthorized access! Please log in."); return response; } - String usrRole = (String) session.getAttribute("usrRole"); + String usrRole = (String) session.getAttribute("pay_usrRole"); + if (usrRole == null) { + usrRole = (String) session.getAttribute("usrRole"); + } if (!"ADMIN".equalsIgnoreCase(usrRole)) { response.put("success", false); response.put("error", "Access denied! Only admins can delete records."); @@ -193,14 +205,20 @@ public class GccServicePaymentTrackerController { @RequestParam("file") MultipartFile file, HttpSession session) { Map response = new HashMap<>(); - String username = (String) session.getAttribute("usrName"); + String username = (String) session.getAttribute("pay_usrName"); + if (username == null) { + username = (String) session.getAttribute("usrName"); + } if (username == null) { response.put("success", false); response.put("error", "Unauthorized access! Please log in."); return response; } - String usrRole = (String) session.getAttribute("usrRole"); + String usrRole = (String) session.getAttribute("pay_usrRole"); + if (usrRole == null) { + usrRole = (String) session.getAttribute("usrRole"); + } if (!"ADMIN".equalsIgnoreCase(usrRole)) { response.put("success", false); response.put("error", "Access denied! Only admins can upload files."); @@ -261,4 +279,21 @@ public class GccServicePaymentTrackerController { public List> getStatusList() { return paymentTrackerService.getStatusList(); } + + @GetMapping("/api/history") + @ResponseBody + public ResponseEntity>> getHistory( + @RequestParam("id") int id, + HttpSession session) { + String username = (String) session.getAttribute("pay_usrName"); + if (username == null) { + username = (String) session.getAttribute("usrName"); + } + if (username == null) { + return ResponseEntity.status(401).body(null); + } + return ResponseEntity.ok() + .cacheControl(CacheControl.noCache().mustRevalidate()) + .body(paymentTrackerService.getHistory(id)); + } } diff --git a/src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java b/src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java index b6d6646..10426fb 100644 --- a/src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java +++ b/src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import com.gcc_manpower.service.AppUserService; import javax.sql.DataSource; import jakarta.annotation.PostConstruct; import java.util.ArrayList; @@ -16,6 +17,9 @@ public class GccServicePaymentTrackerService { private JdbcTemplate jdbcTemplate; + @Autowired + private AppUserService appUserService; + @Autowired public void setDataSource(@Qualifier("mysqlPaymentTrackingServiceDataSource") DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); @@ -74,11 +78,13 @@ public class GccServicePaymentTrackerService { "SELECT p.*, " + "t.type_name as resolved_type, " + "v.vendor_name as resolved_vendor, " - + "s.status_name as resolved_status " + + "s.status_name as resolved_status, " + + "u.usr_name as updated_by_name " + "FROM payment_tracking_details p " + "LEFT JOIN type_list t ON p.type REGEXP '^[0-9]+$' AND t.id = p.type " + "LEFT JOIN vendors_list v ON p.vendor_name REGEXP '^[0-9]+$' AND v.id = p.vendor_name " + "LEFT JOIN status_list s ON p.status REGEXP '^[0-9]+$' AND s.id = p.status " + + "LEFT JOIN gcc_manpower.app_user u ON (p.updated_by = CAST(u.usr_id AS CHAR) OR p.updated_by = u.usr_name) " + "WHERE p.is_active = 1 AND p.is_delete = 0"); List params = new ArrayList<>(); @@ -159,7 +165,12 @@ public class GccServicePaymentTrackerService { return null; } - public boolean saveOrUpdateDetail(Map payload) { + public boolean saveOrUpdateDetail(Map payload, String updatedByUsername) { + String userId = appUserService.getUserIdByName(updatedByUsername); + if (userId == null) { + userId = updatedByUsername; + } + Object idObj = payload.get("id"); java.sql.Date receivedDate = parseDate(payload.get("received_date")); String vendorName = (String) payload.get("vendor_name"); @@ -189,8 +200,8 @@ public class GccServicePaymentTrackerService { String insertHistorySql = "INSERT INTO payment_tracking_details_history (" + "detail_id, received_date, vendor_name, subject, amount, invoice_no, receiptno, " + "invoice_date, type, physical_file_no, remarks, tat, status, followup, myfollowup, " - + "is_active, is_delete, order_by, file_path" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + "is_active, is_delete, order_by, file_path, cdate, updated_at, updated_by" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; jdbcTemplate.update(insertHistorySql, old.get("id"), old.get("received_date"), @@ -210,7 +221,10 @@ public class GccServicePaymentTrackerService { old.get("is_active"), old.get("is_delete"), old.get("order_by"), - old.get("file_path")); + old.get("file_path"), + old.get("cdate"), + new java.sql.Timestamp(System.currentTimeMillis()), + userId); } } catch (Exception e) { System.err.println("Error saving history for payment tracking detail ID " + id + ": " + e.getMessage()); @@ -219,12 +233,14 @@ public class GccServicePaymentTrackerService { String sql = "UPDATE payment_tracking_details SET " + "received_date = ?, vendor_name = ?, subject = ?, amount = ?, invoice_no = ?, " + "receiptno = ?, invoice_date = ?, type = ?, physical_file_no = ?, remarks = ?, " - + "tat = ?, status = ?, followup = ?, myfollowup = ?, order_by = ?, file_path = ? " + + "tat = ?, status = ?, followup = ?, myfollowup = ?, order_by = ?, file_path = ?, " + + "updated_by = ?, updated_date = ? " + "WHERE id = ? AND is_active = 1 AND is_delete = 0"; try { int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo, receiptNo, invoiceDate, type, physicalFileNo, remarks, - tat, status, followup, myfollowup, orderBy, filePath, id); + tat, status, followup, myfollowup, orderBy, filePath, + userId, new java.sql.Timestamp(System.currentTimeMillis()), id); return rows > 0; } catch (Exception e) { System.err.println("Error updating payment tracking detail ID " + id + ": " + e.getMessage()); @@ -235,12 +251,15 @@ public class GccServicePaymentTrackerService { String sql = "INSERT INTO payment_tracking_details (" + "received_date, vendor_name, subject, amount, invoice_no, receiptno, " + "invoice_date, type, physical_file_no, remarks, tat, status, " - + "followup, myfollowup, is_active, is_delete, order_by, file_path" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?)"; + + "followup, myfollowup, is_active, is_delete, order_by, file_path, " + + "updated_by, updated_date, cdate" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?, ?, ?, ?)"; try { + java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis()); int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo, receiptNo, invoiceDate, type, physicalFileNo, remarks, - tat, status, followup, myfollowup, orderBy, filePath); + tat, status, followup, myfollowup, orderBy, filePath, + userId, now, now); return rows > 0; } catch (Exception e) { System.err.println("Error saving payment tracking detail: " + e.getMessage()); @@ -344,4 +363,25 @@ public class GccServicePaymentTrackerService { return new ArrayList<>(); } } + + public List> getHistory(int detailId) { + String sql = "SELECT h.*, " + + "t.type_name as resolved_type, " + + "v.vendor_name as resolved_vendor, " + + "s.status_name as resolved_status, " + + "u.usr_name as updated_by_name " + + "FROM payment_tracking_details_history h " + + "LEFT JOIN type_list t ON h.type REGEXP '^[0-9]+$' AND t.id = h.type " + + "LEFT JOIN vendors_list v ON h.vendor_name REGEXP '^[0-9]+$' AND v.id = h.vendor_name " + + "LEFT JOIN status_list s ON h.status REGEXP '^[0-9]+$' AND s.id = h.status " + + "LEFT JOIN gcc_manpower.app_user u ON (h.updated_by = CAST(u.usr_id AS CHAR) OR h.updated_by = u.usr_name) " + + "WHERE h.detail_id = ? " + + "ORDER BY h.updated_at DESC"; + try { + return jdbcTemplate.queryForList(sql, detailId); + } catch (Exception e) { + System.err.println("Error fetching payment tracking history for detail ID " + detailId + ": " + e.getMessage()); + return new ArrayList<>(); + } + } } diff --git a/src/main/java/com/gcc_manpower/service/AppUserService.java b/src/main/java/com/gcc_manpower/service/AppUserService.java index 58a9c2f..e3198af 100644 --- a/src/main/java/com/gcc_manpower/service/AppUserService.java +++ b/src/main/java/com/gcc_manpower/service/AppUserService.java @@ -25,6 +25,18 @@ public class AppUserService { this.jdbcManpowerTemplate = new JdbcTemplate(manpowerDataSource); } + @PostConstruct + public void init() { + try { + jdbcManpowerTemplate + .execute("ALTER TABLE app_user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci"); + System.out.println( + "Collation mismatch auto-resolved: app_user converted to utf8mb4_0900_ai_ci successfully."); + } catch (Exception e) { + System.err.println("Note: Could not alter app_user collation automatically: " + e.getMessage()); + } + } + public Map authenticate(String username, String password) { Map result = new HashMap<>(); @@ -106,8 +118,11 @@ public class AppUserService { " COALESCE(des.File_no, '-') AS File_no, " + " COALESCE(des.work_order_issue_date, '-') AS work_order_issue_date, " + " COALESCE(des.work_order_end_date, '-') AS work_order_end_date, " + - " des.file_path " + + " des.file_path, " + + " des.updated_at, " + + " COALESCE(u.usr_name, des.updated_by) AS updated_by_name " + "FROM employee_workorder_details des " + + "LEFT JOIN app_user u ON (des.updated_by = CAST(u.usr_id AS CHAR) OR des.updated_by = u.usr_name) " + "WHERE des.vid = ? AND des.isactive = 1 AND des.isdelete = 0 " + "ORDER BY des.orderby, des.designation"; try { @@ -140,6 +155,7 @@ public class AppUserService { return new ArrayList<>(); } } + public int getOverallTenderCount() { String sql = "SELECT SUM(total_employees) FROM vendor_details WHERE isactive = 1 AND isdelete = 0"; try { @@ -179,7 +195,7 @@ public class AppUserService { } public List> getActiveDesignations() { - String sql = "SELECT id, designation, File_no FROM employee_workorder_details WHERE isactive = 1 AND isdelete = 0 ORDER BY orderby, designation"; + String sql = "SELECT id, designation, File_no, file_path FROM employee_workorder_details WHERE isactive = 1 AND isdelete = 0 ORDER BY orderby, designation"; try { return jdbcManpowerTemplate.queryForList(sql); } catch (Exception e) { @@ -222,6 +238,8 @@ public class AppUserService { " e.isdelete, " + " e.joined_date, " + " e.cdate, " + + " e.updated_at, " + + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(des.designation, e.designation) AS designation, " + @@ -236,6 +254,8 @@ public class AppUserService { + "LEFT JOIN employee_location_details loc ON (e.location = CAST(loc.id AS CHAR) OR e.location = loc.location_name) " + + "LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) " + + "WHERE (e.vendor_id = ? OR e.vendor_id = ?) AND e.isactive = 1 AND e.isdelete = 0 " + "ORDER BY e.name"; try { @@ -258,13 +278,15 @@ public class AppUserService { " e.isdelete, " + " e.joined_date, " + " e.cdate, " + + " e.updated_at, " + + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(des.designation, e.designation) AS designation, " + " COALESCE(loc.location_name, e.location) AS location, " + " COALESCE(des.File_no, '-') AS work_order, " + - " COALESCE(des.work_order_issue_date, '-') AS issue_date, " + - " COALESCE(des.work_order_end_date, '-') AS end_date " + + " COALESCE(des.work_order_issue_date, '-') AS work_order_issue_date, " + + " COALESCE(des.work_order_end_date, '-') AS work_order_end_date " + "FROM employee_details e " + "LEFT JOIN vendor_details v ON (e.vendor_id = CAST(v.id AS CHAR) OR e.vendor_id = v.vendor_name) " + "LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) " + @@ -272,6 +294,7 @@ public class AppUserService { + "LEFT JOIN employee_location_details loc ON (e.location = CAST(loc.id AS CHAR) OR e.location = loc.location_name) " + + "LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) " + "WHERE e.isactive = 1 AND e.isdelete = 0 " + "ORDER BY e.name"; try { @@ -298,7 +321,7 @@ public class AppUserService { return null; } - public boolean addEmployee(Map emp) { + public boolean addEmployee(Map emp, String updatedByUsername) { String checkSql = "SELECT COUNT(*) FROM employee_details WHERE emp_code = ? AND isdelete = 0"; try { Integer count = jdbcManpowerTemplate.queryForObject(checkSql, Integer.class, emp.get("emp_code")); @@ -350,9 +373,9 @@ public class AppUserService { } } - String sql = "INSERT INTO employee_details (emp_code, name, email, mobile, user_type, designation, location, vendor_id, joined_date, cdate, isactive, isdelete) " + String sql = "INSERT INTO employee_details (emp_code, name, email, mobile, user_type, designation, location, vendor_id, joined_date, cdate, updated_at, updated_by, isactive, isdelete) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), 1, 0)"; + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), ?, 1, 0)"; try { int updateRows = jdbcManpowerTemplate.update(sql, emp.get("emp_code"), @@ -363,7 +386,8 @@ public class AppUserService { emp.get("designation"), emp.get("location"), emp.get("vendor_id"), - emp.get("joined_date")); + emp.get("joined_date"), + getUserIdByName(updatedByUsername)); if (updateRows > 0) { recalculateDesignationManpower(String.valueOf(emp.get("designation")), String.valueOf(emp.get("vendor_id"))); @@ -376,9 +400,9 @@ public class AppUserService { } } - public boolean updateEmployee(Map emp) { + public boolean updateEmployee(Map emp, String updatedByUsername) { String empCode = (String) emp.get("emp_code"); - String selectSql = "SELECT emp_code, name, email, mobile, user_type, designation, location, vendor_id, isactive, isdelete " + String selectSql = "SELECT emp_code, name, email, mobile, user_type, designation, location, vendor_id, isactive, isdelete, updated_by, cdate " + "FROM employee_details WHERE emp_code = ? AND isdelete = 0"; List> oldList; @@ -438,9 +462,9 @@ public class AppUserService { } } - String historySql = "INSERT INTO employee_details_update_history (emp_code, name, email, mobile, user_type, designation, location, vendor_id, updated_date, isactive, isdelete) " + String historySql = "INSERT INTO employee_details_update_history (emp_code, name, email, mobile, user_type, designation, location, vendor_id, updated_date, isactive, isdelete, updated_by, cdate) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, ?)"; + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, ?, ?, ?)"; try { jdbcManpowerTemplate.update(historySql, old.get("emp_code"), @@ -452,13 +476,15 @@ public class AppUserService { old.get("location"), old.get("vendor_id"), old.get("isactive"), - old.get("isdelete")); + old.get("isdelete"), + old.get("updated_by"), + old.get("cdate")); } catch (Exception e) { System.err.println("Error inserting history record: " + e.getMessage()); return false; } - String updateSql = "UPDATE employee_details SET name = ?, email = ?, mobile = ?, user_type = ?, designation = ?, location = ?, vendor_id = ?, joined_date = ? " + String updateSql = "UPDATE employee_details SET name = ?, email = ?, mobile = ?, user_type = ?, designation = ?, location = ?, vendor_id = ?, joined_date = ?, updated_at = NOW(), updated_by = ? " + "WHERE emp_code = ? AND isdelete = 0"; try { @@ -471,6 +497,7 @@ public class AppUserService { emp.get("location"), emp.get("vendor_id"), emp.get("joined_date"), + getUserIdByName(updatedByUsername), empCode); if (rows > 0) { recalculateDesignationManpower(String.valueOf(old.get("designation")), @@ -500,8 +527,10 @@ public class AppUserService { "FROM employee_details_update_history h " + "LEFT JOIN vendor_details v ON (h.vendor_id = CAST(v.id AS CHAR) OR h.vendor_id = v.vendor_name) " + "LEFT JOIN user_type_details ut ON (h.user_type = CAST(ut.id AS CHAR) OR h.user_type = ut.usertype) " + - "LEFT JOIN employee_workorder_details des ON (h.designation = CAST(des.id AS CHAR) OR h.designation = des.designation) " + - "LEFT JOIN employee_location_details loc ON (h.location = CAST(loc.id AS CHAR) OR h.location = loc.location_name) " + + "LEFT JOIN employee_workorder_details des ON (h.designation = CAST(des.id AS CHAR) OR h.designation = des.designation) " + + + "LEFT JOIN employee_location_details loc ON (h.location = CAST(loc.id AS CHAR) OR h.location = loc.location_name) " + + "WHERE h.emp_code = ? " + "ORDER BY h.updated_date DESC"; try { @@ -566,7 +595,8 @@ public class AppUserService { } public boolean addVendorWorkOrder(String vendorId, String description, String contractorName, String fileNo, - String subject, String issueDate, String endDate, String totalEmployees, Integer orderby) { + String subject, String issueDate, String endDate, String totalEmployees, Integer orderby, String filePath, + String updatedByUsername) { if (orderby == null) { try { Integer maxOrder = jdbcManpowerTemplate @@ -576,9 +606,9 @@ public class AppUserService { orderby = 1; } } - String sql = "INSERT INTO employee_workorder_details (designation, vid, tender_manpower_designation_wise, available_manpower, vacant_count, contract_name, File_no, subject, work_order_issue_date, work_order_end_date, orderby, cdate, isactive, isdelete) " + String sql = "INSERT INTO employee_workorder_details (designation, vid, tender_manpower_designation_wise, available_manpower, vacant_count, contract_name, File_no, subject, work_order_issue_date, work_order_end_date, orderby, cdate, updated_at, updated_by, isactive, isdelete, file_path) " + - "VALUES (?, ?, ?, '0', ?, ?, ?, ?, ?, ?, ?, NOW(), 1, 0)"; + "VALUES (?, ?, ?, '0', ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), ?, 1, 0, ?)"; try { int rows = jdbcManpowerTemplate.update(sql, subject, // designation @@ -590,7 +620,9 @@ public class AppUserService { subject, // subject issueDate, // work_order_issue_date endDate, // work_order_end_date - orderby // orderby + orderby, // orderby + getUserIdByName(updatedByUsername), // updated_by + filePath // file_path ); if (rows > 0) { // Recalculate designation manpower (handles available_manpower and @@ -682,7 +714,7 @@ public class AppUserService { } } - public boolean deleteEmployee(String empCode) { + public boolean deleteEmployee(String empCode, String updatedByUsername) { String selectSql = "SELECT designation, vendor_id FROM employee_details WHERE emp_code = ? AND isdelete = 0"; try { List> list = jdbcManpowerTemplate.queryForList(selectSql, empCode); @@ -693,8 +725,8 @@ public class AppUserService { String designation = String.valueOf(emp.get("designation")); String vendorId = String.valueOf(emp.get("vendor_id")); - String updateSql = "UPDATE employee_details SET isactive = 0, isdelete = 1 WHERE emp_code = ?"; - int rows = jdbcManpowerTemplate.update(updateSql, empCode); + String updateSql = "UPDATE employee_details SET isactive = 0, isdelete = 1, updated_at = NOW(), updated_by = ? WHERE emp_code = ?"; + int rows = jdbcManpowerTemplate.update(updateSql, getUserIdByName(updatedByUsername), empCode); if (rows > 0) { recalculateDesignationManpower(designation, vendorId); return true; @@ -757,6 +789,8 @@ public class AppUserService { " e.isdelete, " + " e.joined_date, " + " e.cdate, " + + " e.updated_at, " + + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(des.designation, e.designation) AS designation, " + @@ -769,6 +803,8 @@ public class AppUserService { + "LEFT JOIN employee_location_details loc ON (e.location = CAST(loc.id AS CHAR) OR e.location = loc.location_name) " + + "LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) " + + "WHERE e.vendor_id = ? OR e.vendor_id = ? " + "ORDER BY e.name"; try { @@ -779,7 +815,7 @@ public class AppUserService { } } - public boolean restoreEmployee(String empCode) { + public boolean restoreEmployee(String empCode, String updatedByUsername) { String selectSql = "SELECT designation, vendor_id FROM employee_details WHERE emp_code = ?"; try { List> list = jdbcManpowerTemplate.queryForList(selectSql, empCode); @@ -790,8 +826,8 @@ public class AppUserService { String designation = String.valueOf(emp.get("designation")); String vendorId = String.valueOf(emp.get("vendor_id")); - String updateSql = "UPDATE employee_details SET isactive = 1, isdelete = 0 WHERE emp_code = ?"; - int rows = jdbcManpowerTemplate.update(updateSql, empCode); + String updateSql = "UPDATE employee_details SET isactive = 1, isdelete = 0, updated_at = NOW(), updated_by = ? WHERE emp_code = ?"; + int rows = jdbcManpowerTemplate.update(updateSql, getUserIdByName(updatedByUsername), empCode); if (rows > 0) { recalculateDesignationManpower(designation, vendorId); return true; @@ -814,6 +850,8 @@ public class AppUserService { " e.isdelete, " + " e.joined_date, " + " e.cdate, " + + " e.updated_at, " + + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(des.designation, e.designation) AS designation, " + @@ -826,6 +864,8 @@ public class AppUserService { + "LEFT JOIN employee_location_details loc ON (e.location = CAST(loc.id AS CHAR) OR e.location = loc.location_name) " + + "LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) " + + "ORDER BY e.name"; try { return jdbcManpowerTemplate.queryForList(sql); @@ -834,4 +874,67 @@ public class AppUserService { return new ArrayList<>(); } } + + public String getUserIdByName(String username) { + if (username == null || username.trim().isEmpty()) + return null; + try { + Integer id = jdbcManpowerTemplate.queryForObject( + "SELECT usr_id FROM app_user WHERE usr_name = ?", Integer.class, username); + return id != null ? String.valueOf(id) : null; + } catch (Exception e) { + System.err.println("Error resolving user ID for name " + username + ": " + e.getMessage()); + return null; + } + } + + public boolean deleteWorkOrder(String workOrderId, String updatedByUsername) { + try { + // 1. Get the details of the work order before deleting + String selectSql = "SELECT vid, tender_manpower_designation_wise, designation FROM employee_workorder_details WHERE id = ? AND isdelete = 0"; + List> list = jdbcManpowerTemplate.queryForList(selectSql, workOrderId); + if (list == null || list.isEmpty()) { + return false; + } + Map wo = list.get(0); + String vendorId = String.valueOf(wo.get("vid")); + String tenderStr = (String) wo.get("tender_manpower_designation_wise"); + String designationName = (String) wo.get("designation"); + + String userId = getUserIdByName(updatedByUsername); + + // 2. Soft-delete the work order in employee_workorder_details, reset counts and + // record updater + String deleteSql = "UPDATE employee_workorder_details SET isactive = 0, isdelete = 1, available_manpower = '0', vacant_count = tender_manpower_designation_wise, updated_at = NOW(), updated_by = ? WHERE id = ?"; + int rows = jdbcManpowerTemplate.update(deleteSql, userId, workOrderId); + + if (rows > 0) { + // 3. Subtract its tender count from vendor's total_employees in vendor_details + int tenderCount = 0; + if (tenderStr != null) { + try { + tenderCount = Integer.parseInt(tenderStr.trim()); + } catch (NumberFormatException ignored) { + } + } + if (tenderCount > 0) { + updateVendorTotalEmployees(vendorId, -tenderCount); + } + + // 4. Soft-delete employees under this designation/work order for this vendor + // and record updater + String updateEmployeesSql = "UPDATE employee_details SET isactive = 0, isdelete = 1, updated_at = NOW(), updated_by = ? " + + + "WHERE (vendor_id = ? OR vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) " + + " AND (designation = ? OR designation = ?)"; + jdbcManpowerTemplate.update(updateEmployeesSql, userId, vendorId, vendorId, workOrderId, + designationName); + + return true; + } + } catch (Exception e) { + System.err.println("Error deleting work order: " + e.getMessage()); + } + return false; + } } diff --git a/src/main/resources/templates/gccservicepaymenttracker/dashboard.html b/src/main/resources/templates/gccservicepaymenttracker/dashboard.html index 30690c7..6925aff 100644 --- a/src/main/resources/templates/gccservicepaymenttracker/dashboard.html +++ b/src/main/resources/templates/gccservicepaymenttracker/dashboard.html @@ -1270,8 +1270,9 @@ Age of Bill (Days) Status (Pending / Completed) Remarks - Updated on - Actions + Last Updated At + Last Updated By + Actions @@ -1482,6 +1483,50 @@ + + + @@ -1493,6 +1538,7 @@ let activeCategory = 'all'; let allRecords = []; let recordModalObj = null; + let historyModalObj = null; let currentPage = 1; let pageSize = 20; @@ -1511,6 +1557,7 @@ document.addEventListener('DOMContentLoaded', async () => { recordModalObj = new bootstrap.Modal(document.getElementById('recordModal')); + historyModalObj = new bootstrap.Modal(document.getElementById('historyModal')); // initDatePickers(); await loadTypesAndVendors(); loadCounts(); @@ -1972,13 +2019,15 @@ const formatDateTime = dateTimeStr => { if (!dateTimeStr) return '-'; try { - const tParts = dateTimeStr.split('T'); - const datePart = tParts[0]; - const timePart = tParts.length > 1 ? tParts[1].split('.')[0] : ''; - const dateParts = datePart.split('-'); - if (dateParts.length === 3) { - const formattedDate = `${dateParts[2]}/${dateParts[1]}/${dateParts[0]}`; - return timePart ? `${formattedDate} ${timePart}` : formattedDate; + const d = new Date(dateTimeStr); + if (!isNaN(d.getTime())) { + const dd = String(d.getDate()).padStart(2, '0'); + const MM = String(d.getMonth() + 1).padStart(2, '0'); + const yyyy = d.getFullYear(); + const HH = String(d.getHours()).padStart(2, '0'); + const mm = String(d.getMinutes()).padStart(2, '0'); + const ss = String(d.getSeconds()).padStart(2, '0'); + return `${dd}/${MM}/${yyyy} ${HH}:${mm}:${ss}`; } } catch (e) { console.error(e); @@ -2041,6 +2090,7 @@ ${text(dispStatus)} ${text(row.remarks)} ${formatDateTime(row.updated_date)} + ${text(row.updated_by_name || '-')} `; // Actions cell @@ -2064,6 +2114,15 @@ tdActions.appendChild(btnViewPdf); } + // History Button (Available to all logged in users) + const btnHistory = document.createElement('button'); + btnHistory.className = 'btn-grid-action btn-outline-secondary me-1'; + btnHistory.innerHTML = ''; + btnHistory.style.border = '1px solid #cbd5e1'; + btnHistory.title = 'View Update History'; + btnHistory.onclick = () => showHistoryModal(row.id); + tdActions.appendChild(btnHistory); + if (isAdmin) { const btnEdit = document.createElement('button'); btnEdit.className = 'btn-grid-action btn-grid-edit me-1'; @@ -2893,6 +2952,136 @@ showLoader(false); }); } + + // Show update history for a payment tracking record + function showHistoryModal(id) { + const tbody = document.getElementById('history-tbody'); + const noHistoryFound = document.getElementById('no-history-found'); + + tbody.innerHTML = ''; + noHistoryFound.style.display = 'none'; + + historyModalObj.show(); + + // Local formatting helpers for history records + const text = val => (val === null || val === undefined || String(val).trim() === '') ? '-' : val; + const formatDate = dateStr => { + if (!dateStr) return '-'; + const parts = dateStr.split('-'); + if (parts.length === 3) { + return `${parts[2]}/${parts[1]}/${parts[0]}`; + } + return dateStr; + }; + const formatDateTime = dateTimeStr => { + if (!dateTimeStr) return '-'; + try { + const d = new Date(dateTimeStr); + if (!isNaN(d.getTime())) { + const dd = String(d.getDate()).padStart(2, '0'); + const MM = String(d.getMonth() + 1).padStart(2, '0'); + const yyyy = d.getFullYear(); + const HH = String(d.getHours()).padStart(2, '0'); + const mm = String(d.getMinutes()).padStart(2, '0'); + const ss = String(d.getSeconds()).padStart(2, '0'); + return `${dd}/${MM}/${yyyy} ${HH}:${mm}:${ss}`; + } + } catch (e) { + console.error(e); + } + return dateTimeStr; + }; + + fetch(contextPath + 'gccservicepaymenttracker/api/history?id=' + id) + .then(response => response.json()) + .then(historyList => { + if (!historyList || historyList.length === 0) { + noHistoryFound.style.display = 'block'; + return; + } + + historyList.forEach((item, index) => { + const tr = document.createElement('tr'); + + const dispStatus = String(item.resolved_status || getStatusName(item.status) || '').trim(); + + // Document button if file path is available + let docBtnHtml = '-'; + if (item.file_path) { + let pdfUrl = item.file_path; + if (contextPath && contextPath.endsWith('/') && pdfUrl.startsWith('/')) { + pdfUrl = contextPath + pdfUrl.substring(1); + } else { + pdfUrl = contextPath + pdfUrl; + } + docBtnHtml = ``; + } + + // Resolve type badge + let typeHtml = ''; + if (item.type) { + const cleanType = String(item.type).toLowerCase().trim(); + let label = cleanType; + let badgeClass = 'type-sd'; + + if (cleanType === 'asd' || cleanType.includes('asd') || cleanType.includes('additional security')) { + label = 'ASD'; + badgeClass = 'type-asd'; + } else if (cleanType.includes('agreement')) { + label = 'Agreement'; + badgeClass = 'type-agreement'; + } else if (cleanType === 'sd' || (cleanType.includes('security') && !cleanType.includes('additional') && !cleanType.includes('asd'))) { + label = 'Security Deposit'; + badgeClass = 'type-sd'; + } else if (cleanType.includes('invoice') || cleanType.includes('bill')) { + label = 'Tax Invoice'; + badgeClass = 'type-tax-invoice'; + } else if (cleanType === 'emd' || cleanType.includes('earnest')) { + label = 'EMD'; + badgeClass = 'type-emd'; + } else if (cleanType.includes('guarantee') || cleanType === 'bg') { + label = 'Bank Guarantee'; + badgeClass = 'type-bank-guarantee'; + } else if (cleanType.includes('chellan') || cleanType.includes('challan') || cleanType === 'dc') { + label = 'Delivery Chellan'; + badgeClass = 'type-delivery-chellan'; + } else { + const typeObj = activeTypes.find(t => String(t.id) === cleanType); + if (typeObj) { + label = typeObj.type_name; + } + badgeClass = 'status-default'; + } + typeHtml = `${label}`; + } else { + typeHtml = '-'; + } + + // Resolve vendor name + let resolvedVendor = item.resolved_vendor || item.vendor_name || '-'; + + tr.innerHTML = ` + ${index + 1} + ${formatDate(item.received_date)} + ${formatDate(item.invoice_date)} + ${text(item.subject)} + ${typeHtml} + ${text(resolvedVendor)} + ₹ ${text(item.amount)} + ${text(dispStatus)} + ${text(item.remarks)} + ${formatDateTime(item.updated_at)} + ${text(item.updated_by_name || '-')} + ${docBtnHtml} + `; + tbody.appendChild(tr); + }); + }) + .catch(err => { + console.error("Fetch History Error:", err); + tbody.innerHTML = ' Failed to load history records.'; + }); + } diff --git a/src/main/resources/templates/home/home.html b/src/main/resources/templates/home/home.html index a2b287b..4cd8030 100644 --- a/src/main/resources/templates/home/home.html +++ b/src/main/resources/templates/home/home.html @@ -346,6 +346,13 @@ oninput="filterEmployees()">
+ + + +
+ + +