Browse Source

added payment processing tracker

master
yoga-agoy 4 months ago
parent
commit
608448f162
  1. 19
      src/main/java/com/gcc_manpower/config/DataSourceConfig.java
  2. 51
      src/main/java/com/gcc_manpower/config/WebConfig.java
  3. 17
      src/main/java/com/gcc_manpower/gccservicedocuments/controller/GccServiceDocumentsController.java
  4. 4
      src/main/java/com/gcc_manpower/gccservicedocuments/service/GccServiceDocumentsService.java
  5. 8
      src/main/java/com/gcc_manpower/gccservicedocuments/service/VideoCompressionService.java
  6. 260
      src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java
  7. 285
      src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java
  8. 2
      src/main/resources/application-local.properties
  9. 4
      src/main/resources/application.properties
  10. 4
      src/main/resources/templates/fragments/navbar.html
  11. 2151
      src/main/resources/templates/gccservicepaymenttracker/dashboard.html
  12. 115
      src/main/resources/templates/gccservicepaymenttracker/login.html

19
src/main/java/com/gcc_manpower/config/DataSourceConfig.java

@ -10,12 +10,13 @@ import javax.sql.DataSource;
@EnableTransactionManagement @EnableTransactionManagement
public class DataSourceConfig { public class DataSourceConfig {
// private static String host = "localhost:3306";
// private static String dbpassword = "root";
private static String host = "localhost:3306";
private static String dbpassword = "root";
// AWS // AWS
private static String host = "gcc-facial-db-instance-1.cf48eqcciziq.ap-south-1.rds.amazonaws.com:3306";
private static String dbpassword = "gcc-facial-password";
// 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") @Bean(name = "mysqlManpowerDataSource")
public DataSource mysqlManpowerDataSource() { public DataSource mysqlManpowerDataSource() {
@ -36,4 +37,14 @@ public class DataSourceConfig {
dataSource.setPassword(dbpassword); dataSource.setPassword(dbpassword);
return dataSource; return dataSource;
} }
@Bean(name = "mysqlPaymentTrackingServiceDataSource")
public DataSource mysqlPaymentTrackingServiceDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://" + host + "/gccpayment_processing_tracker");
dataSource.setUsername("root");
dataSource.setPassword(dbpassword);
return dataSource;
}
} }

51
src/main/java/com/gcc_manpower/config/WebConfig.java

@ -9,24 +9,35 @@ import java.nio.file.Paths;
@Configuration @Configuration
public class WebConfig implements WebMvcConfigurer { public class WebConfig implements WebMvcConfigurer {
@Value("${upload.path:gcc_manpower_uploads}")
private String uploadPathConfig;
@Value("${document.upload.path:gccservicedocuments}")
private String documentUploadPathConfig;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Resolve absolute path (supports both relative and absolute paths)
String uploadPath = Paths.get(uploadPathConfig).toAbsolutePath().toUri().toString();
// Map URL path /gcc_manpower_uploads/** to the configured folder path
registry.addResourceHandler("/gcc_manpower_uploads/**")
.addResourceLocations(uploadPath);
// Map URL path /gccservicedocuments/files/** to the configured document folder path
String docUploadPath = Paths.get(documentUploadPathConfig).toAbsolutePath().toUri().toString();
registry.addResourceHandler("/gccservicedocuments/files/**")
.addResourceLocations(docUploadPath);
}
@Value("${upload.path:gcc_manpower_uploads}")
private String uploadPathConfig;
@Value("${document.upload.path:gccservicedocuments}")
private String documentUploadPathConfig;
@Value("${payment.tracker.upload.path:gccpaymenttrackerfiles}")
private String paymentTrackerUploadPathConfig;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Resolve absolute path (supports both relative and absolute paths)
String uploadPath = Paths.get(uploadPathConfig).toAbsolutePath().toUri().toString();
// Map URL path /gcc_manpower_uploads/** to the configured folder path
registry.addResourceHandler("/gcc_manpower_uploads/**")
.addResourceLocations(uploadPath);
// Map URL path /gccservicedocuments/files/** to the configured document folder
// path
String docUploadPath = Paths.get(documentUploadPathConfig).toAbsolutePath().toUri().toString();
registry.addResourceHandler("/gccservicedocuments/files/**")
.addResourceLocations(docUploadPath);
// Map URL path /gccpaymenttrackerfiles/** to the configured payment tracker
// folder
String trackerUploadPath = Paths.get(paymentTrackerUploadPathConfig).toAbsolutePath().toUri()
.toString();
registry.addResourceHandler("/gccpaymenttrackerfiles/**")
.addResourceLocations(trackerUploadPath);
}
} }

