From a7c1f6929859a925f3e79aaf5bd37e7b4b375ebb Mon Sep 17 00:00:00 2001 From: yoganandhank Date: Fri, 28 Aug 2026 10:59:07 +0530 Subject: [PATCH] newly added controller , service , pages and changes in tapal , toner, adjustment management projects --- .../com/gcc_manpower/config/WebConfig.java | 9 + .../GccServicePaymentTrackerController.java | 71 +- .../GccServicePaymentTrackerService.java | 26 +- .../controller/TapalController.java | 287 + .../service/TapalService.java | 383 +- .../controller/TonerController.java | 12 +- .../service/TonerService.java | 95 +- .../gccadjustmentmanagement/details.html | 185 +- .../gccservicepaymenttracker/dashboard.html | 142 +- .../gccservicepaymenttracker/details.html | 112 +- .../gcctapaldetailsmanagement/dashboard.html | 4883 ++++------------- .../gcctapaldetailsmanagement/details.html | 1962 +++++++ .../gcctapaldetailsmanagement/login.html | 2 +- .../templates/gcctonermanagement/upload.html | 2477 ++++++++- 14 files changed, 6724 insertions(+), 3922 deletions(-) create mode 100644 src/main/resources/templates/gcctapaldetailsmanagement/details.html diff --git a/src/main/java/com/gcc_manpower/config/WebConfig.java b/src/main/java/com/gcc_manpower/config/WebConfig.java index 97a75ee..c181423 100644 --- a/src/main/java/com/gcc_manpower/config/WebConfig.java +++ b/src/main/java/com/gcc_manpower/config/WebConfig.java @@ -24,6 +24,9 @@ public class WebConfig implements WebMvcConfigurer { @Value("${adjustment.upload.path:uploads/file-data}") private String adjustmentUploadPathConfig; + @Value("${tapal.upload.path:gcctapalfiles}") + private String tapalUploadPathConfig; + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // Resolve absolute path (supports both relative and absolute paths) @@ -59,6 +62,12 @@ public class WebConfig implements WebMvcConfigurer { registry.addResourceHandler("/gccadjustmentmanagementfiles/**") .addResourceLocations(adjustmentUploadPath); + // Map URL path /gcctapalfiles/** to the configured tapal folder path + String tapalUploadPath = Paths.get(tapalUploadPathConfig).toAbsolutePath().toUri() + .toString(); + registry.addResourceHandler("/gcctapalfiles/**") + .addResourceLocations(tapalUploadPath); + // Map URL path /uploads/** to the physical uploads folder in the project root String rootUploadPath = Paths.get("uploads").toAbsolutePath().toUri().toString(); registry.addResourceHandler("/uploads/**") 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 5945854..55858e6 100644 --- a/src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java +++ b/src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java @@ -93,12 +93,13 @@ public class GccServicePaymentTrackerController { Map response = new HashMap<>(); try { - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(usrName, usrPass); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(usrName, + usrPass); Authentication authentication = authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); - session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); + session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, + SecurityContextHolder.getContext()); String role = authentication.getAuthorities().stream() .map(auth -> auth.getAuthority().replace("ROLE_", "")) @@ -309,6 +310,70 @@ public class GccServicePaymentTrackerController { return response; } + @PostMapping("/api/upload-screenshot") + @ResponseBody + public Map uploadScreenshot( + @RequestParam("file") MultipartFile file, + HttpSession session) { + Map response = new HashMap<>(); + 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("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."); + return response; + } + + if (file.isEmpty()) { + response.put("success", false); + response.put("error", "Please select a file to upload."); + return response; + } + + try { + File parentDir = new File(paymentTrackerUploadPathConfig).getAbsoluteFile(); + File screenshotDir = new File(parentDir, "screenshots"); + if (!screenshotDir.exists()) { + screenshotDir.mkdirs(); + } + + String originalFilename = file.getOriginalFilename(); + String nameWithoutExt = "screenshot"; + String ext = ".png"; + if (originalFilename != null && originalFilename.contains(".")) { + int dotIdx = originalFilename.lastIndexOf("."); + nameWithoutExt = originalFilename.substring(0, dotIdx); + ext = originalFilename.substring(dotIdx); + } + nameWithoutExt = nameWithoutExt.replaceAll("[^a-zA-Z0-9-_]", "_"); + String savedFilename = nameWithoutExt + "_" + System.currentTimeMillis() + ext; + + File destFile = new File(screenshotDir, savedFilename).getAbsoluteFile(); + file.transferTo(destFile); + + String fileWebUrl = "/gccpaymenttrackerfiles/screenshots/" + savedFilename; + response.put("success", true); + response.put("filePath", fileWebUrl); + } catch (Exception e) { + response.put("success", false); + response.put("error", "Screenshot upload failed: " + e.getMessage()); + } + + return response; + } + @GetMapping("/api/types") @ResponseBody public List> getActiveTypes() { 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 0676b9b..49c1015 100644 --- a/src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java +++ b/src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java @@ -235,6 +235,7 @@ public class GccServicePaymentTrackerService { String orderBy = (String) payload.get("order_by"); String filePath = (String) payload.get("file_path"); String paymentReview = (String) payload.get("payment_review"); + String emailpath = (String) payload.get("mail_screenshot_path"); if (idObj != null && !String.valueOf(idObj).trim().isEmpty()) { // Update @@ -246,9 +247,9 @@ public class GccServicePaymentTrackerService { if (old != null) { 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, " + + "invoice_date, type, physical_file_no, remarks, mail_screenshot_path,tat, status, followup, myfollowup, " + "is_active, is_delete, order_by, file_path, payment_review, cdate, updated_at, updated_by" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?)"; jdbcTemplate.update(insertHistorySql, old.get("id"), old.get("received_date"), @@ -261,6 +262,7 @@ public class GccServicePaymentTrackerService { old.get("type"), old.get("physical_file_no"), old.get("remarks"), + old.get("mail_screenshot_path"), old.get("tat"), old.get("status"), old.get("followup"), @@ -280,13 +282,13 @@ 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 = ?, " + + "receiptno = ?, invoice_date = ?, type = ?, physical_file_no = ?, remarks = ?, mail_screenshot_path = ?, " + "tat = ?, status = ?, followup = ?, myfollowup = ?, order_by = ?, file_path = ?, " + "payment_review = ?, 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, + receiptNo, invoiceDate, type, physicalFileNo, remarks, emailpath, tat, status, followup, myfollowup, orderBy, filePath, paymentReview, userId, new java.sql.Timestamp(System.currentTimeMillis()), id); return rows > 0; @@ -298,14 +300,14 @@ public class GccServicePaymentTrackerService { // Insert 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, " + + "invoice_date, type, physical_file_no, remarks, mail_screenshot_path, tat, status, " + "followup, myfollowup, is_active, is_delete, order_by, file_path, " + "payment_review, updated_by, updated_date, cdate" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?, ?, ?, ?, ?)"; + + ") 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, + receiptNo, invoiceDate, type, physicalFileNo, remarks, emailpath, tat, status, followup, myfollowup, orderBy, filePath, paymentReview, userId, now, now); return rows > 0; @@ -334,9 +336,9 @@ public class GccServicePaymentTrackerService { try { String histSql = "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, " + + "invoice_date, type, physical_file_no, remarks, mail_screenshot_path, tat, status, followup, myfollowup, " + "is_active, is_delete, order_by, file_path, payment_review, cdate, updated_at, updated_by" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; jdbcTemplate.update(histSql, id, old.get("received_date"), @@ -349,6 +351,7 @@ public class GccServicePaymentTrackerService { old.get("type"), old.get("physical_file_no"), remarks, + old.get("mail_screenshot_path"), old.get("tat"), old.get("status"), old.get("followup"), @@ -489,9 +492,9 @@ public class GccServicePaymentTrackerService { try { String histSql = "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, " + + "invoice_date, type, physical_file_no, remarks, mail_screenshot_path, tat, status, followup, myfollowup, " + "is_active, is_delete, order_by, file_path, payment_review, cdate, updated_at, updated_by" - + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; jdbcTemplate.update(histSql, id, old.get("received_date"), @@ -504,6 +507,7 @@ public class GccServicePaymentTrackerService { old.get("type"), old.get("physical_file_no"), "Record restored", // remarks + old.get("mail_screenshot_path"), old.get("tat"), old.get("status"), old.get("followup"), diff --git a/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/controller/TapalController.java b/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/controller/TapalController.java index eb7104d..99223a3 100644 --- a/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/controller/TapalController.java +++ b/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/controller/TapalController.java @@ -1,9 +1,14 @@ package com.gcc_manpower.gcctapaldetailsmanagement.controller; +import java.io.File; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.CacheControl; +import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -14,9 +19,11 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; import com.gcc_manpower.gcctapaldetailsmanagement.service.TapalService; @@ -33,6 +40,9 @@ public class TapalController { @Autowired private AuthenticationManager authenticationManager; + @Value("${tapal.upload.path:gcctapalfiles}") + private String tapalUploadPathConfig; + @GetMapping({ "", "/", "/dashboard" }) public String viewDashboard(Model model, HttpSession session) { String username = (String) session.getAttribute("tap_usrName"); @@ -51,6 +61,23 @@ public class TapalController { return "gcctapaldetailsmanagement/dashboard"; } + @GetMapping("/details") + public String viewDetails(HttpSession session, Model model) { + String username = (String) session.getAttribute("tap_usrName"); + if (username == null) { + return "redirect:/gcctapaldetailsmanagement/login"; + } + String usrRole = (String) session.getAttribute("tap_usrRole"); + boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole); + + model.addAttribute("isLoggedIn", true); + model.addAttribute("isAdmin", isAdmin); + model.addAttribute("usrName", username); + model.addAttribute("curPage", "gcctapaldetailsmanagement"); + + return "gcctapaldetailsmanagement/details"; + } + @GetMapping("/login") public String login(HttpSession session) { return "gcctapaldetailsmanagement/login"; @@ -103,4 +130,264 @@ public class TapalController { request.getSession(true); return "redirect:/gcctapaldetailsmanagement/login"; } + + @GetMapping("/api/counts") + @ResponseBody + public ResponseEntity> getCounts() { + return ResponseEntity.ok() + .cacheControl(CacheControl.noCache().mustRevalidate()) + .body(tapalService.getCountsByCategory()); + } + + @GetMapping("/api/list") + @ResponseBody + public ResponseEntity>> listDetails( + @RequestParam(value = "category", required = false) String category, + @RequestParam(value = "fromDate", required = false) String fromDate, + @RequestParam(value = "toDate", required = false) String toDate, + @RequestParam(value = "status", required = false) String status) { + List> details = tapalService.searchTapals(category, fromDate, toDate, status); + return ResponseEntity.ok() + .cacheControl(CacheControl.noCache().mustRevalidate()) + .body(details); + } + + @PostMapping("/api/save") + @ResponseBody + public Map saveDetail( + @RequestBody Map payload, + HttpSession session) { + + Map response = new HashMap<>(); + String username = (String) session.getAttribute("tap_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("tap_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."); + return response; + } + + try { + boolean saved = tapalService.saveOrUpdateTapal(payload, username); + response.put("success", saved); + if (!saved) { + response.put("error", "Failed to save Tapal details to database."); + } + } catch (Exception e) { + response.put("success", false); + response.put("error", "Error: " + e.getMessage()); + } + + return response; + } + + @PostMapping("/api/delete") + @ResponseBody + public Map deleteDetail( + @RequestParam("id") int id, + @RequestParam(value = "remarks", required = false, defaultValue = "") String remarks, + HttpSession session) { + + Map response = new HashMap<>(); + String username = (String) session.getAttribute("tap_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("tap_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."); + return response; + } + + try { + boolean deleted = tapalService.deleteTapal(id, remarks, username); + response.put("success", deleted); + if (!deleted) { + response.put("error", "Failed to delete record."); + } + } catch (Exception e) { + response.put("success", false); + response.put("error", "Error: " + e.getMessage()); + } + + return response; + } + + @PostMapping("/api/upload-file") + @ResponseBody + public Map uploadFile( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "type", required = false, defaultValue = "tapal") String type, + HttpSession session) { + Map response = new HashMap<>(); + String username = (String) session.getAttribute("tap_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("tap_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."); + return response; + } + + if (file.isEmpty()) { + response.put("success", false); + response.put("error", "Please select a file to upload."); + return response; + } + + try { + File parentDir = new File(tapalUploadPathConfig).getAbsoluteFile(); + File destDir = parentDir; + String webPathPrefix = "/gcctapalfiles/"; + + if ("oc".equalsIgnoreCase(type)) { + destDir = new File(parentDir, "oc"); + webPathPrefix = "/gcctapalfiles/oc/"; + } + + if (!destDir.exists()) { + destDir.mkdirs(); + } + + String originalFilename = file.getOriginalFilename(); + String nameWithoutExt = type; + String ext = ".pdf"; + if (originalFilename != null && originalFilename.contains(".")) { + int dotIdx = originalFilename.lastIndexOf("."); + nameWithoutExt = originalFilename.substring(0, dotIdx); + ext = originalFilename.substring(dotIdx); + } + nameWithoutExt = nameWithoutExt.replaceAll("[^a-zA-Z0-9-_]", "_"); + String savedFilename = nameWithoutExt + "_" + System.currentTimeMillis() + ext; + + File destFile = new File(destDir, savedFilename).getAbsoluteFile(); + file.transferTo(destFile); + + String fileWebUrl = webPathPrefix + savedFilename; + response.put("success", true); + response.put("filePath", fileWebUrl); + } catch (Exception e) { + response.put("success", false); + response.put("error", "File upload failed: " + e.getMessage()); + } + + return response; + } + + @GetMapping("/api/categories") + @ResponseBody + public List> getActiveCategories() { + return tapalService.getActiveCategories(); + } + + @GetMapping("/api/statuslist") + @ResponseBody + public List> getStatusList() { + return tapalService.getActiveStatuses(); + } + + @GetMapping("/api/history") + @ResponseBody + public ResponseEntity>> getHistory( + @RequestParam("tapalNo") String tapalNo, + HttpSession session) { + String username = (String) session.getAttribute("tap_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(tapalService.getHistory(tapalNo)); + } + + @GetMapping("/api/deleted-list") + @ResponseBody + public ResponseEntity>> getDeletedList(HttpSession session) { + String username = (String) session.getAttribute("tap_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(tapalService.getDeletedTapals()); + } + + @PostMapping("/api/restore") + @ResponseBody + public Map restoreDetail( + @RequestParam("id") int id, + HttpSession session) { + Map response = new HashMap<>(); + String username = (String) session.getAttribute("tap_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("tap_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 restore records."); + return response; + } + + try { + boolean restored = tapalService.restoreTapal(id, username); + response.put("success", restored); + if (!restored) { + response.put("error", "Failed to restore record."); + } + } catch (Exception e) { + response.put("success", false); + response.put("error", "Error: " + e.getMessage()); + } + + return response; + } } diff --git a/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/service/TapalService.java b/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/service/TapalService.java index b13ad1c..4922476 100644 --- a/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/service/TapalService.java +++ b/src/main/java/com/gcc_manpower/gcctapaldetailsmanagement/service/TapalService.java @@ -1,6 +1,8 @@ package com.gcc_manpower.gcctapaldetailsmanagement.service; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,14 +23,385 @@ public class TapalService { this.jdbcTemplate = new JdbcTemplate(dataSource); } - public List> getAllTapalsStatus() { - String sql = "select * from tapal_status_list where is_active = 1 and is_delete=0"; + public List> getActiveCategories() { + String sql = "SELECT id, category_name FROM tapal_category_list " + + "WHERE is_active = 1 AND is_delete = 0 " + + "ORDER BY order_by ASC, category_name ASC"; try { - return this.jdbcTemplate.queryForList(sql); + return jdbcTemplate.queryForList(sql); } catch (Exception e) { - System.out.println(e.getMessage()); - return Collections.emptyList(); + System.err.println("Error fetching active categories: " + e.getMessage()); + return new ArrayList<>(); } } + public List> getActiveStatuses() { + String sql = "SELECT id, status_name FROM tapal_status_list " + + "WHERE is_active = 1 AND is_delete = 0 " + + "ORDER BY order_by ASC, status_name ASC"; + try { + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + System.err.println("Error fetching active statuses: " + e.getMessage()); + return new ArrayList<>(); + } + } + + public Map getCountsByCategory() { + Map counts = new HashMap<>(); + + List> categories = getActiveCategories(); + for (Map cat : categories) { + String catName = String.valueOf(cat.get("category_name")).toLowerCase().trim(); + counts.put(catName, 0); + } + counts.put("total", 0); + counts.put("status_not_updated", 0); + counts.put("completed", 0); + counts.put("pending", 0); + counts.put("age_1_15", 0); + counts.put("age_16_30", 0); + counts.put("age_31_60", 0); + counts.put("age_61_90", 0); + counts.put("age_91_120", 0); + counts.put("age_over_120", 0); + + try { + String sqlTotal = "SELECT COUNT(*) FROM tapal_tracking_details WHERE is_active = 1 AND is_delete = 0"; + Integer totalCount = jdbcTemplate.queryForObject(sqlTotal, Integer.class); + counts.put("total", totalCount != null ? totalCount : 0); + + String sqlMissingCat = "SELECT COUNT(*) FROM tapal_tracking_details WHERE is_active = 1 AND is_delete = 0 AND (cat_type_id IS NULL OR TRIM(cat_type_id) = '')"; + Integer missingCatCount = jdbcTemplate.queryForObject(sqlMissingCat, Integer.class); + counts.put("status_not_updated", missingCatCount != null ? missingCatCount : 0); + + String sql = "SELECT " + + "CASE WHEN t.cat_type_id REGEXP '^[0-9]+$' THEN c.category_name ELSE t.cat_type_id END as category_resolved, " + + "COUNT(*) as cnt " + + "FROM tapal_tracking_details t " + + "LEFT JOIN tapal_category_list c ON t.cat_type_id REGEXP '^[0-9]+$' AND c.id = CAST(t.cat_type_id AS UNSIGNED) " + + "WHERE t.is_active = 1 AND t.is_delete = 0 " + + "GROUP BY category_resolved"; + + List> rows = jdbcTemplate.queryForList(sql); + for (Map row : rows) { + String catName = String.valueOf(row.get("category_resolved")).toLowerCase().trim(); + Number countNum = (Number) row.get("cnt"); + int count = countNum != null ? countNum.intValue() : 0; + counts.put(catName, counts.getOrDefault(catName, 0) + count); + } + + // Calculate completed, pending, and age counts + String sqlAll = "SELECT tapal_received_date, remarks FROM tapal_tracking_details WHERE is_active = 1 AND is_delete = 0"; + List> records = jdbcTemplate.queryForList(sqlAll); + int completed = 0; + int pending = 0; + int age_1_15 = 0; + int age_16_30 = 0; + int age_31_60 = 0; + int age_61_90 = 0; + int age_91_120 = 0; + int age_over_120 = 0; + + for (Map r : records) { + String remarks = (String) r.get("remarks"); + java.sql.Date receivedDate = (java.sql.Date) r.get("tapal_received_date"); + + boolean isPending = (remarks == null || remarks.trim().isEmpty() || remarks.trim().equals("-")); + if (isPending) { + pending++; + int age = 0; + if (receivedDate != null) { + long diffTime = Math.abs(System.currentTimeMillis() - receivedDate.getTime()); + age = (int) Math.ceil(diffTime / (1000.0 * 60.0 * 60.0 * 24.0)) - 1; + } + if (age >= 0 && age <= 15) { + age_1_15++; + } else if (age >= 16 && age <= 30) { + age_16_30++; + } else if (age >= 31 && age <= 60) { + age_31_60++; + } else if (age >= 61 && age <= 90) { + age_61_90++; + } else if (age >= 91 && age <= 120) { + age_91_120++; + } else if (age > 120) { + age_over_120++; + } + } else { + completed++; + } + } + + counts.put("completed", completed); + counts.put("pending", pending); + counts.put("age_1_15", age_1_15); + counts.put("age_16_30", age_16_30); + counts.put("age_31_60", age_31_60); + counts.put("age_61_90", age_61_90); + counts.put("age_91_120", age_91_120); + counts.put("age_over_120", age_over_120); + + } catch (Exception e) { + System.err.println("Error getting Tapal counts: " + e.getMessage()); + } + return counts; + } + + public List> searchTapals(String category, String fromDate, String toDate, String status) { + StringBuilder sql = new StringBuilder( + "SELECT t.*, " + + "c.category_name as resolved_category, " + + "s.status_name as resolved_status, " + + "u.usr_name as updated_by_name " + + "FROM tapal_tracking_details t " + + "LEFT JOIN tapal_category_list c ON TRIM(t.cat_type_id) REGEXP '^[0-9]+$' AND c.id = CAST(TRIM(t.cat_type_id) AS UNSIGNED) " + + "LEFT JOIN tapal_status_list s ON TRIM(t.status_id) REGEXP '^[0-9]+$' AND s.id = CAST(TRIM(t.status_id) AS UNSIGNED) " + + "LEFT JOIN app_user u ON (t.updated_by = CAST(u.usr_id AS CHAR) OR t.updated_by = u.usr_name) " + + "WHERE t.is_active = 1 AND t.is_delete = 0"); + List params = new ArrayList<>(); + + if (category != null && !category.trim().isEmpty()) { + String categoryName = category.trim().toLowerCase(); + if (categoryName.equals("status_not_updated")) { + sql.append(" AND (t.cat_type_id IS NULL OR TRIM(t.cat_type_id) = '')"); + } else { + sql.append(" AND (LOWER(t.cat_type_id) = ? OR LOWER(c.category_name) = ?)"); + params.add(categoryName); + params.add(categoryName); + } + } + + if (fromDate != null && !fromDate.trim().isEmpty()) { + sql.append(" AND t.tapal_received_date >= ?"); + params.add(parseDate(fromDate)); + } + + if (toDate != null && !toDate.trim().isEmpty()) { + sql.append(" AND t.tapal_received_date <= ?"); + params.add(parseDate(toDate)); + } + + if (status != null && !status.trim().isEmpty()) { + if (status.trim().matches("^[0-9]+$")) { + sql.append( + " AND (t.status_id = ? OR (NOT t.status_id REGEXP '^[0-9]+$' AND t.status_id = (SELECT status_name FROM tapal_status_list WHERE id = ? LIMIT 1)))"); + params.add(status.trim()); + params.add(status.trim()); + } else { + sql.append( + " AND (LOWER(t.status_id) = ? OR (t.status_id REGEXP '^[0-9]+$' AND LOWER(s.status_name) = ?))"); + params.add(status.trim().toLowerCase()); + params.add(status.trim().toLowerCase()); + } + } + + sql.append(" ORDER BY t.tapal_received_date DESC, t.id DESC"); + + try { + return jdbcTemplate.queryForList(sql.toString(), params.toArray()); + } catch (Exception e) { + System.err.println("Error searching Tapal details: " + e.getMessage()); + return new ArrayList<>(); + } + } + + public Map getTapalById(int id) { + String sql = "SELECT * FROM tapal_tracking_details WHERE id = ? AND is_active = 1 AND is_delete = 0"; + try { + List> list = jdbcTemplate.queryForList(sql, id); + if (list != null && !list.isEmpty()) { + return list.get(0); + } + } catch (Exception e) { + System.err.println("Error fetching Tapal detail ID " + id + ": " + e.getMessage()); + } + return null; + } + + private void saveToHistory(Map old, String updatedBy) { + String sql = "INSERT INTO tapal_tracking_details_history (" + + "tapal_no, tapal_received_date, request_from, subject, cat_type_id, status_id, " + + "remarks, tapal_file_path, oc_file_path, cdate, updated_date, updated_by, " + + "is_active, is_delete" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + try { + jdbcTemplate.update(sql, + old.get("tapal_no"), + old.get("tapal_received_date"), + old.get("request_from"), + old.get("subject"), + old.get("cat_type_id"), + old.get("status_id"), + old.get("remarks"), + old.get("tapal_file_path"), + old.get("oc_file_path"), + old.get("cdate"), + new java.sql.Timestamp(System.currentTimeMillis()), + updatedBy, + old.get("is_active"), + old.get("is_delete")); + } catch (Exception e) { + System.err.println("Error inserting Tapal history: " + e.getMessage()); + } + } + + public boolean saveOrUpdateTapal(Map payload, String updatedBy) { + Object idObj = payload.get("id"); + String tapalNo = (String) payload.get("tapal_no"); + java.sql.Date tapalReceivedDate = parseDate(payload.get("tapal_received_date")); + String requestFrom = (String) payload.get("request_from"); + String subject = (String) payload.get("subject"); + String catTypeId = (String) payload.get("cat_type_id"); + String statusId = (String) payload.get("status_id"); + String remarks = (String) payload.get("remarks"); + String tapalFilePath = (String) payload.get("tapal_file_path"); + String ocFilePath = (String) payload.get("oc_file_path"); + + if (idObj != null && !String.valueOf(idObj).trim().isEmpty()) { + int id = Integer.parseInt(String.valueOf(idObj).trim()); + Map old = getTapalById(id); + if (old != null) { + saveToHistory(old, updatedBy); + } + + String sql = "UPDATE tapal_tracking_details SET " + + "tapal_no = ?, tapal_received_date = ?, request_from = ?, subject = ?, " + + "cat_type_id = ?, status_id = ?, remarks = ?, tapal_file_path = ?, oc_file_path = ?, " + + "updated_date = ?, updated_by = ? " + + "WHERE id = ? AND is_active = 1 AND is_delete = 0"; + try { + int rows = jdbcTemplate.update(sql, tapalNo, tapalReceivedDate, requestFrom, subject, + catTypeId, statusId, remarks, tapalFilePath, ocFilePath, + new java.sql.Timestamp(System.currentTimeMillis()), updatedBy, id); + return rows > 0; + } catch (Exception e) { + System.err.println("Error updating Tapal detail ID " + id + ": " + e.getMessage()); + return false; + } + } else { + String sql = "INSERT INTO tapal_tracking_details (" + + "tapal_no, tapal_received_date, request_from, subject, cat_type_id, status_id, " + + "remarks, tapal_file_path, oc_file_path, updated_by, updated_date, cdate, " + + "is_active, is_delete" + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0)"; + try { + java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis()); + int rows = jdbcTemplate.update(sql, tapalNo, tapalReceivedDate, requestFrom, subject, + catTypeId, statusId, remarks, tapalFilePath, ocFilePath, updatedBy, now, now); + return rows > 0; + } catch (Exception e) { + System.err.println("Error inserting Tapal details: " + e.getMessage()); + return false; + } + } + } + + public boolean deleteTapal(int id, String remarks, String updatedBy) { + String sql = "UPDATE tapal_tracking_details SET is_active = 0, is_delete = 1, remarks = ?, updated_by = ?, updated_date = ? WHERE id = ?"; + try { + Map old = null; + try { + old = jdbcTemplate.queryForMap("SELECT * FROM tapal_tracking_details WHERE id = ?", id); + } catch (Exception e) { + System.err.println("Could not find old record for history before delete: " + e.getMessage()); + } + + int rows = jdbcTemplate.update(sql, remarks, updatedBy, new java.sql.Timestamp(System.currentTimeMillis()), + id); + + if (rows > 0 && old != null) { + old.put("remarks", remarks); + old.put("is_active", 0); + old.put("is_delete", 1); + saveToHistory(old, updatedBy); + } + return rows > 0; + } catch (Exception e) { + System.err.println("Error soft-deleting Tapal detail ID " + id + ": " + e.getMessage()); + return false; + } + } + + public boolean restoreTapal(int id, String updatedBy) { + String sql = "UPDATE tapal_tracking_details SET is_active = 1, is_delete = 0, remarks = 'Record restored', updated_by = ?, updated_date = ? WHERE id = ?"; + try { + Map old = null; + try { + old = jdbcTemplate.queryForMap("SELECT * FROM tapal_tracking_details WHERE id = ?", id); + } catch (Exception e) { + System.err.println("Could not find old record for history before restore: " + e.getMessage()); + } + + int rows = jdbcTemplate.update(sql, updatedBy, new java.sql.Timestamp(System.currentTimeMillis()), id); + + if (rows > 0 && old != null) { + old.put("remarks", "Record restored"); + old.put("is_active", 1); + old.put("is_delete", 0); + saveToHistory(old, updatedBy); + } + return rows > 0; + } catch (Exception e) { + System.err.println("Error restoring Tapal detail ID " + id + ": " + e.getMessage()); + return false; + } + } + + public List> getDeletedTapals() { + String sql = "SELECT t.*, " + + "c.category_name as resolved_category, " + + "s.status_name as resolved_status, " + + "u.usr_name as updated_by_name " + + "FROM tapal_tracking_details t " + + "LEFT JOIN tapal_category_list c ON TRIM(t.cat_type_id) REGEXP '^[0-9]+$' AND c.id = CAST(TRIM(t.cat_type_id) AS UNSIGNED) " + + "LEFT JOIN tapal_status_list s ON TRIM(t.status_id) REGEXP '^[0-9]+$' AND s.id = CAST(TRIM(t.status_id) AS UNSIGNED) " + + "LEFT JOIN app_user u ON (t.updated_by = CAST(u.usr_id AS CHAR) OR t.updated_by = u.usr_name) " + + "WHERE t.is_active = 0 AND t.is_delete = 1 " + + "ORDER BY t.updated_date DESC"; + try { + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + System.err.println("Error fetching deleted Tapal details: " + e.getMessage()); + return new ArrayList<>(); + } + } + + public List> getHistory(String tapalNo) { + String sql = "SELECT h.*, " + + "c.category_name as resolved_category, " + + "s.status_name as resolved_status, " + + "u.usr_name as updated_by_name " + + "FROM tapal_tracking_details_history h " + + "LEFT JOIN tapal_category_list c ON TRIM(h.cat_type_id) REGEXP '^[0-9]+$' AND c.id = CAST(TRIM(h.cat_type_id) AS UNSIGNED) " + + "LEFT JOIN tapal_status_list s ON TRIM(h.status_id) REGEXP '^[0-9]+$' AND s.id = CAST(TRIM(h.status_id) AS UNSIGNED) " + + "LEFT JOIN app_user u ON (h.updated_by = CAST(u.usr_id AS CHAR) OR h.updated_by = u.usr_name) " + + "WHERE h.tapal_no = ? " + + "ORDER BY h.updated_date DESC"; + try { + return jdbcTemplate.queryForList(sql, tapalNo); + } catch (Exception e) { + System.err.println("Error fetching Tapal history for tapal_no " + tapalNo + ": " + e.getMessage()); + return new ArrayList<>(); + } + } + + private java.sql.Date parseDate(Object obj) { + if (obj == null || String.valueOf(obj).trim().isEmpty()) { + return null; + } + String str = String.valueOf(obj).trim(); + try { + if (str.contains("/")) { + String[] parts = str.split("/"); + if (parts.length == 3) { + str = parts[2] + "-" + parts[1] + "-" + parts[0]; + } + } + return java.sql.Date.valueOf(str); + } catch (Exception e) { + System.err.println("Error parsing date '" + obj + "' for Tapal: " + e.getMessage()); + return null; + } + } } diff --git a/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java b/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java index c8f7e9f..02bc309 100644 --- a/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java +++ b/src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java @@ -149,6 +149,15 @@ public class TonerController { return tonerService.getActiveDepartments(); } + @GetMapping("/api/zones") + @ResponseBody + public List> getZones(HttpSession session) { + if (session.getAttribute("toner_usrName") == null) { + return java.util.Collections.emptyList(); + } + return tonerService.getActiveZones(); + } + @PostMapping("/api/inward") @ResponseBody public Map uploadInward( @@ -225,6 +234,7 @@ public class TonerController { public Map uploadRequest( @RequestParam("itcellRefNo") String itcellRefNo, @RequestParam("deptId") String deptId, + @RequestParam("zoneId") String zoneId, @RequestParam("itemsJson") String itemsJson, @RequestParam(value = "userRequestFile", required = false) MultipartFile userRequestFile, HttpSession session) { @@ -273,7 +283,7 @@ public class TonerController { String urPath = saveUploadedFile(userRequestFile, "user_request", uploadDir); boolean saved = tonerService.saveUserRequest( - itcellRefNo.trim(), deptId.trim(), items, urPath, updatedBy); + itcellRefNo.trim(), deptId.trim(), zoneId.trim(), items, urPath, updatedBy); if (saved) { response.put("success", true); diff --git a/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java b/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java index 437f748..29ba0c3 100644 --- a/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java +++ b/src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java @@ -88,12 +88,16 @@ public class TonerService { java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis()); try { for (Map item : items) { - int pid = Integer.parseInt(String.valueOf(item.get("pid"))); - int tid = Integer.parseInt(String.valueOf(item.get("tid"))); + Integer pidVal = parseIntegerSafely(item.get("pid")); + Integer tidVal = parseIntegerSafely(item.get("tid")); + if (pidVal == null || tidVal == null) { + continue; // Skip invalid item + } + int pid = pidVal; + int tid = tidVal; + String qty = String.valueOf(item.get("qty")); - Integer requestId = item.get("requestId") != null - ? Integer.parseInt(String.valueOf(item.get("requestId"))) - : null; + Integer requestId = parseIntegerSafely(item.get("requestId")); int qtyInt = 0; try { @@ -126,17 +130,22 @@ public class TonerService { } @Transactional - public boolean saveUserRequest(String itCellRefNo, String deptId, List> items, + public boolean saveUserRequest(String itCellRefNo, String deptId, String zoneId, List> items, String userRequestUpload, String username) { java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis()); String sql = "INSERT INTO toner_department_requested_details " + - "(req_no, itcell_ref_no, dept_id, pid, tid, qty_requested, qty_delivered, user_request_upload, cdate, updated_date, updated_by, is_active, is_delete) " + "(req_no, itcell_ref_no, dept_id, zone_id, pid, tid, qty_requested, qty_delivered, user_request_upload, cdate, updated_date, updated_by, is_active, is_delete) " + - "VALUES (?, ?, ?, ?, ?, ?, '0', ?, ?, ?, ?, 1, 0)"; + "VALUES (?, ?, ?, ?, ?, ?, ?, '0', ?, ?, ?, ?, 1, 0)"; try { for (Map item : items) { - int pid = Integer.parseInt(String.valueOf(item.get("pid"))); - int tid = Integer.parseInt(String.valueOf(item.get("tid"))); + Integer pidVal = parseIntegerSafely(item.get("pid")); + Integer tidVal = parseIntegerSafely(item.get("tid")); + if (pidVal == null || tidVal == null) { + continue; // Skip invalid item + } + int pid = pidVal; + int tid = tidVal; String qtyRequested = String.valueOf(item.get("qty")); String tonerModel = getTonerModelName(tid); @@ -146,7 +155,7 @@ public class TonerService { int year = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR); String reqNo = String.format("%s-%d-%04d", cleanModel, year, seqNum); - jdbcTemplate.update(sql, reqNo, itCellRefNo, deptId, String.valueOf(pid), String.valueOf(tid), + jdbcTemplate.update(sql, reqNo, itCellRefNo, deptId, zoneId, String.valueOf(pid), String.valueOf(tid), qtyRequested, userRequestUpload, now, now, username); } return true; @@ -158,13 +167,14 @@ public class TonerService { } public List> getPendingRequests() { - String sql = "SELECT r.id, r.req_no, COALESCE(d.dept_name, r.dept_id) AS dept_id, r.pid, r.tid, r.qty_requested, r.qty_delivered, " + String sql = "SELECT r.id, r.req_no, COALESCE(d.dept_name, r.dept_id) AS dept_id, COALESCE(z.zone_name, r.zone_id) AS zone_name, r.pid, r.tid, r.qty_requested, r.qty_delivered, " + "p.printer_name, t.model_name " + "FROM toner_department_requested_details r " + "LEFT JOIN toner_printer_model_list p ON r.pid = p.id " + "LEFT JOIN toner_model_list t ON r.tid = t.id " + "LEFT JOIN toner_department_list d ON CAST(r.dept_id AS SIGNED) = d.id " + + "LEFT JOIN toner_zone_list z ON CAST(r.zone_id AS SIGNED) = z.id " + "WHERE r.is_active = 1 AND r.is_delete = 0 " + "AND (r.qty_delivered IS NULL OR CAST(r.qty_delivered AS SIGNED) < CAST(r.qty_requested AS SIGNED)) " + "ORDER BY r.id DESC"; @@ -185,9 +195,12 @@ public class TonerService { sql = "UPDATE toner_department_requested_details " + "SET final_delivered_upload = CASE WHEN final_delivered_upload IS NULL OR final_delivered_upload = '' THEN ? ELSE CONCAT(final_delivered_upload, ',', ?) END, " + + "qty_delivered = CASE WHEN qty_delivered IS NULL OR CAST(qty_delivered AS SIGNED) = 0 THEN ? ELSE qty_delivered END, " + + "updated_date = ?, updated_by = ? " + "WHERE id = ?"; - rows = jdbcTemplate.update(sql, finalDeliveredUpload, finalDeliveredUpload, now, username, id); + rows = jdbcTemplate.update(sql, finalDeliveredUpload, finalDeliveredUpload, qtyIssued, now, username, + id); } else { sql = "UPDATE toner_department_requested_details " + "SET qty_delivered = CAST(COALESCE(qty_delivered, 0) AS SIGNED) + CAST(? AS SIGNED), " + @@ -270,7 +283,7 @@ public class TonerService { + "MAX(s.cdate) AS cdate, COALESCE(MAX(u.usr_name), MAX(s.updated_by)) AS order_by, " + "p.printer_name, t.model_name, " + "COALESCE((SELECT w.qty_registered FROM toner_workorder_printermodel_qty w WHERE w.ref_no = s.ref_no AND w.pid = s.pid AND w.tid = s.tid AND w.is_active = 1 AND w.is_delete = 0 LIMIT 1), 0) AS qty_workorder_registered, " - + "COALESCE((SELECT SUM(CAST(mr.qty_registered AS SIGNED)) FROM toner_workorder_model_multiple_request_qty mr INNER JOIN toner_workorder_model_request_details r ON mr.ref_no = r.ref_no WHERE r.ref_no = s.ref_no AND mr.pid = s.pid AND mr.tid = s.tid AND mr.is_active = 1 AND mr.is_delete = 0 AND r.is_active = 1 AND r.is_delete = 0), 0) AS qty_workorder_requested " + + "COALESCE((SELECT SUM(CAST(mr.qty_registered AS SIGNED)) FROM toner_workorder_model_multiple_request_qty mr INNER JOIN toner_workorder_model_request_details r ON mr.ref_no = r.ref_no AND mr.cdate = r.cdate WHERE r.ref_no = s.ref_no AND mr.pid = s.pid AND mr.tid = s.tid AND mr.is_active = 1 AND mr.is_delete = 0 AND r.is_active = 1 AND r.is_delete = 0), 0) AS qty_workorder_requested " + "FROM toner_stock_register_details s " + "LEFT JOIN toner_printer_model_list p ON CAST(s.pid AS SIGNED) = p.id " + "LEFT JOIN toner_model_list t ON CAST(s.tid AS SIGNED) = t.id " @@ -287,7 +300,7 @@ public class TonerService { } public List> getAllUserRequests() { - String sql = "SELECT r.id, r.pid, r.tid, r.req_no, r.itcell_ref_no, COALESCE(d.dept_name, r.dept_id) AS dept_id, r.qty_requested, r.qty_delivered, " + String sql = "SELECT r.id, r.pid, r.tid, r.req_no, r.itcell_ref_no, COALESCE(d.dept_name, r.dept_id) AS dept_id, COALESCE(z.zone_name, r.zone_id) AS zone_name, r.qty_requested, r.qty_delivered, " + "r.user_request_upload, r.cdate, r.updated_date, COALESCE(u.usr_name, r.updated_by) AS updated_by, " + "p.printer_name, t.model_name " + "FROM toner_department_requested_details r " @@ -295,6 +308,7 @@ public class TonerService { + "LEFT JOIN toner_model_list t ON CAST(r.tid AS SIGNED) = t.id " + "LEFT JOIN app_user u ON CAST(r.updated_by AS SIGNED) = u.usr_id " + "LEFT JOIN toner_department_list d ON CAST(r.dept_id AS SIGNED) = d.id " + + "LEFT JOIN toner_zone_list z ON CAST(r.zone_id AS SIGNED) = z.id " + "WHERE r.is_active = 1 AND r.is_delete = 0 " + "ORDER BY r.id DESC"; try { @@ -306,13 +320,14 @@ public class TonerService { } public List> getOutwardHistory() { - String sql = "SELECT r.id, r.pid, r.tid, r.req_no, r.itcell_ref_no, COALESCE(d.dept_name, r.dept_id) AS dept_id, r.qty_requested, r.qty_delivered, r.final_delivered_upload, r.user_request_upload, r.cdate, r.updated_date, " + String sql = "SELECT r.id, r.pid, r.tid, r.req_no, r.itcell_ref_no, COALESCE(d.dept_name, r.dept_id) AS dept_id, COALESCE(z.zone_name, r.zone_id) AS zone_name, r.qty_requested, r.qty_delivered, r.final_delivered_upload, r.user_request_upload, r.cdate, r.updated_date, " + "p.printer_name, t.model_name " + "FROM toner_department_requested_details r " + "LEFT JOIN toner_printer_model_list p ON CAST(r.pid AS SIGNED) = p.id " + "LEFT JOIN toner_model_list t ON CAST(r.tid AS SIGNED) = t.id " + "LEFT JOIN toner_department_list d ON CAST(r.dept_id AS SIGNED) = d.id " + + "LEFT JOIN toner_zone_list z ON CAST(r.zone_id AS SIGNED) = z.id " + "WHERE r.is_active = 1 AND r.is_delete = 0 " + "AND r.qty_delivered IS NOT NULL AND CAST(r.qty_delivered AS SIGNED) > 0 " + "ORDER BY r.updated_date DESC"; @@ -392,7 +407,10 @@ public class TonerService { // Calculate sum of qty_requested int totalQtyRequested = 0; for (Map item : items) { - totalQtyRequested += Integer.parseInt(String.valueOf(item.get("qty"))); + Integer q = parseIntegerSafely(item.get("qty")); + if (q != null) { + totalQtyRequested += q; + } } final int finalTotalQty = totalQtyRequested; @@ -460,7 +478,7 @@ public class TonerService { + "WHERE mr.ref_no = w.ref_no AND mr.pid = w.pid AND mr.tid = w.tid AND mr.is_active = 1 AND mr.is_delete = 0), 0) AS qty_already_requested " + "FROM toner_workorder_printermodel_qty w " + "LEFT JOIN toner_workorder_register_details r ON w.ref_no = r.ref_no " - + "LEFT JOIN gccpayment_processing_tracker.vendors_list vl ON CAST(r.vendor_name AS SIGNED) = vl.id " + + "LEFT JOIN payment_vendors_list vl ON CAST(r.vendor_name AS SIGNED) = vl.id " + "LEFT JOIN toner_printer_model_list p ON CAST(w.pid AS SIGNED) = p.id " + "LEFT JOIN toner_model_list t ON CAST(w.tid AS SIGNED) = t.id " + "WHERE w.ref_no = ? AND w.is_active = 1 AND w.is_delete = 0"; @@ -483,12 +501,12 @@ public class TonerService { } public List> getWorkorderRequests(String refNo) { - String sql = "SELECT mr.id, mr.pid, mr.tid, mr.qty_registered AS qty_requested, r.company_name AS vendor_id, r.cdate, " + String sql = "SELECT mr.id, r.id AS main_request_id, mr.pid, mr.tid, mr.qty_registered AS qty_requested, r.company_name AS vendor_id, r.cdate, " + "COALESCE(u.usr_name, r.updated_by) AS updated_by, r.email_copy_upload, p.printer_name, t.model_name, t.part_code, " + "COALESCE((SELECT SUM(CAST(sr.qty_registered AS SIGNED)) FROM toner_stock_register_details sr " - + "WHERE sr.ref_no = r.ref_no AND sr.pid = mr.pid AND sr.tid = mr.tid AND sr.is_active = 1 AND sr.is_delete = 0), 0) AS qty_inward_received " + + "WHERE sr.ref_no = r.ref_no AND sr.pid = mr.pid AND sr.tid = mr.tid AND sr.request_id = mr.id AND sr.is_active = 1 AND sr.is_delete = 0), 0) AS qty_inward_received " + "FROM toner_workorder_model_multiple_request_qty mr " - + "LEFT JOIN toner_workorder_model_request_details r ON mr.ref_no = r.ref_no " + + "LEFT JOIN toner_workorder_model_request_details r ON mr.ref_no = r.ref_no AND mr.cdate = r.cdate " + "LEFT JOIN toner_printer_model_list p ON CAST(mr.pid AS SIGNED) = p.id " + "LEFT JOIN toner_model_list t ON CAST(mr.tid AS SIGNED) = t.id " + "LEFT JOIN app_user u ON CAST(r.updated_by AS SIGNED) = u.usr_id " @@ -509,7 +527,7 @@ public class TonerService { "p.printer_name, t.model_name " + "FROM toner_workorder_printermodel_qty w " + "LEFT JOIN toner_workorder_register_details r ON w.ref_no = r.ref_no " + - "LEFT JOIN gccpayment_processing_tracker.vendors_list vl ON CAST(r.vendor_name AS SIGNED) = vl.id " + + "LEFT JOIN payment_vendors_list vl ON CAST(r.vendor_name AS SIGNED) = vl.id " + "LEFT JOIN toner_printer_model_list p ON CAST(w.pid AS SIGNED) = p.id " + "LEFT JOIN toner_model_list t ON CAST(w.tid AS SIGNED) = t.id " + "LEFT JOIN app_user u ON CAST(r.updated_by AS SIGNED) = u.usr_id " + @@ -524,12 +542,12 @@ public class TonerService { } public List> getWorkorderModelRequestHistory() { - String sql = "SELECT mr.id, r.ref_no, mr.pid, mr.tid, r.company_name AS vendor_id, " + String sql = "SELECT mr.id, r.id AS main_request_id, r.ref_no, mr.pid, mr.tid, r.company_name AS vendor_id, " + "CAST(mr.qty_registered AS SIGNED) AS qty_requested, r.email_copy_upload, " + "r.cdate, COALESCE(u.usr_name, r.updated_by) AS order_by, p.printer_name, t.model_name, " + "COALESCE((SELECT w.qty_registered FROM toner_workorder_printermodel_qty w WHERE w.ref_no = r.ref_no AND w.pid = mr.pid AND w.tid = mr.tid AND w.is_active = 1 AND w.is_delete = 0 LIMIT 1), 0) AS qty_registered " + "FROM toner_workorder_model_multiple_request_qty mr " - + "LEFT JOIN toner_workorder_model_request_details r ON mr.ref_no = r.ref_no " + + "LEFT JOIN toner_workorder_model_request_details r ON mr.ref_no = r.ref_no AND mr.cdate = r.cdate " + "LEFT JOIN toner_printer_model_list p ON CAST(mr.pid AS SIGNED) = p.id " + "LEFT JOIN toner_model_list t ON CAST(mr.tid AS SIGNED) = t.id " + "LEFT JOIN app_user u ON CAST(r.updated_by AS SIGNED) = u.usr_id " @@ -552,4 +570,33 @@ public class TonerService { return new ArrayList<>(); } } + + public List> getActiveZones() { + String sql = "SELECT id, zone_name FROM toner_zone_list WHERE is_active = 1 AND is_delete = 0"; + try { + return jdbcTemplate.queryForList(sql); + } catch (Exception e) { + System.err.println("Error fetching active zones: " + e.getMessage()); + return new ArrayList<>(); + } + } + + public JdbcTemplate getJdbcTemplate() { + return this.jdbcTemplate; + } + + private Integer parseIntegerSafely(Object val) { + if (val == null) { + return null; + } + String str = String.valueOf(val).trim(); + if (str.isEmpty() || "null".equalsIgnoreCase(str) || "undefined".equalsIgnoreCase(str)) { + return null; + } + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + return null; + } + } } diff --git a/src/main/resources/templates/gccadjustmentmanagement/details.html b/src/main/resources/templates/gccadjustmentmanagement/details.html index f63c7c0..acbee9f 100644 --- a/src/main/resources/templates/gccadjustmentmanagement/details.html +++ b/src/main/resources/templates/gccadjustmentmanagement/details.html @@ -1046,12 +1046,21 @@ let suffix = ''; if (filterReason !== 'Total' && filterReason !== 'All') { let reasonLabel = filterReason; + /* Old translation logic commented if (filterReason === 'AllThreeNotAvail') reasonLabel = 'FI, SE & DC Not Available'; else if (filterReason === 'FINotAvail') reasonLabel = 'FI Not Available'; else if (filterReason === 'SENotDone') reasonLabel = 'Stock Not Available'; else if (filterReason === 'DCNotAvail') reasonLabel = 'DC Not Available'; else if (filterReason === 'FIAvailSEDCProp') reasonLabel = 'FI Available, SE & DC Pending'; else if (filterReason === 'Others') reasonLabel = 'Others'; + */ + if (filterReason === 'FIAvail') reasonLabel = 'FI Available'; + else if (filterReason === 'FINotAvail') reasonLabel = 'FI Not Available'; + else if (filterReason === 'Others') reasonLabel = 'Others'; + else if (filterReason === 'AllThreeNotAvail') reasonLabel = 'FI, SE & DC Not Available'; + else if (filterReason === 'SENotDone') reasonLabel = 'Stock Not Available'; + else if (filterReason === 'DCNotAvail') reasonLabel = 'DC Not Available'; + else if (filterReason === 'FIAvailSEDCProp') reasonLabel = 'FI Available, SE & DC Pending'; suffix = ` - Pending: ${reasonLabel}`; } else { @@ -1112,12 +1121,21 @@ // Reason Badge if (filterReason !== 'Total' && filterReason !== 'All') { let reasonLabel = filterReason; + /* Old translation logic commented if (filterReason === 'AllThreeNotAvail') reasonLabel = 'FI, SE & DC Not Available'; else if (filterReason === 'FINotAvail') reasonLabel = 'FI Not Available'; - else if (filterReason === 'StockDone') reasonLabel = 'Stock Registry Done'; + else if (filterReason === 'SENotDone') reasonLabel = 'Stock Not Available'; else if (filterReason === 'DCNotAvail') reasonLabel = 'DC Not Available'; else if (filterReason === 'FIAvailSEDCProp') reasonLabel = 'FI Available, SE & DC Pending'; else if (filterReason === 'Others') reasonLabel = 'Others'; + */ + if (filterReason === 'FIAvail') reasonLabel = 'FI Available'; + else if (filterReason === 'FINotAvail') reasonLabel = 'FI Not Available'; + else if (filterReason === 'Others') reasonLabel = 'Others'; + else if (filterReason === 'AllThreeNotAvail') reasonLabel = 'FI, SE & DC Not Available'; + else if (filterReason === 'SENotDone') reasonLabel = 'Stock Not Available'; + else if (filterReason === 'DCNotAvail') reasonLabel = 'DC Not Available'; + else if (filterReason === 'FIAvailSEDCProp') reasonLabel = 'FI Available, SE & DC Pending'; html += `Reason: ${reasonLabel}`; } @@ -1473,20 +1491,34 @@ const hasSE = !isNullOrEmpty(r.sr_filepath); const hasDC = !isNullOrEmpty(r.dc_file_path); + /* Old logic commented if (!hasFI && !hasSE && !hasDC) return 'AllThreeNotAvail'; if (!hasFI && (hasSE || hasDC)) return 'FINotAvail'; if (hasFI && !hasSE && hasDC) return 'SENotDone'; if (hasFI && hasSE && !hasDC) return 'DCNotAvail'; if (hasFI && !hasSE && !hasDC) return 'FIAvailSEDCProp'; return 'Others'; + */ + + // New logic + if (!hasFI) return 'FINotAvail'; + if (hasFI && (!hasSE || !hasDC)) return 'FIAvail'; + return 'Others'; }; + /* Old counts commented let allThreeNotAvailCount = basePending.filter(r => getPendingReasonCode(r) === 'AllThreeNotAvail').length; let fiNotAvailCount = basePending.filter(r => getPendingReasonCode(r) === 'FINotAvail').length; let seNotDoneCount = basePending.filter(r => getPendingReasonCode(r) === 'SENotDone').length; let dcNotAvailCount = basePending.filter(r => getPendingReasonCode(r) === 'DCNotAvail').length; let fiAvailSedcPropCount = basePending.filter(r => getPendingReasonCode(r) === 'FIAvailSEDCProp').length; let othersCount = basePending.filter(r => getPendingReasonCode(r) === 'Others').length; + */ + + // New counts + let fiAvailCount = basePending.filter(r => getPendingReasonCode(r) === 'FIAvail').length; + let fiNotAvailCount = basePending.filter(r => getPendingReasonCode(r) === 'FINotAvail').length; + let othersCount = basePending.filter(r => getPendingReasonCode(r) === 'Others').length; // Render the quick filter buttons dynamically const qContainer = document.getElementById('quick-filters-container'); @@ -1504,12 +1536,9 @@ let qHtml = ''; qHtml += renderButton('All', 'Total Pending', totalPendingCount, 'primary'); - qHtml += renderButton('AllThreeNotAvail', 'FI, SE & DC Not Available', allThreeNotAvailCount, 'danger'); - qHtml += renderButton('FINotAvail', 'FI Not Available', fiNotAvailCount, 'warning text-dark'); - qHtml += renderButton('StockDone', 'Stock Not Available', seNotDoneCount, 'info'); - qHtml += renderButton('DCNotAvail', 'DC Not Available', dcNotAvailCount, 'dark'); - qHtml += renderButton('FIAvailSEDCProp', 'FI Available, SE & DC Pending', fiAvailSedcPropCount, 'success'); - qHtml += renderButton('Others', 'Others / Remarks', othersCount, 'secondary'); + qHtml += renderButton('FIAvail', 'FI Available', fiAvailCount, 'success'); + qHtml += renderButton('FINotAvail', 'FI Not Available', fiNotAvailCount, 'danger'); + qHtml += renderButton('Others', 'Others', othersCount, 'secondary'); qContainer.innerHTML = qHtml; } @@ -1812,7 +1841,7 @@ if (modalBody) { modalBody.innerHTML = `
-
+
Basic Details

Financial Year: ${fileObj.fin_year || '-'}

File Number: ${fileObj.file_data_num || '-'}

@@ -1823,31 +1852,123 @@

File Status (Category): ${fileObj.file_status || '-'}

Subject: ${fileObj.subject || '-'}

-
+
Amounts & Files
-

Bal Amount: ${formatAmount(fileObj.bal_amt)}

-

AS File: ${renderViewIcon(fileObj.as_file_path, 'AS File', fileObj.file_data_num)}

-

AS Signed Date: ${formatDate(fileObj.as_file_gen_date)}

-

PO Amount: ${formatAmount(fileObj.po_amt)}

-

PO File: ${renderViewIcon(fileObj.po_file_path, 'PO File', fileObj.file_data_num)}

-

PO completed date: ${formatDate(fileObj.po_file_gen_date)}

-

PI Amount: ${formatAmount(fileObj.pi_amt)}

-

PI File: ${renderViewIcon(fileObj.pi_file_path, 'PI File', fileObj.file_data_num)}

-

PI completed date: ${formatDate(fileObj.pi_file_gen_date)}

-

RTGS File: ${renderViewIcon(fileObj.rtgs_file_path, 'RTGS File', fileObj.file_data_num)}

-

RTGS Date: ${formatDate(fileObj.rtgs_date)}

-

FI Amount: ${formatAmount(fileObj.fi_amt)}

-

FI File: ${renderViewIcon(fileObj.fi_file_path, 'FI File', fileObj.file_data_num)}

-

FI completed date: ${formatDate(fileObj.fi_file_gen_date)}

-

Stock registry File: ${renderViewIcon(fileObj.sr_filepath, 'SR File', fileObj.file_data_num)}

-

DC file: ${renderViewIcon(fileObj.dc_file_path, 'DC File', fileObj.file_data_num)}

-

DC completed date: ${formatDate(fileObj.dc_completed_date)}

-

EJV date: ${formatDate(fileObj.ejv_num_file_gen_date)}

-

EJV File: ${renderViewIcon(fileObj.ejv_num_file_path, 'EJV File', fileObj.file_data_num)}

-

Adjustment Order Final File: ${renderViewIcon(fileObj.as_final_file_path, 'AS Final File', fileObj.file_data_num)}

-

AS Final Date: ${formatDate(fileObj.as_final_file_gen_date)}

-

TC File: ${renderViewIcon(fileObj.tc_file_path, 'TC File', fileObj.file_data_num)}

-

TC Date: ${formatDate(fileObj.tc_file_gen_date)}

+ + +
+
+ Bal Amount: +
${formatAmount(fileObj.bal_amt)}
+
+
+ AS File: +
${renderViewIcon(fileObj.as_file_path, 'AS File', fileObj.file_data_num)}
+
+
+ AS Signed Date: +
${formatDate(fileObj.as_file_gen_date)}
+
+
+ + + + +
+
+ PI Amount: +
${formatAmount(fileObj.pi_amt)}
+
+
+ PI File: +
${renderViewIcon(fileObj.pi_file_path, 'PI File', fileObj.file_data_num)}
+
+
+ PI completed date: +
${formatDate(fileObj.pi_file_gen_date)}
+
+
+ + +
+
+ FI Amount: +
${formatAmount(fileObj.fi_amt)}
+
+
+ FI File: +
${renderViewIcon(fileObj.fi_file_path, 'FI File', fileObj.file_data_num)}
+
+
+ FI completed date: +
${formatDate(fileObj.fi_file_gen_date)}
+
+
+ + +
+
+ RTGS File: +
${renderViewIcon(fileObj.rtgs_file_path, 'RTGS File', fileObj.file_data_num)}
+
+
+ RTGS Date: +
${formatDate(fileObj.rtgs_date)}
+
+
+ Stock registry File: +
${renderViewIcon(fileObj.sr_filepath, 'SR File', fileObj.file_data_num)}
+
+
+ + +
+
+ DC file: +
${renderViewIcon(fileObj.dc_file_path, 'DC File', fileObj.file_data_num)}
+
+
+ DC completed date: +
${formatDate(fileObj.dc_completed_date)}
+
+
+ EJV File: +
${renderViewIcon(fileObj.ejv_num_file_path, 'EJV File', fileObj.file_data_num)}
+
+
+ EJV date: +
${formatDate(fileObj.ejv_num_file_gen_date)}
+
+
+ + +
+
+ Adjustment Order Final File: +
${renderViewIcon(fileObj.as_final_file_path, 'AS Final File', fileObj.file_data_num)}
+
+
+ AS Final Date: +
${formatDate(fileObj.as_final_file_gen_date)}
+
+
+ TC File: +
${renderViewIcon(fileObj.tc_file_path, 'TC File', fileObj.file_data_num)}
+
+
+ TC Date: +
${formatDate(fileObj.tc_file_gen_date)}
+
+
+ + +
+
+ PurchaseOrder File: +
${renderViewIcon(fileObj.po_file_path, 'PO File', fileObj.file_data_num)}
+
+ +
diff --git a/src/main/resources/templates/gccservicepaymenttracker/dashboard.html b/src/main/resources/templates/gccservicepaymenttracker/dashboard.html index d4be027..d9c6f5e 100644 --- a/src/main/resources/templates/gccservicepaymenttracker/dashboard.html +++ b/src/main/resources/templates/gccservicepaymenttracker/dashboard.html @@ -1368,6 +1368,7 @@ Age of Bill (Days) Status Remarks + Mail Screenshot Payment Review Last Updated At
+ +
+ + + +
+
@@ -2572,6 +2588,17 @@ const dispStatus = String(row.resolved_status || getStatusName(row.status) || '').trim(); + let screenshotHtml = '-'; + if (row.mail_screenshot_path) { + let imgUrl = row.mail_screenshot_path; + if (contextPath && contextPath.endsWith('/') && imgUrl.startsWith('/')) { + imgUrl = contextPath + imgUrl.substring(1); + } else { + imgUrl = contextPath + imgUrl; + } + screenshotHtml = ` View`; + } + tr.innerHTML += ` ${formatDate(row.received_date)} ${formatDate(row.invoice_date)} @@ -2584,6 +2611,7 @@ ${calculateAgeOfBill(row.received_date)} ${text(dispStatus)} ${text(row.remarks)} + ${screenshotHtml} ${text(row.payment_review)} ${formatDateTime(row.updated_date)} ${text(row.updated_by_name || '-')} @@ -2610,6 +2638,25 @@ tdActions.appendChild(btnViewPdf); } + if (row.mail_screenshot_path) { + const btnViewScreenshot = document.createElement('button'); + btnViewScreenshot.className = 'btn-grid-action btn-outline-info me-1'; + btnViewScreenshot.style.border = '1px solid #06b6d4'; + btnViewScreenshot.style.color = '#06b6d4'; + btnViewScreenshot.innerHTML = ''; + btnViewScreenshot.title = 'View Screenshot'; + btnViewScreenshot.onclick = () => { + let imgUrl = row.mail_screenshot_path; + if (contextPath && contextPath.endsWith('/') && imgUrl.startsWith('/')) { + imgUrl = contextPath + imgUrl.substring(1); + } else { + imgUrl = contextPath + imgUrl; + } + window.open(imgUrl, '_blank'); + }; + tdActions.appendChild(btnViewScreenshot); + } + // History Button (Available to all logged in users) const btnHistory = document.createElement('button'); btnHistory.className = 'btn-grid-action btn-outline-secondary me-1'; @@ -2841,6 +2888,10 @@ document.getElementById('form-pdf').value = ''; document.getElementById('form-existing-pdf-container').style.display = 'none'; + // Reset Screenshot upload elements + document.getElementById('form-screenshot').value = ''; + document.getElementById('form-existing-screenshot-container').style.display = 'none'; + recordModalObj.show(); } @@ -2984,6 +3035,23 @@ existingPdfContainer.style.display = 'none'; } + // Populate/Reset Screenshot elements + document.getElementById('form-screenshot').value = ''; + const existingScreenshotContainer = document.getElementById('form-existing-screenshot-container'); + const existingScreenshotLink = document.getElementById('form-existing-screenshot-link'); + if (row.mail_screenshot_path) { + existingScreenshotContainer.style.display = 'block'; + let imgUrl = row.mail_screenshot_path; + if (contextPath && contextPath.endsWith('/') && imgUrl.startsWith('/')) { + imgUrl = contextPath + imgUrl.substring(1); + } else { + imgUrl = contextPath + imgUrl; + } + existingScreenshotLink.href = imgUrl; + } else { + existingScreenshotContainer.style.display = 'none'; + } + recordModalObj.show(); } @@ -3061,10 +3129,44 @@ } } + // Upload Screenshot if selected + let screenshotPath = originalRecord ? originalRecord.mail_screenshot_path : null; + const screenshotInput = document.getElementById('form-screenshot'); + if (screenshotInput && screenshotInput.files && screenshotInput.files.length > 0) { + showLoader(true, 'Uploading Screenshot...'); + const formData = new FormData(); + formData.append('file', screenshotInput.files[0]); + + const csrfHeaders = getCsrfHeaders(); + try { + const uploadResponse = await fetch(contextPath + 'gccservicepaymenttracker/api/upload-screenshot', { + method: 'POST', + headers: { + ...csrfHeaders + }, + body: formData + }); + const uploadResult = await uploadResponse.json(); + if (uploadResult.success) { + screenshotPath = uploadResult.filePath; + } else { + alert(uploadResult.error || 'Failed to upload Screenshot.'); + showLoader(false); + return; + } + } catch (err) { + console.error("Upload error:", err); + alert("Failed to upload Screenshot due to connection error."); + showLoader(false); + return; + } + } + const payload = { id, received_date, type, vendor_name: vendorName, subject, amount, invoice_no, receiptno, invoice_date, physical_file_no, tat, status, followup, - myfollowup, order_by, remarks, payment_review, file_path: filePath + myfollowup, order_by, remarks, payment_review, file_path: filePath, + mail_screenshot_path: screenshotPath }; const csrfHeaders = getCsrfHeaders(); @@ -4258,6 +4360,25 @@ tdActions.appendChild(btnViewPdf); } + if (row.mail_screenshot_path) { + const btnViewScreenshot = document.createElement('button'); + btnViewScreenshot.className = 'btn-grid-action btn-outline-info me-1'; + btnViewScreenshot.style.border = '1px solid #06b6d4'; + btnViewScreenshot.style.color = '#06b6d4'; + btnViewScreenshot.innerHTML = ''; + btnViewScreenshot.title = 'View Screenshot'; + btnViewScreenshot.onclick = () => { + let imgUrl = row.mail_screenshot_path; + if (contextPath && contextPath.endsWith('/') && imgUrl.startsWith('/')) { + imgUrl = contextPath + imgUrl.substring(1); + } else { + imgUrl = contextPath + imgUrl; + } + window.open(imgUrl, '_blank'); + }; + tdActions.appendChild(btnViewScreenshot); + } + // History Button const btnHistory = document.createElement('button'); btnHistory.className = 'btn-grid-action btn-outline-secondary me-1'; @@ -4409,6 +4530,7 @@ Age of Bill (Days) Status Remarks + Screenshot Status Payment Review Last Updated At Last Updated By @@ -4495,6 +4617,7 @@ ${calculateAgeOfBill(row.received_date)} ${text(dispStatus)} ${text(row.remarks)} + ${row.mail_screenshot_path ? 'Screenshot Uploaded' : 'Screenshot not uploaded'} ${text(row.payment_review)} ${formatDateTime(row.updated_date)} ${text(row.updated_by_name || '-')} @@ -4712,8 +4835,7 @@ const dispStatus = String(item.resolved_status || getStatusName(item.status) || '').trim(); - // Document button if file path is available - let docBtnHtml = '-'; + let docBtnHtml = ''; if (item.file_path) { let pdfUrl = item.file_path; if (contextPath && contextPath.endsWith('/') && pdfUrl.startsWith('/')) { @@ -4721,7 +4843,19 @@ } else { pdfUrl = contextPath + pdfUrl; } - docBtnHtml = ``; + docBtnHtml += ``; + } + if (item.mail_screenshot_path) { + let imgUrl = item.mail_screenshot_path; + if (contextPath && contextPath.endsWith('/') && imgUrl.startsWith('/')) { + imgUrl = contextPath + imgUrl.substring(1); + } else { + imgUrl = contextPath + imgUrl; + } + docBtnHtml += ``; + } + if (!docBtnHtml) { + docBtnHtml = '-'; } // Resolve type badge diff --git a/src/main/resources/templates/gccservicepaymenttracker/details.html b/src/main/resources/templates/gccservicepaymenttracker/details.html index bd5ad80..dde7f9a 100644 --- a/src/main/resources/templates/gccservicepaymenttracker/details.html +++ b/src/main/resources/templates/gccservicepaymenttracker/details.html @@ -459,6 +459,7 @@ style="max-width: 90px;">STATUS REMARKS + Mail SCREENSHOT PAYMENT REVIEW LAST
UPDATED AT
+ +
+ + + +
+
- - -
- - -
-
@@ -1598,29 +1071,37 @@
- -