17
src/main/java/com/gcc_manpower/controller/GccServiceDocumentController.java → src/main/java/com/gcc_manpower/gccservicedocuments/controller/GccServiceDocumentsController.java

@ -1,6 +1,7 @@
package com.gcc_manpower.controller;
package com.gcc_manpower.gccservicedocuments.controller;
import com.gcc_manpower.service.GccServiceDocumentService;
import com.gcc_manpower.gccservicedocuments.service.GccServiceDocumentsService;
import com.gcc_manpower.gccservicedocuments.service.VideoCompressionService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -15,13 +16,13 @@ import java.util.Map;
@Controller @Controller
@RequestMapping("/gccservicedocuments") @RequestMapping("/gccservicedocuments")
public class GccServiceDocumentController {
public class GccServiceDocumentsController {
@Autowired @Autowired
private GccServiceDocumentService documentService;
private GccServiceDocumentsService documentService;
@Autowired @Autowired
private com.gcc_manpower.service.VideoCompressionService videoCompressionService;
private VideoCompressionService videoCompressionService;
@Value("${document.upload.path:gccservicedocuments}") @Value("${document.upload.path:gccservicedocuments}")
private String documentUploadPathConfig; private String documentUploadPathConfig;
@ -62,9 +63,9 @@ public class GccServiceDocumentController {
@GetMapping("/api/list") @GetMapping("/api/list")
@ResponseBody @ResponseBody
public List<Map<String, Object>> listDocuments( public List<Map<String, Object>> listDocuments(
@RequestParam(value = "category", required = false) String category,
@RequestParam(value = "fromDate", required = false) String fromDate,
@RequestParam(value = "toDate", required = false) String toDate) {
@RequestParam(value = "category", required = false) String category,
@RequestParam(value = "fromDate", required = false) String fromDate,
@RequestParam(value = "toDate", required = false) String toDate) {
return documentService.searchDocuments(category, fromDate, toDate); return documentService.searchDocuments(category, fromDate, toDate);
} }

4
src/main/java/com/gcc_manpower/service/GccServiceDocumentService.java → src/main/java/com/gcc_manpower/gccservicedocuments/service/GccServiceDocumentsService.java

@ -1,4 +1,4 @@
package com.gcc_manpower.service;
package com.gcc_manpower.gccservicedocuments.service;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -11,7 +11,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
@Service @Service
public class GccServiceDocumentService {
public class GccServiceDocumentsService {
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;

8
src/main/java/com/gcc_manpower/service/VideoCompressionService.java → src/main/java/com/gcc_manpower/gccservicedocuments/service/VideoCompressionService.java

@ -1,4 +1,4 @@
package com.gcc_manpower.service;
package com.gcc_manpower.gccservicedocuments.service;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,10 +27,12 @@ public class VideoCompressionService {
String originalPath = originalFile.getAbsolutePath(); String originalPath = originalFile.getAbsolutePath();
long originalSize = originalFile.length(); long originalSize = originalFile.length();
LOGGER.info("Starting background video compression for file: " + originalPath + " (Size: " + (originalSize / (1024 * 1024)) + " MB)");
LOGGER.info("Starting background video compression for file: " + originalPath + " (Size: "
+ (originalSize / (1024 * 1024)) + " MB)");
// Create a temporary file in the same directory for the compressed output // Create a temporary file in the same directory for the compressed output
File tempFile = new File(originalFile.getParentFile(), "temp_" + System.currentTimeMillis() + "_" + originalFile.getName());
File tempFile = new File(originalFile.getParentFile(),
"temp_" + System.currentTimeMillis() + "_" + originalFile.getName());
try { try {
// Read input file details // Read input file details

260
src/main/java/com/gcc_manpower/gccservicepaymenttracker/controller/GccServicePaymentTrackerController.java

@ -0,0 +1,260 @@
package com.gcc_manpower.gccservicepaymenttracker.controller;
import com.gcc_manpower.gccservicepaymenttracker.service.GccServicePaymentTrackerService;
import com.gcc_manpower.service.AppUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/gccservicepaymenttracker")
public class GccServicePaymentTrackerController {
@Autowired
private GccServicePaymentTrackerService paymentTrackerService;
@Autowired
private AppUserService appUserService;
@Value("${payment.tracker.upload.path:gccpaymenttrackerfiles}")
private String paymentTrackerUploadPathConfig;
@GetMapping({ "", "/", "/dashboard" })
public String viewDashboard(Model model, HttpSession session) {
String username = (String) session.getAttribute("usrName");
if (username == null) {
return "redirect:/gccservicepaymenttracker/login";
}
String usrRole = (String) session.getAttribute("usrRole");
boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole);
List<Map<String, Object>> vendorDetails = appUserService.getVendorDetailsWithCounts();
model.addAttribute("isLoggedIn", true);
model.addAttribute("isAdmin", isAdmin);
model.addAttribute("usrName", username);
model.addAttribute("curPage", "gccservicepaymenttracker");
model.addAttribute("vendorDetails", vendorDetails);
return "gccservicepaymenttracker/dashboard";
}
@GetMapping("/login")
public String login(HttpSession session) {
if (session.getAttribute("usrName") != null) {
return "redirect:/gccservicepaymenttracker/dashboard";
}
return "gccservicepaymenttracker/login";
}
@PostMapping("/api/login")
@ResponseBody
public Map<String, Object> validateLogin(
@RequestParam("usrName") String usrName,
@RequestParam("usrPass") String usrPass,
HttpSession session) {
Map<String, Object> authResult = appUserService.authenticate(usrName, usrPass);
Map<String, Object> response = new HashMap<>();
if (Boolean.TRUE.equals(authResult.get("success"))) {
session.setAttribute("usrName", authResult.get("usrName"));
session.setAttribute("usrRole", authResult.get("usrRole"));
session.setMaxInactiveInterval(1800); // 30 minutes session timeout
response.put("result", true);
response.put("usrName", authResult.get("usrName"));
} else {
response.put("result", false);
response.put("error", authResult.get("error"));
}
return response;
}
@PostMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("usrName");
session.removeAttribute("usrRole");
session.invalidate();
return "redirect:/gccservicepaymenttracker/login";
}
@GetMapping("/logout")
public String logoutGet(HttpSession session) {
session.removeAttribute("usrName");
session.removeAttribute("usrRole");
session.invalidate();
return "redirect:/gccservicepaymenttracker/login";
}
@GetMapping("/api/counts")
@ResponseBody
public ResponseEntity<Map<String, Integer>> getCounts() {
return ResponseEntity.ok()
.cacheControl(CacheControl.noCache().mustRevalidate())
.body(paymentTrackerService.getCountsByCategory());
}
@GetMapping("/api/list")
@ResponseBody
public ResponseEntity<List<Map<String, Object>>> 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<Map<String, Object>> details = paymentTrackerService.searchDetails(category, fromDate, toDate, status);
return ResponseEntity.ok()
.cacheControl(CacheControl.noCache().mustRevalidate())
.body(details);
}
@PostMapping("/api/save")
@ResponseBody
public Map<String, Object> saveDetail(
@RequestBody Map<String, Object> payload,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
String 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");
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 = paymentTrackerService.saveOrUpdateDetail(payload);
response.put("success", saved);
if (!saved) {
response.put("error", "Failed to save details to database.");
}
} catch (Exception e) {
response.put("success", false);
response.put("error", "Error: " + e.getMessage());
}
return response;
}
@PostMapping("/api/delete")
@ResponseBody
public Map<String, Object> deleteDetail(
@RequestParam("id") int id,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
String 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");
if (!"ADMIN".equalsIgnoreCase(usrRole)) {
response.put("success", false);
response.put("error", "Access denied! Only admins can delete records.");
return response;
}
try {
boolean deleted = paymentTrackerService.deleteDetail(id);
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-pdf")
@ResponseBody
public Map<String, Object> uploadPdf(
@RequestParam("file") MultipartFile file,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
String 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");
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 uploadDir = new File(paymentTrackerUploadPathConfig).getAbsoluteFile();
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
String originalFilename = file.getOriginalFilename();
String nameWithoutExt = "pdf";
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(uploadDir, savedFilename).getAbsoluteFile();
file.transferTo(destFile);
String fileWebUrl = "/gccpaymenttrackerfiles/" + 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/types")
@ResponseBody
public List<Map<String, Object>> getActiveTypes() {
return paymentTrackerService.getActiveTypes();
}
@GetMapping("/api/vendors")
@ResponseBody
public List<Map<String, Object>> getActiveVendors() {
return paymentTrackerService.getActiveVendors();
}
}

285
src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java

@ -0,0 +1,285 @@
package com.gcc_manpower.gccservicepaymenttracker.service;
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 javax.sql.DataSource;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class GccServicePaymentTrackerService {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(@Qualifier("mysqlPaymentTrackingServiceDataSource") DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Map<String, Integer> getCountsByCategory() {
Map<String, Integer> counts = new HashMap<>();
counts.put("sd", 0);
counts.put("tax invoice", 0);
counts.put("emd", 0);
counts.put("bank guarantee", 0);
counts.put("delivery chellan", 0);
counts.put("asd", 0);
counts.put("agreement", 0);
String sql = "SELECT "
+ "CASE WHEN p.type REGEXP '^[0-9]+$' THEN t.type_name ELSE p.type END as type_resolved, "
+ "COUNT(*) as cnt "
+ "FROM payment_tracking_details p "
+ "LEFT JOIN type_list t ON p.type REGEXP '^[0-9]+$' AND t.id = p.type "
+ "WHERE p.is_active = 1 AND p.is_delete = 0 "
+ "GROUP BY type_resolved";
try {
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
for (Map<String, Object> row : rows) {
String type = String.valueOf(row.get("type_resolved")).toLowerCase().trim();
Number countNum = (Number) row.get("cnt");
int count = countNum != null ? countNum.intValue() : 0;
// Map database values back to categories
if (type.equals("asd") || type.contains("asd") || type.contains("additional security")) {
counts.put("asd", counts.getOrDefault("asd", 0) + count);
} else if (type.equals("agreement") || type.contains("agreement")) {
counts.put("agreement", counts.getOrDefault("agreement", 0) + count);
} else if (type.equals("sd") || type.contains("security")) {
counts.put("sd", counts.getOrDefault("sd", 0) + count);
} else if (type.contains("invoice") || type.contains("bill")) {
counts.put("tax invoice", counts.getOrDefault("tax invoice", 0) + count);
} else if (type.equals("emd")) {
counts.put("emd", counts.getOrDefault("emd", 0) + count);
} else if (type.contains("guarantee") || type.contains("bg")) {
counts.put("bank guarantee", counts.getOrDefault("bank guarantee", 0) + count);
} else if (type.contains("chellan") || type.contains("challan") || type.contains("dc")) {
counts.put("delivery chellan", counts.getOrDefault("delivery chellan", 0) + count);
}
}
} catch (Exception e) {
System.err.println("Error getting category counts: " + e.getMessage());
}
return counts;
}
public List<Map<String, Object>> searchDetails(String type, String fromDate, String toDate, String status) {
StringBuilder sql = new StringBuilder(
"SELECT p.*, "
+ "t.type_name as resolved_type, "
+ "v.vendor_name as resolved_vendor "
+ "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 "
+ "WHERE p.is_active = 1 AND p.is_delete = 0");
List<Object> params = new ArrayList<>();
if (type != null && !type.trim().isEmpty()) {
String category = type.trim().toLowerCase();
if (category.equals("asd")) {
sql.append(" AND (LOWER(p.type) = 'asd' OR LOWER(p.type) LIKE '%asd%' OR LOWER(t.type_name) LIKE '%asd%' OR LOWER(t.type_name) LIKE '%additional security%')");
} else if (category.equals("agreement")) {
sql.append(" AND (LOWER(p.type) LIKE '%agreement%' OR LOWER(t.type_name) LIKE '%agreement%')");
} else if (category.equals("sd")) {
sql.append(" AND ((LOWER(p.type) = 'sd' OR LOWER(p.type) LIKE '%security%' OR LOWER(t.type_name) LIKE '%security%') "
+ "AND LOWER(p.type) NOT LIKE '%additional security%' AND LOWER(p.type) NOT LIKE '%asd%' "
+ "AND (t.type_name IS NULL OR (LOWER(t.type_name) NOT LIKE '%additional security%' AND LOWER(t.type_name) NOT LIKE '%asd%')))");
} else if (category.equals("tax invoice")) {
sql.append(" AND (LOWER(p.type) LIKE '%invoice%' OR LOWER(p.type) LIKE '%bill%' OR LOWER(t.type_name) LIKE '%invoice%' OR LOWER(t.type_name) LIKE '%bill%')");
} else if (category.equals("emd")) {
sql.append(" AND (LOWER(p.type) = 'emd' OR LOWER(p.type) LIKE '%earnest%' OR LOWER(t.type_name) LIKE '%emd%' OR LOWER(t.type_name) LIKE '%earnest%')");
} else if (category.equals("bank guarantee")) {
sql.append(" AND (LOWER(p.type) LIKE '%guarantee%' OR LOWER(p.type) = 'bg' OR LOWER(t.type_name) LIKE '%guarantee%' OR LOWER(t.type_name) = 'bg')");
} else if (category.equals("delivery chellan")) {
sql.append(" AND (LOWER(p.type) LIKE '%chellan%' OR LOWER(p.type) LIKE '%challan%' OR LOWER(p.type) = 'dc' "
+ "OR LOWER(t.type_name) LIKE '%chellan%' OR LOWER(t.type_name) LIKE '%challan%' OR LOWER(t.type_name) = 'dc')");
} else {
sql.append(" AND (LOWER(p.type) = ? OR LOWER(t.type_name) = ?)");
params.add(category);
params.add(category);
}
}
if (fromDate != null && !fromDate.trim().isEmpty()) {
sql.append(" AND p.received_date >= ?");
params.add(parseDate(fromDate));
}
if (toDate != null && !toDate.trim().isEmpty()) {
sql.append(" AND p.received_date <= ?");
params.add(parseDate(toDate));
}
if (status != null && !status.trim().isEmpty()) {
sql.append(" AND LOWER(p.status) = ?");
params.add(status.trim().toLowerCase());
}
sql.append(" ORDER BY p.order_by ASC, p.received_date DESC, p.id DESC");
try {
return jdbcTemplate.queryForList(sql.toString(), params.toArray());
} catch (Exception e) {
System.err.println("Error searching payment tracking details: " + e.getMessage());
return new ArrayList<>();
}
}
public Map<String, Object> getDetailById(int id) {
String sql = "SELECT * FROM payment_tracking_details WHERE id = ? AND is_active = 1 AND is_delete = 0";
try {
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, id);
if (list != null && !list.isEmpty()) {
return list.get(0);
}
} catch (Exception e) {
System.err.println("Error fetching payment tracking detail ID " + id + ": " + e.getMessage());
}
return null;
}
public boolean saveOrUpdateDetail(Map<String, Object> payload) {
Object idObj = payload.get("id");
java.sql.Date receivedDate = parseDate(payload.get("received_date"));
String vendorName = (String) payload.get("vendor_name");
String subject = (String) payload.get("subject");
String amount = (String) payload.get("amount");
String invoiceNo = (String) payload.get("invoice_no");
String receiptNo = payload.get("receiptno") != null ? String.valueOf(payload.get("receiptno")) : null;
java.sql.Date invoiceDate = parseDate(payload.get("invoice_date"));
String type = (String) payload.get("type");
String physicalFileNo = (String) payload.get("physical_file_no");
String remarks = (String) payload.get("remarks");
String tat = (String) payload.get("tat");
String status = (String) payload.get("status");
String followup = (String) payload.get("followup");
String myfollowup = (String) payload.get("myfollowup");
String orderBy = (String) payload.get("order_by");
String filePath = (String) payload.get("file_path");
if (idObj != null && !String.valueOf(idObj).trim().isEmpty()) {
// Update
int id = Integer.parseInt(String.valueOf(idObj).trim());
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 = ? "
+ "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);
return rows > 0;
} catch (Exception e) {
System.err.println("Error updating payment tracking detail ID " + id + ": " + e.getMessage());
return false;
}
} else {
// 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, "
+ "followup, myfollowup, is_active, is_delete, order_by, file_path"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?)";
try {
int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo,
receiptNo, invoiceDate, type, physicalFileNo, remarks,
tat, status, followup, myfollowup, orderBy, filePath);
return rows > 0;
} catch (Exception e) {
System.err.println("Error saving payment tracking detail: " + e.getMessage());
return false;
}
}
}
public boolean deleteDetail(int id) {
String sql = "UPDATE payment_tracking_details SET is_active = 0, is_delete = 1 WHERE id = ?";
try {
int rows = jdbcTemplate.update(sql, id);
return rows > 0;
} catch (Exception e) {
System.err.println("Error soft-deleting payment tracking detail ID " + id + ": " + e.getMessage());
return false;
}
}
private java.sql.Date parseDate(Object obj) {
if (obj == null || String.valueOf(obj).trim().isEmpty()) {
return null;
}
String str = String.valueOf(obj).trim();
try {
// Support both yyyy-MM-dd and dd/MM/yyyy
if (str.contains("/")) {
String[] parts = str.split("/");
if (parts.length == 3) {
// Convert dd/MM/yyyy to yyyy-MM-dd
str = parts[2] + "-" + parts[1] + "-" + parts[0];
}
}
return java.sql.Date.valueOf(str);
} catch (Exception e) {
System.err.println("Error parsing date '" + obj + "': " + e.getMessage());
return null;
}
}
private java.sql.Timestamp parseTimestamp(Object obj) {
if (obj == null || String.valueOf(obj).trim().isEmpty()) {
return null;
}
String str = String.valueOf(obj).trim();
try {
if (str.contains("/")) {
// Parse format dd/MM/yyyy HH:mm:ss or similar if provided
// For simplicity, fallback to current time or standard parsing if it fails
}
if (str.contains("T")) {
str = str.replace("T", " ");
}
if (str.length() == 16) {
str = str + ":00";
}
return java.sql.Timestamp.valueOf(str);
} catch (Exception e) {
// Try parsing as DATE first, then convert to TIMESTAMP
java.sql.Date d = parseDate(obj);
if (d != null) {
return new java.sql.Timestamp(d.getTime());
}
System.err.println("Error parsing timestamp '" + obj + "': " + e.getMessage());
return null;
}
}
public List<Map<String, Object>> getActiveTypes() {
String sql = "SELECT id, type_name FROM type_list "
+ "WHERE is_active = 1 AND is_delete = 0 "
+ "ORDER BY order_by ASC, type_name ASC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {
System.err.println("Error fetching active types: " + e.getMessage());
return new ArrayList<>();
}
}
public List<Map<String, Object>> getActiveVendors() {
String sql = "SELECT id, vendor_name FROM vendors_list "
+ "WHERE is_active = 1 AND is_delete = 0 "
+ "ORDER BY order_by ASC, vendor_name ASC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {
System.err.println("Error fetching active vendors: " + e.getMessage());
return new ArrayList<>();
}
}
}

2
src/main/resources/application-local.properties

@ -31,7 +31,7 @@ logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql=TRACE logging.level.org.hibernate.type.descriptor.sql=TRACE
# Production context path # Production context path
server.servlet.context-path=/
server.servlet.context-path=/gcc_manpower
# Disable Thymeleaf caching for hot-swapping templates # Disable Thymeleaf caching for hot-swapping templates
spring.thymeleaf.cache=false spring.thymeleaf.cache=false

4
src/main/resources/application.properties

@ -1,5 +1,5 @@
#spring.profiles.active=local
spring.profiles.active=prod
spring.profiles.active=local
#spring.profiles.active=prod
spring.application.name=gcc-manpower spring.application.name=gcc-manpower
server.servlet.session.timeout=10m server.servlet.session.timeout=10m

4
src/main/resources/templates/fragments/navbar.html

@ -16,6 +16,10 @@
class=" fa-solid fa-house"></i> class=" fa-solid fa-house"></i>
Home Home
</a></li> </a></li>
<li class="nav-item"><a class="nav-link" th:href="@{/gccservicepaymenttracker/}"> <i
class="fa-solid fa-file-invoice-dollar"></i>
Payment Tracker
</a></li>
<!-- Admin-only links, visible only if isAdmin is true --> <!-- Admin-only links, visible only if isAdmin is true -->
<th:block th:if="${isAdmin}"> <th:block th:if="${isAdmin}">
<li class="nav-item"> <li class="nav-item">

2151
src/main/resources/templates/gccservicepaymenttracker/dashboard.html
File diff suppressed because it is too large
View File

115
src/main/resources/templates/gccservicepaymenttracker/login.html

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GCC Payment Tracker - Login</title>
<!-- CSRF Tokens -->
<meta th:if="${_csrf != null}" name="_csrf" th:content="${_csrf.token}" />
<meta th:if="${_csrf != null}" name="_csrf_header" th:content="${_csrf.headerName}" />
<!-- Include CSS -->
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/login-form-styles.css}">
<!-- Awesome Font -->
<link th:href="@{/assets/font-awesome-6.5.2/css/all.min.css}" rel="stylesheet" type="text/css">
</head>
<body>
<div class="container">
<img th:src="@{/assets/header-images/gcc1.png}" alt="Greater Chennai Corporation" class="small-logo">
<h3>GREATER CHENNAI CORPORATION</h3>
<h4>பெருநகர சென்னை மாநகராட்சி</h4>
<br>
<h3>GCC Service Payment Processing Tracker</h3>
<br>
<h2>Login</h2>
<div id="error-box"
style="color: #c0392b; font-weight: 500; font-size: 0.85rem; margin-bottom: 1.5rem; text-align: center; display: none;">
<i class="fa-solid fa-circle-exclamation"></i> <span id="error-text">Invalid username or password!</span>
</div>
<form id="loginForm" onsubmit="handleLogin(event)">
<div class="input-field">
<input type="text" name="usrName" id="usrName" placeholder="Enter Username" required
autocomplete="username">
<div class="underline"></div>
</div>
<div class="input-field">
<input type="password" name="usrPass" id="usrPass" placeholder="Enter Password" required
autocomplete="current-password">
<div class="underline"></div>
</div>
<input type="submit" value="Login" id="submit-btn">
</form>
</div>
<!-- Footer Fragment -->
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
<!-- Custom JS -->
<script th:inline="javascript">
const contextPath = /*[[@{/}]]*/ '';
</script>
<script>
function handleLogin(event) {
event.preventDefault();
const errorBox = document.getElementById("error-box");
const errorText = document.getElementById("error-text");
const submitBtn = document.getElementById("submit-btn");
errorBox.style.display = "none";
submitBtn.disabled = true;
submitBtn.value = 'Authenticating...';
const username = document.getElementById("usrName").value;
const password = document.getElementById("usrPass").value;
const urlParams = new URLSearchParams();
urlParams.append('usrName', username);
urlParams.append('usrPass', password);
// Fetch CSRF tokens
const csrfToken = document.querySelector("meta[name='_csrf']")?.getAttribute("content");
const csrfHeader = document.querySelector("meta[name='_csrf_header']")?.getAttribute("content");
const headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
if (csrfToken && csrfHeader) {
headers[csrfHeader] = csrfToken;
}
fetch(contextPath + 'gccservicepaymenttracker/api/login', {
method: 'POST',
headers: headers,
body: urlParams
})
.then(response => response.json())
.then(data => {
if (data.result) {
window.location.href = contextPath + 'gccservicepaymenttracker/dashboard';
} else {
submitBtn.disabled = false;
submitBtn.value = 'Login';
errorText.textContent = data.error || "Authentication failed.";
errorBox.style.display = "block";
}
})
.catch(err => {
console.error("Login Error:", err);
submitBtn.disabled = false;
submitBtn.value = 'Login';
errorText.textContent = "Network error. Please try again later.";
errorBox.style.display = "block";
});
}
</script>
</body>
</html>
Loading…
Cancel
Save