Browse Source

new changes added adjustment management controller, service, pages and manpower standby count, and payment tracker last updated files

master
yoganandhank 3 months ago
parent
commit
ce5a5652a6
  1. 10
      src/main/java/com/gcc_manpower/config/DataSourceConfig.java
  2. 14
      src/main/java/com/gcc_manpower/config/SecurityConfig.java
  3. 9
      src/main/java/com/gcc_manpower/config/WebConfig.java
  4. 307
      src/main/java/com/gcc_manpower/gccadjustmentmanagement/controller/AdjustmentController.java
  5. 214
      src/main/java/com/gcc_manpower/gccadjustmentmanagement/service/AdjustmentService.java
  6. 26
      src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java
  7. 102
      src/main/java/com/gcc_manpower/service/AppUserService.java
  8. 28
      src/main/resources/static/assets/css/home-styles.css
  9. 22
      src/main/resources/templates/fragments/adjustment.html
  10. 1661
      src/main/resources/templates/gccadjustmentmanagement/dashboard.html
  11. 89
      src/main/resources/templates/gccadjustmentmanagement/login.html
  12. 56
      src/main/resources/templates/gccservicepaymenttracker/dashboard.html
  13. 35
      src/main/resources/templates/gccservicepaymenttracker/details.html
  14. 82
      src/main/resources/templates/home/home.html
  15. 75
      src/main/resources/templates/home/view-login-details.html

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

@ -57,4 +57,14 @@ public class DataSourceConfig {
dataSource.setPassword(dbpassword); dataSource.setPassword(dbpassword);
return dataSource; return dataSource;
} }
@Bean(name = "mysqladjustmentDataSource")
public DataSource mysqladjustmentDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://" + host + "/gcc_adjustment_management");
dataSource.setUsername("root");
dataSource.setPassword(dbpassword);
return dataSource;
}
} }

14
src/main/java/com/gcc_manpower/config/SecurityConfig.java

@ -68,14 +68,17 @@ public class SecurityConfig {
"/gccservicepaymenttracker/login", "/gccservicepaymenttracker/login",
"/gccservicepaymenttracker/api/login", "/gccservicepaymenttracker/api/login",
"/gccservicedocuments/login", "/gccservicedocuments/login",
"/gccservicedocuments/api/login")
"/gccservicedocuments/api/login",
"/gccadjustmentmanagement/login",
"/gccadjustmentmanagement/api/login")
.permitAll() .permitAll()
// Allow logout endpoints // Allow logout endpoints
.requestMatchers( .requestMatchers(
"/usr-logout", "/usr-logout",
"/gcctonermanagement/logout", "/gcctonermanagement/logout",
"/gccservicepaymenttracker/logout", "/gccservicepaymenttracker/logout",
"/gccservicedocuments/logout")
"/gccservicedocuments/logout",
"/gccadjustmentmanagement/logout")
.permitAll() .permitAll()
// Require authentication for all other requests // Require authentication for all other requests
.anyRequest().authenticated()) .anyRequest().authenticated())
@ -85,7 +88,8 @@ public class SecurityConfig {
"/api/val-app-usr-login", "/api/val-app-usr-login",
"/gcctonermanagement/api/login", "/gcctonermanagement/api/login",
"/gccservicepaymenttracker/api/login", "/gccservicepaymenttracker/api/login",
"/gccservicedocuments/api/login"))
"/gccservicedocuments/api/login",
"/gccadjustmentmanagement/api/login"))
// Session management configuration with module-aware invalid session // Session management configuration with module-aware invalid session
// redirection // redirection
.sessionManagement(session -> session .sessionManagement(session -> session
@ -99,6 +103,8 @@ public class SecurityConfig {
response.sendRedirect(request.getContextPath() + "/gcctonermanagement/login"); response.sendRedirect(request.getContextPath() + "/gcctonermanagement/login");
} else if (requestURI.contains("/gccservicedocuments")) { } else if (requestURI.contains("/gccservicedocuments")) {
response.sendRedirect(request.getContextPath() + "/gccservicedocuments/login"); response.sendRedirect(request.getContextPath() + "/gccservicedocuments/login");
} else if (requestURI.contains("/gccadjustmentmanagement")) {
response.sendRedirect(request.getContextPath() + "/gccadjustmentmanagement/login");
} else { } else {
response.sendRedirect(request.getContextPath() + "/login"); response.sendRedirect(request.getContextPath() + "/login");
} }
@ -113,6 +119,8 @@ public class SecurityConfig {
response.sendRedirect(request.getContextPath() + "/gcctonermanagement/login"); response.sendRedirect(request.getContextPath() + "/gcctonermanagement/login");
} else if (requestURI.contains("/gccservicedocuments")) { } else if (requestURI.contains("/gccservicedocuments")) {
response.sendRedirect(request.getContextPath() + "/gccservicedocuments/login"); response.sendRedirect(request.getContextPath() + "/gccservicedocuments/login");
} else if (requestURI.contains("/gccadjustmentmanagement")) {
response.sendRedirect(request.getContextPath() + "/gccadjustmentmanagement/login");
} else { } else {
response.sendRedirect(request.getContextPath() + "/login"); response.sendRedirect(request.getContextPath() + "/login");
} }

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

@ -21,6 +21,9 @@ public class WebConfig implements WebMvcConfigurer {
@Value("${toner.upload.path:gcctonermanagementfiles}") @Value("${toner.upload.path:gcctonermanagementfiles}")
private String tonerUploadPathConfig; private String tonerUploadPathConfig;
@Value("${adjustment.upload.path:gccadjustmentmanagementfiles}")
private String adjustmentUploadPathConfig;
@Override @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Resolve absolute path (supports both relative and absolute paths) // Resolve absolute path (supports both relative and absolute paths)
@ -48,5 +51,11 @@ public class WebConfig implements WebMvcConfigurer {
.toString(); .toString();
registry.addResourceHandler("/gcctonermanagementfiles/**") registry.addResourceHandler("/gcctonermanagementfiles/**")
.addResourceLocations(tonerUploadPath); .addResourceLocations(tonerUploadPath);
// Map URL path /gccadjustmentmanagementfiles/** to the configured adjustment folder
String adjustmentUploadPath = Paths.get(adjustmentUploadPathConfig).toAbsolutePath().toUri()
.toString();
registry.addResourceHandler("/gccadjustmentmanagementfiles/**")
.addResourceLocations(adjustmentUploadPath);
} }
} }

307
src/main/java/com/gcc_manpower/gccadjustmentmanagement/controller/AdjustmentController.java

@ -0,0 +1,307 @@
package com.gcc_manpower.gccadjustmentmanagement.controller;
import java.io.File;
import java.util.ArrayList;
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.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
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.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.gccadjustmentmanagement.service.AdjustmentService;
import com.gcc_manpower.service.AppUserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@Controller
@RequestMapping("/gccadjustmentmanagement")
public class AdjustmentController {
@Autowired
private AdjustmentService adjustmentService;
@Autowired
private AppUserService appUserService;
@Autowired
private AuthenticationManager authenticationManager;
@Value("${adjustment.upload.path:gccadjustmentmanagementfiles}")
private String adjustmentUploadPathConfig;
@GetMapping({ "", "/", "/dashboard" })
public String viewDashboard(Model model, HttpSession session) {
String username = (String) session.getAttribute("adj_usrName");
if (username == null) {
return "redirect:/gccadjustmentmanagement/login";
}
String usrRole = (String) session.getAttribute("adj_usrRole");
boolean isAdmin = "ADMIN".equalsIgnoreCase(usrRole);
model.addAttribute("isLoggedIn", true);
model.addAttribute("isAdmin", isAdmin);
model.addAttribute("usrName", username);
model.addAttribute("curPage", "gccadjustmentmanagement");
return "gccadjustmentmanagement/dashboard";
}
@GetMapping("/login")
public String login(HttpSession session) {
return "gccadjustmentmanagement/login";
}
@PostMapping("/api/login")
@ResponseBody
public Map<String, Object> validateLogin(
@RequestParam("usrName") String usrName,
@RequestParam("usrPass") String usrPass,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(usrName,
usrPass);
Authentication authentication = authenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
String role = authentication.getAuthorities().stream()
.map(auth -> auth.getAuthority().replace("ROLE_", ""))
.findFirst()
.orElse("USER");
session.setAttribute("adj_usrName", usrName);
session.setAttribute("adj_usrRole", role);
session.setMaxInactiveInterval(1800); // 30 minutes session timeout
response.put("result", true);
response.put("usrName", usrName);
} catch (AuthenticationException e) {
response.put("result", false);
response.put("error", e.getMessage() != null ? e.getMessage() : "Invalid username or password!");
}
return response;
}
@GetMapping("/logout")
public String logout(HttpSession session, HttpServletRequest request) {
SecurityContextHolder.clearContext();
if (session != null) {
session.removeAttribute("adj_usrName");
session.removeAttribute("adj_usrRole");
session.invalidate();
}
request.getSession(true);
return "redirect:/gccadjustmentmanagement/login";
}
@GetMapping("/api/advance-types")
@ResponseBody
public List<Map<String, Object>> getAdvanceTypes(HttpSession session) {
if (session.getAttribute("adj_usrName") == null) {
return java.util.Collections.emptyList();
}
return adjustmentService.getAdvanceTypes();
}
@GetMapping("/api/fund-types")
@ResponseBody
public List<Map<String, Object>> getFundTypes(HttpSession session) {
if (session.getAttribute("adj_usrName") == null) {
return java.util.Collections.emptyList();
}
return adjustmentService.getFundTypes();
}
@GetMapping("/api/list")
@ResponseBody
public List<Map<String, Object>> getFilesList(HttpSession session) {
if (session.getAttribute("adj_usrName") == null) {
return java.util.Collections.emptyList();
}
return adjustmentService.getAllFiles();
}
@PostMapping("/api/save")
@ResponseBody
public Map<String, Object> saveAdjustment(
@RequestParam(value = "id", required = false) Integer id,
@RequestParam(value = "vend_name", required = false) String vendName,
@RequestParam(value = "fin_year", required = false) String finYear,
@RequestParam(value = "adv_type_id", required = false) String advTypeId,
@RequestParam(value = "fund_type_id", required = false) String fundTypeId,
@RequestParam(value = "bal_amt", required = false) Double balAmt,
@RequestParam(value = "file_remark", required = false) String fileRemark,
@RequestParam(value = "file_data_num", required = false) String fileDataNum,
@RequestParam(value = "pi_amt", required = false) Double piAmt,
@RequestParam(value = "pi_file_gen_date", required = false) String piFileGenDate,
@RequestParam(value = "fi_amt", required = false) String fiAmt,
@RequestParam(value = "fi_file_gen_date", required = false) String fiFileGenDate,
@RequestParam(value = "tc_file_gen_date", required = false) String tcFileGenDate,
@RequestParam(value = "ejv_num", required = false) String ejvNum,
@RequestParam(value = "ejv_num_file_gen_date", required = false) String ejvNumFileGenDate,
@RequestParam(value = "as_file_gen_date", required = false) String asFileGenDate,
@RequestParam(value = "as_final_file_gen_date", required = false) String asFinalFileGenDate,
@RequestParam(value = "file", required = false) MultipartFile file,
@RequestParam(value = "poFile", required = false) MultipartFile poFile,
@RequestParam(value = "dcFile", required = false) MultipartFile dcFile,
@RequestParam(value = "irFile", required = false) MultipartFile irFile,
@RequestParam(value = "srFile", required = false) MultipartFile srFile,
@RequestParam(value = "piFile", required = false) MultipartFile piFile,
@RequestParam(value = "fiFile", required = false) MultipartFile fiFile,
@RequestParam(value = "tcFile", required = false) MultipartFile tcFile,
@RequestParam(value = "rtgsFile", required = false) MultipartFile rtgsFile,
@RequestParam(value = "ejvFile", required = false) MultipartFile ejvFile,
@RequestParam(value = "asFile", required = false) MultipartFile asFile,
@RequestParam(value = "asFinalFile", required = false) MultipartFile asFinalFile,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
String username = (String) session.getAttribute("adj_usrName");
if (username == null) {
response.put("success", false);
response.put("error", "Unauthorized access. Please log in.");
return response;
}
try {
Map<String, Object> existing = null;
if (id != null) {
existing = adjustmentService.getAdjustmentFileById(id);
if (existing == null) {
response.put("success", false);
response.put("error", "Record not found.");
return response;
}
}
File uploadDir = new File(adjustmentUploadPathConfig).getAbsoluteFile();
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// Save files or keep existing
String filePath = saveFile(file, "file", uploadDir, existing != null ? (String) existing.get("file_path") : null);
String poPath = saveFile(poFile, "po", uploadDir, existing != null ? (String) existing.get("po_file_path") : null);
String dcPath = saveFile(dcFile, "dc", uploadDir, existing != null ? (String) existing.get("dc_file_path") : null);
String irPath = saveFile(irFile, "ir", uploadDir, existing != null ? (String) existing.get("ir_file_path") : null);
String srPath = saveFile(srFile, "sr", uploadDir, existing != null ? (String) existing.get("sr_filepath") : null);
String piPath = saveFile(piFile, "pi", uploadDir, existing != null ? (String) existing.get("pi_file_path") : null);
String fiPath = saveFile(fiFile, "fi", uploadDir, existing != null ? (String) existing.get("fi_file_path") : null);
String tcPath = saveFile(tcFile, "tc", uploadDir, existing != null ? (String) existing.get("tc_file_path") : null);
String rtgsPath = saveFile(rtgsFile, "rtgs", uploadDir, existing != null ? (String) existing.get("rtgs_file_path") : null);
String ejvPath = saveFile(ejvFile, "ejv", uploadDir, existing != null ? (String) existing.get("ejv_num_file_path") : null);
String asPath = saveFile(asFile, "as", uploadDir, existing != null ? (String) existing.get("as_file_path") : null);
String asFinalPath = saveFile(asFinalFile, "as_final", uploadDir, existing != null ? (String) existing.get("as_final_file_path") : null);
// Build data map
Map<String, Object> data = new HashMap<>();
if (id != null) data.put("id", id);
data.put("vend_name", vendName);
data.put("fin_year", finYear);
data.put("adv_type_id", advTypeId);
data.put("fund_type_id", fundTypeId);
data.put("bal_amt", balAmt);
data.put("file_remark", fileRemark);
data.put("file_data_num", fileDataNum);
data.put("pi_amt", piAmt);
data.put("pi_file_gen_date", piFileGenDate);
data.put("fi_amt", fiAmt);
data.put("fi_file_gen_date", fiFileGenDate);
data.put("tc_file_gen_date", tcFileGenDate);
data.put("ejv_num", ejvNum);
data.put("ejv_num_file_gen_date", ejvNumFileGenDate);
data.put("as_file_gen_date", asFileGenDate);
data.put("as_final_file_gen_date", asFinalFileGenDate);
// Paths
data.put("file_path", filePath);
data.put("po_file_path", poPath);
data.put("dc_file_path", dcPath);
data.put("ir_file_path", irPath);
data.put("sr_filepath", srPath);
data.put("pi_file_path", piPath);
data.put("fi_file_path", fiPath);
data.put("tc_file_path", tcPath);
data.put("rtgs_file_path", rtgsPath);
data.put("ejv_num_file_path", ejvPath);
data.put("as_file_path", asPath);
data.put("as_final_file_path", asFinalPath);
boolean saved = adjustmentService.saveAdjustmentFile(data, username);
if (saved) {
response.put("success", true);
response.put("message", "Adjustment file details saved successfully.");
} else {
response.put("success", false);
response.put("error", "Failed to save details in database.");
}
} catch (Exception e) {
response.put("success", false);
response.put("error", "Error occurred: " + e.getMessage());
e.printStackTrace();
}
return response;
}
@PostMapping("/api/delete")
@ResponseBody
public Map<String, Object> deleteAdjustment(
@RequestParam("id") Integer id,
@RequestParam(value = "remarks", required = false) String remarks,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
String username = (String) session.getAttribute("adj_usrName");
if (username == null) {
response.put("success", false);
response.put("error", "Unauthorized access. Please log in.");
return response;
}
boolean deleted = adjustmentService.deleteAdjustmentFile(id, remarks != null ? remarks : "Deleted by user", username);
if (deleted) {
response.put("success", true);
response.put("message", "Record deleted successfully.");
} else {
response.put("success", false);
response.put("error", "Failed to delete record.");
}
return response;
}
private String saveFile(MultipartFile f, String prefix, File dir, String currentPath) throws Exception {
if (f == null || f.isEmpty()) {
return currentPath;
}
String orig = f.getOriginalFilename();
String ext = orig != null && orig.lastIndexOf('.') > 0 ? orig.substring(orig.lastIndexOf('.')) : ".pdf";
String filename = prefix + "_" + System.currentTimeMillis() + ext;
File dest = new File(dir, filename).getAbsoluteFile();
f.transferTo(dest);
return "gccadjustmentmanagementfiles/" + filename;
}
}

214
src/main/java/com/gcc_manpower/gccadjustmentmanagement/service/AdjustmentService.java

@ -0,0 +1,214 @@
package com.gcc_manpower.gccadjustmentmanagement.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
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;
@Service
public class AdjustmentService {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(@Qualifier("mysqladjustmentDataSource") DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getAdvanceTypes() {
String sql = "SELECT id, adv_type FROM advance_type_list WHERE is_active = 1 AND is_delete = 0 ORDER BY CAST(order_by AS UNSIGNED) ASC, id ASC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {
System.err.println("Error fetching advance types: " + e.getMessage());
return new ArrayList<>();
}
}
public List<Map<String, Object>> getFundTypes() {
String sql = "SELECT id, fund_type FROM fund_type_list WHERE is_active = 1 AND is_delete = 0 ORDER BY CAST(order_by AS UNSIGNED) ASC, id ASC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {
System.err.println("Error fetching fund types: " + e.getMessage());
return new ArrayList<>();
}
}
public List<Map<String, Object>> getAllFiles() {
String sql = "SELECT a.*, " +
"adv.adv_type AS resolved_adv_type, " +
"f.fund_type AS resolved_fund_type " +
"FROM adjustment_file_details a " +
"LEFT JOIN advance_type_list adv ON TRIM(a.adv_type_id) REGEXP '^[0-9]+$' AND adv.id = CAST(TRIM(a.adv_type_id) AS UNSIGNED) " +
"LEFT JOIN fund_type_list f ON TRIM(a.fund_type_id) REGEXP '^[0-9]+$' AND f.id = CAST(TRIM(a.fund_type_id) AS UNSIGNED) " +
"WHERE a.is_active = 1 AND a.is_delete = 0 " +
"ORDER BY a.updated_date DESC, a.id DESC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {
System.err.println("Error fetching adjustment files: " + e.getMessage());
return new ArrayList<>();
}
}
public Map<String, Object> getAdjustmentFileById(int id) {
String sql = "SELECT * FROM adjustment_file_details WHERE id = ? AND is_active = 1 AND is_delete = 0";
try {
return jdbcTemplate.queryForMap(sql, id);
} catch (Exception e) {
return null;
}
}
public boolean saveAdjustmentFile(Map<String, Object> data, String username) {
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
Object idVal = data.get("id");
if (idVal != null && !String.valueOf(idVal).trim().isEmpty()) {
// Update
int id = Integer.parseInt(String.valueOf(idVal).trim());
String sql = "UPDATE adjustment_file_details SET " +
"as_file_gen_date = ?, as_file_path = ?, as_final_file_gen_date = ?, as_final_file_path = ?, " +
"bal_amt = ?, dc_file_path = ?, ejv_num = ?, ejv_num_file_gen_date = ?, ejv_num_file_path = ?, " +
"fi_amt = ?, fi_file_gen_date = ?, fi_file_path = ?, file_data_num = ?, file_path = ?, " +
"file_remark = ?, fin_year = ?, ir_file_path = ?, pi_amt = ?, pi_file_gen_date = ?, " +
"pi_file_path = ?, po_file_path = ?, rtgs_file_path = ?, sr_filepath = ?, tc_file_gen_date = ?, " +
"tc_file_path = ?, vend_name = ?, adv_type_id = ?, fund_type_id = ?, updated_date = ?, " +
"updated_by = ? WHERE id = ?";
try {
int rows = jdbcTemplate.update(sql,
parseDate(data.get("as_file_gen_date")),
data.get("as_file_path"),
data.get("as_final_file_gen_date"),
data.get("as_final_file_path"),
parseDouble(data.get("bal_amt")),
data.get("dc_file_path"),
data.get("ejv_num"),
parseDate(data.get("ejv_num_file_gen_date")),
data.get("ejv_num_file_path"),
data.get("fi_amt"),
parseDate(data.get("fi_file_gen_date")),
data.get("fi_file_path"),
data.get("file_data_num"),
data.get("file_path"),
data.get("file_remark"),
data.get("fin_year"),
data.get("ir_file_path"),
parseDouble(data.get("pi_amt")),
parseDate(data.get("pi_file_gen_date")),
data.get("pi_file_path"),
data.get("po_file_path"),
data.get("rtgs_file_path"),
data.get("sr_filepath"),
parseDate(data.get("tc_file_gen_date")),
data.get("tc_file_path"),
data.get("vend_name"),
data.get("adv_type_id"),
data.get("fund_type_id"),
now,
username,
id
);
return rows > 0;
} catch (Exception e) {
System.err.println("Error updating adjustment file: " + e.getMessage());
e.printStackTrace();
return false;
}
} else {
// Insert
String sql = "INSERT INTO adjustment_file_details (" +
"as_file_gen_date, as_file_path, as_final_file_gen_date, as_final_file_path, " +
"bal_amt, dc_file_path, ejv_num, ejv_num_file_gen_date, ejv_num_file_path, " +
"fi_amt, fi_file_gen_date, fi_file_path, file_data_num, file_path, " +
"file_remark, fin_year, ir_file_path, pi_amt, pi_file_gen_date, " +
"pi_file_path, po_file_path, rtgs_file_path, sr_filepath, tc_file_gen_date, " +
"tc_file_path, vend_name, adv_type_id, fund_type_id, cdate, updated_date, " +
"updated_by, is_active, is_delete) VALUES " +
"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0)";
try {
int rows = jdbcTemplate.update(sql,
parseDate(data.get("as_file_gen_date")),
data.get("as_file_path"),
data.get("as_final_file_gen_date"),
data.get("as_final_file_path"),
parseDouble(data.get("bal_amt")),
data.get("dc_file_path"),
data.get("ejv_num"),
parseDate(data.get("ejv_num_file_gen_date")),
data.get("ejv_num_file_path"),
data.get("fi_amt"),
parseDate(data.get("fi_file_gen_date")),
data.get("fi_file_path"),
data.get("file_data_num"),
data.get("file_path"),
data.get("file_remark"),
data.get("fin_year"),
data.get("ir_file_path"),
parseDouble(data.get("pi_amt")),
parseDate(data.get("pi_file_gen_date")),
data.get("pi_file_path"),
data.get("po_file_path"),
data.get("rtgs_file_path"),
data.get("sr_filepath"),
parseDate(data.get("tc_file_gen_date")),
data.get("tc_file_path"),
data.get("vend_name"),
data.get("adv_type_id"),
data.get("fund_type_id"),
now,
now,
username
);
return rows > 0;
} catch (Exception e) {
System.err.println("Error saving adjustment file: " + e.getMessage());
e.printStackTrace();
return false;
}
}
}
public boolean deleteAdjustmentFile(int id, String remarks, String username) {
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
String sql = "UPDATE adjustment_file_details SET is_active = 0, is_delete = 1, file_remark = CONCAT(COALESCE(file_remark, ''), '\nDeleted: ', ?), updated_by = ?, updated_date = ? WHERE id = ?";
try {
int rows = jdbcTemplate.update(sql, remarks, username, now, id);
return rows > 0;
} catch (Exception e) {
System.err.println("Error deleting adjustment file: " + e.getMessage());
e.printStackTrace();
return false;
}
}
private java.sql.Date parseDate(Object val) {
if (val == null || String.valueOf(val).trim().isEmpty()) {
return null;
}
try {
return java.sql.Date.valueOf(String.valueOf(val).trim());
} catch (Exception e) {
return null;
}
}
private Double parseDouble(Object val) {
if (val == null || String.valueOf(val).trim().isEmpty()) {
return null;
}
try {
return Double.parseDouble(String.valueOf(val).trim());
} catch (Exception e) {
return null;
}
}
}

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

@ -207,6 +207,7 @@ public class GccServicePaymentTrackerService {
String myfollowup = (String) payload.get("myfollowup"); String myfollowup = (String) payload.get("myfollowup");
String orderBy = (String) payload.get("order_by"); String orderBy = (String) payload.get("order_by");
String filePath = (String) payload.get("file_path"); String filePath = (String) payload.get("file_path");
String paymentReview = (String) payload.get("payment_review");
if (idObj != null && !String.valueOf(idObj).trim().isEmpty()) { if (idObj != null && !String.valueOf(idObj).trim().isEmpty()) {
// Update // Update
@ -219,8 +220,8 @@ public class GccServicePaymentTrackerService {
String insertHistorySql = "INSERT INTO payment_tracking_details_history (" String insertHistorySql = "INSERT INTO payment_tracking_details_history ("
+ "detail_id, received_date, vendor_name, subject, amount, invoice_no, receiptno, " + "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, tat, status, followup, myfollowup, "
+ "is_active, is_delete, order_by, file_path, cdate, updated_at, updated_by"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ "is_active, is_delete, order_by, file_path, payment_review, cdate, updated_at, updated_by"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(insertHistorySql, jdbcTemplate.update(insertHistorySql,
old.get("id"), old.get("id"),
old.get("received_date"), old.get("received_date"),
@ -241,6 +242,7 @@ public class GccServicePaymentTrackerService {
old.get("is_delete"), old.get("is_delete"),
old.get("order_by"), old.get("order_by"),
old.get("file_path"), old.get("file_path"),
old.get("payment_review"),
old.get("cdate"), old.get("cdate"),
new java.sql.Timestamp(System.currentTimeMillis()), new java.sql.Timestamp(System.currentTimeMillis()),
userId); userId);
@ -253,13 +255,13 @@ public class GccServicePaymentTrackerService {
+ "received_date = ?, vendor_name = ?, subject = ?, amount = ?, invoice_no = ?, " + "received_date = ?, vendor_name = ?, subject = ?, amount = ?, invoice_no = ?, "
+ "receiptno = ?, invoice_date = ?, type = ?, physical_file_no = ?, remarks = ?, " + "receiptno = ?, invoice_date = ?, type = ?, physical_file_no = ?, remarks = ?, "
+ "tat = ?, status = ?, followup = ?, myfollowup = ?, order_by = ?, file_path = ?, " + "tat = ?, status = ?, followup = ?, myfollowup = ?, order_by = ?, file_path = ?, "
+ "updated_by = ?, updated_date = ? "
+ "payment_review = ?, updated_by = ?, updated_date = ? "
+ "WHERE id = ? AND is_active = 1 AND is_delete = 0"; + "WHERE id = ? AND is_active = 1 AND is_delete = 0";
try { try {
int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo, int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo,
receiptNo, invoiceDate, type, physicalFileNo, remarks, receiptNo, invoiceDate, type, physicalFileNo, remarks,
tat, status, followup, myfollowup, orderBy, filePath, tat, status, followup, myfollowup, orderBy, filePath,
userId, new java.sql.Timestamp(System.currentTimeMillis()), id);
paymentReview, userId, new java.sql.Timestamp(System.currentTimeMillis()), id);
return rows > 0; return rows > 0;
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error updating payment tracking detail ID " + id + ": " + e.getMessage()); System.err.println("Error updating payment tracking detail ID " + id + ": " + e.getMessage());
@ -271,14 +273,14 @@ public class GccServicePaymentTrackerService {
+ "received_date, vendor_name, subject, amount, invoice_no, receiptno, " + "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, tat, status, "
+ "followup, myfollowup, is_active, is_delete, order_by, file_path, " + "followup, myfollowup, is_active, is_delete, order_by, file_path, "
+ "updated_by, updated_date, cdate"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?, ?, ?, ?)";
+ "payment_review, updated_by, updated_date, cdate"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?, ?, ?, ?, ?)";
try { try {
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis()); java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo, int rows = jdbcTemplate.update(sql, receivedDate, vendorName, subject, amount, invoiceNo,
receiptNo, invoiceDate, type, physicalFileNo, remarks, receiptNo, invoiceDate, type, physicalFileNo, remarks,
tat, status, followup, myfollowup, orderBy, filePath, tat, status, followup, myfollowup, orderBy, filePath,
userId, now, now);
paymentReview, userId, now, now);
return rows > 0; return rows > 0;
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error saving payment tracking detail: " + e.getMessage()); System.err.println("Error saving payment tracking detail: " + e.getMessage());
@ -306,8 +308,8 @@ public class GccServicePaymentTrackerService {
String histSql = "INSERT INTO payment_tracking_details_history (" String histSql = "INSERT INTO payment_tracking_details_history ("
+ "detail_id, received_date, vendor_name, subject, amount, invoice_no, receiptno, " + "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, tat, status, followup, myfollowup, "
+ "is_active, is_delete, order_by, file_path, cdate, updated_at, updated_by"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ "is_active, is_delete, order_by, file_path, payment_review, cdate, updated_at, updated_by"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(histSql, jdbcTemplate.update(histSql,
id, id,
old.get("received_date"), old.get("received_date"),
@ -328,6 +330,7 @@ public class GccServicePaymentTrackerService {
1, // is_delete = 1 1, // is_delete = 1
old.get("order_by"), old.get("order_by"),
old.get("file_path"), old.get("file_path"),
old.get("payment_review"),
old.get("cdate"), old.get("cdate"),
new java.sql.Timestamp(System.currentTimeMillis()), new java.sql.Timestamp(System.currentTimeMillis()),
updatedBy); updatedBy);
@ -488,8 +491,8 @@ public class GccServicePaymentTrackerService {
String histSql = "INSERT INTO payment_tracking_details_history (" String histSql = "INSERT INTO payment_tracking_details_history ("
+ "detail_id, received_date, vendor_name, subject, amount, invoice_no, receiptno, " + "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, tat, status, followup, myfollowup, "
+ "is_active, is_delete, order_by, file_path, cdate, updated_at, updated_by"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ "is_active, is_delete, order_by, file_path, payment_review, cdate, updated_at, updated_by"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(histSql, jdbcTemplate.update(histSql,
id, id,
old.get("received_date"), old.get("received_date"),
@ -510,6 +513,7 @@ public class GccServicePaymentTrackerService {
0, // is_delete = 0 0, // is_delete = 0
old.get("order_by"), old.get("order_by"),
old.get("file_path"), old.get("file_path"),
old.get("payment_review"),
old.get("cdate"), old.get("cdate"),
new java.sql.Timestamp(System.currentTimeMillis()), new java.sql.Timestamp(System.currentTimeMillis()),
updatedBy); updatedBy);

102
src/main/java/com/gcc_manpower/service/AppUserService.java

@ -135,7 +135,9 @@ public class AppUserService {
public List<Map<String, Object>> getVendorDetailsWithCounts() { public List<Map<String, Object>> getVendorDetailsWithCounts() {
String sql = "SELECT id, vendor_name, description, total_employees, orderby, cdate, isactive, isdelete, " + String sql = "SELECT id, vendor_name, description, total_employees, orderby, cdate, isactive, isdelete, " +
"(SELECT COUNT(*) FROM employee_details e WHERE (e.vendor_id = CAST(v.id AS CHAR) OR e.vendor_id = v.vendor_name) AND e.isactive = 1 AND e.isdelete = 0) AS actual_employee_count "
"(SELECT COUNT(*) FROM employee_details e LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) WHERE (e.vendor_id = CAST(v.id AS CHAR) OR e.vendor_id = v.vendor_name) AND (e.user_type != '3' AND (ut.usertype IS NULL OR ut.usertype != 'Standby Employees')) AND e.isactive = 1 AND e.isdelete = 0) AS actual_employee_count, "
+
"(SELECT COUNT(*) FROM employee_details e LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) WHERE (e.vendor_id = CAST(v.id AS CHAR) OR e.vendor_id = v.vendor_name) AND (e.user_type = '3' OR ut.usertype = 'Standby Employees') AND e.isactive = 1 AND e.isdelete = 0) AS standby_employee_count "
+ +
"FROM vendor_details v " + "FROM vendor_details v " +
"WHERE v.isactive = 1 AND v.isdelete = 0 " + "WHERE v.isactive = 1 AND v.isdelete = 0 " +
@ -168,7 +170,7 @@ public class AppUserService {
} }
public int getOverallActiveEmployeeCount() { public int getOverallActiveEmployeeCount() {
String sql = "SELECT COUNT(*) FROM employee_details WHERE isactive = 1 AND isdelete = 0";
String sql = "SELECT COUNT(*) FROM employee_details e LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) WHERE (e.user_type != '3' AND (ut.usertype IS NULL OR ut.usertype != 'Standby Employees')) AND e.isactive = 1 AND e.isdelete = 0";
try { try {
Integer count = jdbcManpowerTemplate.queryForObject(sql, Integer.class); Integer count = jdbcManpowerTemplate.queryForObject(sql, Integer.class);
return count != null ? count : 0; return count != null ? count : 0;
@ -242,6 +244,7 @@ public class AppUserService {
" COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " +
" COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " +
" COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " +
" e.user_type AS user_type_id, " +
" COALESCE(des.designation, e.designation) AS designation, " + " COALESCE(des.designation, e.designation) AS designation, " +
" COALESCE(loc.location_name, e.location) AS location, " + " COALESCE(loc.location_name, e.location) AS location, " +
" COALESCE(des.File_no, '-') AS work_order, " + " COALESCE(des.File_no, '-') AS work_order, " +
@ -256,7 +259,8 @@ public class AppUserService {
+ +
"LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) " "LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) "
+ +
"WHERE (e.vendor_id = ? OR e.vendor_id = ?) AND e.isactive = 1 AND e.isdelete = 0 " +
"WHERE ((e.vendor_id = ? OR e.vendor_id = ?) OR (e.user_type = '3' OR ut.usertype = 'Standby Employees')) AND e.isactive = 1 AND e.isdelete = 0 "
+
"ORDER BY e.name"; "ORDER BY e.name";
try { try {
return jdbcManpowerTemplate.queryForList(sql, vendorId != null ? vendorId : "", vendorName); return jdbcManpowerTemplate.queryForList(sql, vendorId != null ? vendorId : "", vendorName);
@ -282,6 +286,7 @@ public class AppUserService {
" COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " +
" COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " +
" COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " +
" e.user_type AS user_type_id, " +
" COALESCE(des.designation, e.designation) AS designation, " + " COALESCE(des.designation, e.designation) AS designation, " +
" COALESCE(loc.location_name, e.location) AS location, " + " COALESCE(loc.location_name, e.location) AS location, " +
" COALESCE(des.File_no, '-') AS work_order, " + " COALESCE(des.File_no, '-') AS work_order, " +
@ -332,14 +337,16 @@ public class AppUserService {
System.err.println("Error checking duplicate employee code: " + e.getMessage()); System.err.println("Error checking duplicate employee code: " + e.getMessage());
} }
// Check designation limit
// Check designation limit (Skip limit validation for standby employees)
if (!isStandbyUserType(emp.get("user_type"))) {
String designationIdOrName = String.valueOf(emp.get("designation")); String designationIdOrName = String.valueOf(emp.get("designation"));
String vendorId = String.valueOf(emp.get("vendor_id")); String vendorId = String.valueOf(emp.get("vendor_id"));
String resolveSql = "SELECT id, designation, tender_manpower_designation_wise FROM employee_workorder_details " String resolveSql = "SELECT id, designation, tender_manpower_designation_wise FROM employee_workorder_details "
+ +
"WHERE vid = ? AND (CAST(id AS CHAR) = ? OR designation = ?) AND isactive = 1 AND isdelete = 0"; "WHERE vid = ? AND (CAST(id AS CHAR) = ? OR designation = ?) AND isactive = 1 AND isdelete = 0";
List<Map<String, Object>> rows = jdbcManpowerTemplate.queryForList(resolveSql, vendorId, designationIdOrName,
List<Map<String, Object>> rows = jdbcManpowerTemplate.queryForList(resolveSql, vendorId,
designationIdOrName,
designationIdOrName); designationIdOrName);
if (rows != null && !rows.isEmpty()) { if (rows != null && !rows.isEmpty()) {
for (Map<String, Object> row : rows) { for (Map<String, Object> row : rows) {
@ -347,14 +354,20 @@ public class AppUserService {
String desName = (String) row.get("designation"); String desName = (String) row.get("designation");
String tenderStr = (String) row.get("tender_manpower_designation_wise"); String tenderStr = (String) row.get("tender_manpower_designation_wise");
// Fetch the count of active, non-deleted employees for this designation/vendor
// Fetch the count of active, regular (non-standby) employees for this
// designation/vendor
String countSql = "SELECT COUNT(*) FROM employee_details e " + String countSql = "SELECT COUNT(*) FROM employee_details e " +
"LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) "
+
"WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) " "WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) "
+ +
" AND (e.designation = ? OR e.designation = ?) " + " AND (e.designation = ? OR e.designation = ?) " +
" AND (e.user_type != '3' AND (ut.usertype IS NULL OR ut.usertype != 'Standby Employees')) "
+
" AND e.isactive = 1 AND e.isdelete = 0"; " AND e.isactive = 1 AND e.isdelete = 0";
Integer activeCount = jdbcManpowerTemplate.queryForObject(countSql, Integer.class, vendorId, vendorId,
Integer activeCount = jdbcManpowerTemplate.queryForObject(countSql, Integer.class, vendorId,
vendorId,
desId, desName); desId, desName);
int currentActive = activeCount != null ? activeCount : 0; int currentActive = activeCount != null ? activeCount : 0;
@ -372,6 +385,7 @@ public class AppUserService {
} }
} }
} }
}
String sql = "INSERT INTO employee_details (emp_code, name, email, mobile, user_type, designation, location, vendor_id, joined_date, cdate, updated_at, updated_by, isactive, isdelete) " String sql = "INSERT INTO employee_details (emp_code, name, email, mobile, user_type, designation, location, vendor_id, joined_date, cdate, updated_at, updated_by, isactive, isdelete) "
+ +
@ -424,7 +438,7 @@ public class AppUserService {
String newDes = String.valueOf(emp.get("designation")); String newDes = String.valueOf(emp.get("designation"));
String newVendor = String.valueOf(emp.get("vendor_id")); String newVendor = String.valueOf(emp.get("vendor_id"));
if (!oldDes.equals(newDes) || !oldVendor.equals(newVendor)) {
if (!isStandbyUserType(emp.get("user_type")) && (!oldDes.equals(newDes) || !oldVendor.equals(newVendor))) {
// Check designation limit for new designation & vendor // Check designation limit for new designation & vendor
String resolveSql = "SELECT id, designation, tender_manpower_designation_wise FROM employee_workorder_details " String resolveSql = "SELECT id, designation, tender_manpower_designation_wise FROM employee_workorder_details "
+ +
@ -436,10 +450,15 @@ public class AppUserService {
String desName = (String) row.get("designation"); String desName = (String) row.get("designation");
String tenderStr = (String) row.get("tender_manpower_designation_wise"); String tenderStr = (String) row.get("tender_manpower_designation_wise");
// Fetch regular active count only
String countSql = "SELECT COUNT(*) FROM employee_details e " + String countSql = "SELECT COUNT(*) FROM employee_details e " +
"LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) "
+
"WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) " "WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) "
+ +
" AND (e.designation = ? OR e.designation = ?) " + " AND (e.designation = ? OR e.designation = ?) " +
" AND (e.user_type != '3' AND (ut.usertype IS NULL OR ut.usertype != 'Standby Employees')) "
+
" AND e.isactive = 1 AND e.isdelete = 0"; " AND e.isactive = 1 AND e.isdelete = 0";
Integer activeCount = jdbcManpowerTemplate.queryForObject(countSql, Integer.class, newVendor, Integer activeCount = jdbcManpowerTemplate.queryForObject(countSql, Integer.class, newVendor,
@ -528,8 +547,10 @@ public class AppUserService {
"FROM employee_details_update_history h " + "FROM employee_details_update_history h " +
"LEFT JOIN vendor_details v ON (h.vendor_id = CAST(v.id AS CHAR) OR h.vendor_id = v.vendor_name) " + "LEFT JOIN vendor_details v ON (h.vendor_id = CAST(v.id AS CHAR) OR h.vendor_id = v.vendor_name) " +
"LEFT JOIN user_type_details ut ON (h.user_type = CAST(ut.id AS CHAR) OR h.user_type = ut.usertype) " + "LEFT JOIN user_type_details ut ON (h.user_type = CAST(ut.id AS CHAR) OR h.user_type = ut.usertype) " +
"LEFT JOIN employee_workorder_details des ON (h.designation = CAST(des.id AS CHAR) OR h.designation = des.designation) " +
"LEFT JOIN employee_location_details loc ON (h.location = CAST(loc.id AS CHAR) OR h.location = loc.location_name) " +
"LEFT JOIN employee_workorder_details des ON (h.designation = CAST(des.id AS CHAR) OR h.designation = des.designation) "
+
"LEFT JOIN employee_location_details loc ON (h.location = CAST(loc.id AS CHAR) OR h.location = loc.location_name) "
+
"LEFT JOIN app_user u ON (h.updated_by = CAST(u.usr_id AS CHAR) OR h.updated_by = u.usr_name) " + "LEFT JOIN app_user u ON (h.updated_by = CAST(u.usr_id AS CHAR) OR h.updated_by = u.usr_name) " +
"WHERE h.emp_code = ? " + "WHERE h.emp_code = ? " +
"ORDER BY h.updated_date DESC"; "ORDER BY h.updated_date DESC";
@ -685,16 +706,37 @@ public class AppUserService {
String desName = (String) row.get("designation"); String desName = (String) row.get("designation");
String tenderStr = (String) row.get("tender_manpower_designation_wise"); String tenderStr = (String) row.get("tender_manpower_designation_wise");
// Count active employees for this vendor and designation
String countSql = "SELECT COUNT(*) FROM employee_details e " +
// Count active regular (tender) employees (excludes standby employees)
String countRegularSql = "SELECT COUNT(*) FROM employee_details e " +
"LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) "
+
"WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) " "WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) "
+ +
" AND (e.designation = ? OR e.designation = ?) " + " AND (e.designation = ? OR e.designation = ?) " +
" AND (e.user_type != '3' AND (ut.usertype IS NULL OR ut.usertype != 'Standby Employees')) " +
" AND e.isactive = 1 AND e.isdelete = 0"; " AND e.isactive = 1 AND e.isdelete = 0";
Integer activeCount = jdbcManpowerTemplate.queryForObject(countSql, Integer.class, vendorId, vendorId,
Integer regularActiveCount = jdbcManpowerTemplate.queryForObject(countRegularSql, Integer.class,
vendorId, vendorId,
desId, desName); desId, desName);
int available = activeCount != null ? activeCount : 0;
int regularCount = regularActiveCount != null ? regularActiveCount : 0;
// Count active standby employees
String countStandbySql = "SELECT COUNT(*) FROM employee_details e " +
"LEFT JOIN user_type_details ut ON (e.user_type = CAST(ut.id AS CHAR) OR e.user_type = ut.usertype) "
+
"WHERE (e.vendor_id = ? OR e.vendor_id = (SELECT vendor_name FROM vendor_details WHERE id = ?)) "
+
" AND (e.designation = ? OR e.designation = ?) " +
" AND (e.user_type = '3' OR ut.usertype = 'Standby Employees') " +
" AND e.isactive = 1 AND e.isdelete = 0";
Integer standbyActiveCount = jdbcManpowerTemplate.queryForObject(countStandbySql, Integer.class,
vendorId, vendorId,
desId, desName);
int standbyCount = standbyActiveCount != null ? standbyActiveCount : 0;
int available = regularCount;
int tender = 0; int tender = 0;
if (tenderStr != null) { if (tenderStr != null) {
@ -703,10 +745,11 @@ public class AppUserService {
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
} }
} }
int vacant = tender - available;
int vacant = tender - regularCount;
String updateSql = "UPDATE employee_workorder_details SET available_manpower = ?, vacant_count = ? WHERE id = ?";
jdbcManpowerTemplate.update(updateSql, String.valueOf(available), String.valueOf(vacant), desId);
String updateSql = "UPDATE employee_workorder_details SET available_manpower = ?, vacant_count = ?, standby_employees_count = ? WHERE id = ?";
jdbcManpowerTemplate.update(updateSql, String.valueOf(available), String.valueOf(vacant),
String.valueOf(standbyCount), desId);
} }
} catch (Exception e) { } catch (Exception e) {
System.err.println("Error recalculating designation manpower for designation: " + designationIdOrName System.err.println("Error recalculating designation manpower for designation: " + designationIdOrName
@ -793,6 +836,7 @@ public class AppUserService {
" COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " +
" COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " +
" COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " +
" e.user_type AS user_type_id, " +
" COALESCE(des.designation, e.designation) AS designation, " + " COALESCE(des.designation, e.designation) AS designation, " +
" COALESCE(loc.location_name, e.location) AS location, " + " COALESCE(loc.location_name, e.location) AS location, " +
" COALESCE(des.File_no, '-') AS work_order " + " COALESCE(des.File_no, '-') AS work_order " +
@ -805,7 +849,7 @@ public class AppUserService {
+ +
"LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) " "LEFT JOIN app_user u ON (e.updated_by = CAST(u.usr_id AS CHAR) OR e.updated_by = u.usr_name) "
+ +
"WHERE e.vendor_id = ? OR e.vendor_id = ? " +
"WHERE e.vendor_id = ? OR e.vendor_id = ? OR e.user_type = '3' OR ut.usertype = 'Standby Employees' " +
"ORDER BY e.name"; "ORDER BY e.name";
try { try {
return jdbcManpowerTemplate.queryForList(sql, vendorId, vendorName != null ? vendorName : ""); return jdbcManpowerTemplate.queryForList(sql, vendorId, vendorName != null ? vendorName : "");
@ -855,6 +899,7 @@ public class AppUserService {
" COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " + " COALESCE(u.usr_name, e.updated_by) AS updated_by_name, " +
" COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " + " COALESCE(v.vendor_name, e.vendor_id) AS vendor_name, " +
" COALESCE(ut.usertype, e.user_type) AS user_type, " + " COALESCE(ut.usertype, e.user_type) AS user_type, " +
" e.user_type AS user_type_id, " +
" COALESCE(des.designation, e.designation) AS designation, " + " COALESCE(des.designation, e.designation) AS designation, " +
" COALESCE(loc.location_name, e.location) AS location, " + " COALESCE(loc.location_name, e.location) AS location, " +
" COALESCE(des.File_no, '-') AS work_order " + " COALESCE(des.File_no, '-') AS work_order " +
@ -985,4 +1030,25 @@ public class AppUserService {
return new ArrayList<>(); return new ArrayList<>();
} }
} }
private boolean isStandbyUserType(Object userTypeObj) {
String userTypeStr = userTypeObj != null ? String.valueOf(userTypeObj).trim() : "";
if ("3".equals(userTypeStr) || "Standby Employees".equalsIgnoreCase(userTypeStr)) {
return true;
}
try {
String checkStandbySql = "SELECT usertype FROM user_type_details WHERE id = ? OR usertype = ?";
List<String> types = jdbcManpowerTemplate.queryForList(checkStandbySql, String.class, userTypeStr,
userTypeStr);
if (types != null && !types.isEmpty()) {
for (String t : types) {
if ("Standby Employees".equalsIgnoreCase(t)) {
return true;
}
}
}
} catch (Exception ignored) {
}
return false;
}
} }

28
src/main/resources/static/assets/css/home-styles.css

@ -824,3 +824,31 @@
margin-top: 5px; margin-top: 5px;
} }
} }
/* Standby Employee Count Interactive Badge */
.emp-count-badge-standby {
background: linear-gradient(135deg, #f39c12, #e67e22);
color: #ffffff;
font-weight: 800;
font-size: 1.05rem;
padding: 8px 18px;
border-radius: 30px;
cursor: pointer;
border: none;
box-shadow: 0 4px 10px rgba(230, 126, 34, 0.25);
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.emp-count-badge-standby:hover {
transform: scale(1.08);
box-shadow: 0 6px 15px rgba(230, 126, 34, 0.4);
background: linear-gradient(135deg, #e67e22, #f39c12);
color: #ffffff;
}
.emp-count-badge-standby:active {
transform: scale(0.96);
}

22
src/main/resources/templates/fragments/adjustment.html

@ -0,0 +1,22 @@
<th:block th:fragment="adjustmentheader">
<!-- Awesome Font -->
<link rel="stylesheet" th:href="@{/assets/font-awesome-6.5.2/css/all.min.css}" type="text/css">
<!-- Bootstrap CSS -->
<link rel="stylesheet" th:href="@{/assets/bootstrap-5.3.2/css/bootstrap.min.css}" type="text/css">
<!-- CSS for Header -->
<link rel="stylesheet" th:href="@{/assets/css/header-styles.css}" type="text/css">
<div class="header">
<div class="logo-left">
<img th:src="@{/assets/header-images/gcc1.png}" alt="Greater Chennai Corporation Logo">
<div class="logo-text">
<h2>GREATER</h2>
<h2>CHENNAI</h2>
<h2>CORPORATION</h2>
<p>பெருநகர சென்னை மாநகராட்சி</p>
</div>
</div>
<div class="title">
<h2>GCC Adjustment Management</h2>
</div>
</div>
</th:block>

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

89
src/main/resources/templates/gccadjustmentmanagement/login.html

@ -0,0 +1,89 @@
<!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 Adjustment Management</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.1/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 Adjustment Management</h3>
<br>
<h2>Login</h2>
<!--<form method="post" th:action="@{/liveurls/api/val-app-usr-login}" id="frm-usr-login-det"
name="frm-usr-login-det">-->
<form id="frm-usr-login-det" name="frm-usr-login-det">
<div class="input-field">
<input type="text" name="usrName" id="usrName" placeholder="Enter Username">
<div class="underline"></div>
</div>
<div class="input-field">
<input type="password" name="usrPass" id="usrPass" placeholder="Enter Password">
<div class="underline"></div>
</div>
<input type="submit" value="Login" onclick="handleLoginSubmit(event)">
</form>
</div>
<!-- Footer Fragment -->
<th:block th:replace="~{fragments/footer :: footer}"></th:block>
<!-- Custom JS -->
<!--<script th:src="@{/assets/js/login-script.js}" type="text/javascript"></script>-->
<script th:inline="javascript">
const contextPath = /*[[@{/}]]*/ '';
</script>
<script>
function handleLoginSubmit(event) {
event.preventDefault();
const form = document.getElementById("frm-usr-login-det");
const formData = new FormData(form);
fetch(contextPath + 'gccadjustmentmanagement/api/login', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.result) {
let usrName = data.usrName && data.usrName.length > 0 ? data.usrName : null;
if (usrName) {
sessionStorage.setItem("adj_usrName", usrName);
console.log("Redirecting to:", contextPath + 'gccadjustmentmanagement/');
alert("Login Success");
window.location.href = contextPath + 'gccadjustmentmanagement/';
} else {
alert("No role found for this user!");
}
} else {
alert(data.error || "Login failed");
}
})
.catch(err => {
console.error("Error during login:", err);
});
}
</script>
</body>
</html>

56
src/main/resources/templates/gccservicepaymenttracker/dashboard.html

@ -1355,6 +1355,7 @@
<th>Age of Bill (Days)</th> <th>Age of Bill (Days)</th>
<th>Status</th> <th>Status</th>
<th>Remarks</th> <th>Remarks</th>
<th>Payment Review</th>
<th onclick="sortMainTable('updated_date')" style="cursor: pointer; user-select: none;">Last <th onclick="sortMainTable('updated_date')" style="cursor: pointer; user-select: none;">Last
Updated At <span id="main-sort-updated_date-icon"><i class="fa-solid fa-sort ms-1" Updated At <span id="main-sort-updated_date-icon"><i class="fa-solid fa-sort ms-1"
style="opacity: 0.4;"></i></span></th> style="opacity: 0.4;"></i></span></th>
@ -1555,6 +1556,14 @@
placeholder="Additional remarks..."></textarea> placeholder="Additional remarks..."></textarea>
</div> </div>
<!-- Payment Review -->
<div class="col-12 mt-3">
<label for="form-payment-review" class="form-label fw-semibold text-secondary">Payment
Review</label>
<textarea class="form-control" id="form-payment-review" rows="2"
placeholder="Enter payment review details..."></textarea>
</div>
</div> </div>
<div class="d-flex justify-content-end gap-2 mt-4"> <div class="d-flex justify-content-end gap-2 mt-4">
@ -1595,6 +1604,7 @@
<th>Amount</th> <th>Amount</th>
<th>Status</th> <th>Status</th>
<th>Remarks</th> <th>Remarks</th>
<th>Payment Review</th>
<th>Last Updated At</th> <th>Last Updated At</th>
<th>Last Updated By</th> <th>Last Updated By</th>
<th class="text-center" style="width: 80px;">Document</th> <th class="text-center" style="width: 80px;">Document</th>
@ -1774,6 +1784,7 @@
<th>Age of Bill (Days)</th> <th>Age of Bill (Days)</th>
<th>Status</th> <th>Status</th>
<th>Deletion Remarks</th> <th>Deletion Remarks</th>
<th>Payment Review</th>
<th>Deleted Date</th> <th>Deleted Date</th>
<th>Deleted By</th> <th>Deleted By</th>
<th class="text-center" style="width: 100px;">Actions</th> <th class="text-center" style="width: 100px;">Actions</th>
@ -1803,6 +1814,20 @@
let activeCategory = 'all'; let activeCategory = 'all';
let allRecords = []; let allRecords = [];
function getLocalDateString(dateTimeStr) {
if (!dateTimeStr) return null;
try {
const d = new Date(dateTimeStr);
if (isNaN(d.getTime())) return null;
const dd = String(d.getDate()).padStart(2, '0');
const MM = String(d.getMonth() + 1).padStart(2, '0');
const yyyy = d.getFullYear();
return `${yyyy}-${MM}-${dd}`;
} catch (e) {
return null;
}
}
let currentFilteredRecords = []; let currentFilteredRecords = [];
let recordModalObj = null; let recordModalObj = null;
let historyModalObj = null; let historyModalObj = null;
@ -2510,6 +2535,7 @@
<td>${calculateAgeOfBill(row.received_date)}</td> <td>${calculateAgeOfBill(row.received_date)}</td>
<td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td> <td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td>
<td><small class="text-muted">${text(row.remarks)}</small></td> <td><small class="text-muted">${text(row.remarks)}</small></td>
<td><small class="text-muted">${text(row.payment_review)}</small></td>
<td>${formatDateTime(row.updated_date)}</td> <td>${formatDateTime(row.updated_date)}</td>
<td class="fw-semibold text-secondary">${text(row.updated_by_name || '-')}</td> <td class="fw-semibold text-secondary">${text(row.updated_by_name || '-')}</td>
`; `;
@ -2855,6 +2881,7 @@
const orderbyEl = document.getElementById('form-orderby'); const orderbyEl = document.getElementById('form-orderby');
if (orderbyEl) orderbyEl.value = row.order_by || ''; if (orderbyEl) orderbyEl.value = row.order_by || '';
document.getElementById('form-remarks').value = row.remarks || ''; document.getElementById('form-remarks').value = row.remarks || '';
document.getElementById('form-payment-review').value = row.payment_review || '';
// Set vendor select (using ID or legacy fallback/custom) // Set vendor select (using ID or legacy fallback/custom)
const select = document.getElementById('form-vendor-select'); const select = document.getElementById('form-vendor-select');
@ -2940,6 +2967,7 @@
const orderbyEl = document.getElementById('form-orderby'); const orderbyEl = document.getElementById('form-orderby');
const order_by = orderbyEl ? orderbyEl.value : (originalRecord ? originalRecord.order_by : ''); const order_by = orderbyEl ? orderbyEl.value : (originalRecord ? originalRecord.order_by : '');
const remarks = document.getElementById('form-remarks').value; const remarks = document.getElementById('form-remarks').value;
const payment_review = document.getElementById('form-payment-review').value;
// Resolve vendor // Resolve vendor
const vendorSelect = document.getElementById('form-vendor-select').value; const vendorSelect = document.getElementById('form-vendor-select').value;
@ -2987,7 +3015,7 @@
const payload = { const payload = {
id, received_date, type, vendor_name: vendorName, subject, amount, invoice_no, id, received_date, type, vendor_name: vendorName, subject, amount, invoice_no,
receiptno, invoice_date, physical_file_no, tat, status, followup, receiptno, invoice_date, physical_file_no, tat, status, followup,
myfollowup, order_by, remarks, file_path: filePath
myfollowup, order_by, remarks, payment_review, file_path: filePath
}; };
const csrfHeaders = getCsrfHeaders(); const csrfHeaders = getCsrfHeaders();
@ -3930,7 +3958,7 @@
} }
if (clickType === 'latest_updates') { if (clickType === 'latest_updates') {
if (row.updated_date !== clickValue) return false;
if (getLocalDateString(row.updated_date) !== clickValue) return false;
} }
return true; return true;
@ -4151,6 +4179,7 @@
<td>${calculateAgeOfBill(row.received_date)}</td> <td>${calculateAgeOfBill(row.received_date)}</td>
<td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td> <td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td>
<td><small class="text-muted">${text(row.remarks)}</small></td> <td><small class="text-muted">${text(row.remarks)}</small></td>
<td><small class="text-muted">${text(row.payment_review)}</small></td>
<td>${formatDateTime(row.updated_date)}</td> <td>${formatDateTime(row.updated_date)}</td>
<td class="fw-semibold text-secondary">${text(row.updated_by_name)}</td> <td class="fw-semibold text-secondary">${text(row.updated_by_name)}</td>
`; `;
@ -4328,6 +4357,7 @@
<th>Age of Bill (Days)</th> <th>Age of Bill (Days)</th>
<th>Status</th> <th>Status</th>
<th>Remarks</th> <th>Remarks</th>
<th>Payment Review</th>
<th>Last Updated At</th> <th>Last Updated At</th>
<th>Last Updated By</th> <th>Last Updated By</th>
</tr> </tr>
@ -4413,6 +4443,7 @@
<td>${calculateAgeOfBill(row.received_date)}</td> <td>${calculateAgeOfBill(row.received_date)}</td>
<td>${text(dispStatus)}</td> <td>${text(dispStatus)}</td>
<td>${text(row.remarks)}</td> <td>${text(row.remarks)}</td>
<td>${text(row.payment_review)}</td>
<td>${formatDateTime(row.updated_date)}</td> <td>${formatDateTime(row.updated_date)}</td>
<td>${text(row.updated_by_name || '-')}</td> <td>${text(row.updated_by_name || '-')}</td>
`; `;
@ -4454,10 +4485,12 @@
return; return;
} }
// Count how many files share this exact latest update date/time stamp
const latestDateOnly = getLocalDateString(maxDateStr);
// Count how many files share this exact latest update date stamp
let count = 0; let count = 0;
allRecords.forEach(r => { allRecords.forEach(r => {
if (r.updated_date === maxDateStr) {
if (r.updated_date && getLocalDateString(r.updated_date) === latestDateOnly) {
count++; count++;
} }
}); });
@ -4481,7 +4514,7 @@
if (timeEl) timeEl.textContent = formatDateTimeHelper(maxDateStr); if (timeEl) timeEl.textContent = formatDateTimeHelper(maxDateStr);
badge.style.setProperty('display', 'flex', 'important'); badge.style.setProperty('display', 'flex', 'important');
badge.setAttribute('data-latest-date', maxDateStr);
badge.setAttribute('data-latest-date', latestDateOnly);
} }
function openLatestUpdatesModal() { function openLatestUpdatesModal() {
@ -4490,7 +4523,16 @@
const latestDate = badge.getAttribute('data-latest-date'); const latestDate = badge.getAttribute('data-latest-date');
if (!latestDate) return; if (!latestDate) return;
openModalForCount('latest_updates', latestDate, 'Latest Updates');
let dateDisplay = '-';
try {
const d = new Date(latestDate);
if (!isNaN(d.getTime())) {
const pad = n => String(n).padStart(2, '0');
dateDisplay = `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()}`;
}
} catch (e) {}
openModalForCount('latest_updates', latestDate, `Latest Updates (${dateDisplay})`);
} }
function escapeHtml(str) { function escapeHtml(str) {
@ -4683,6 +4725,7 @@
<td class="fw-semibold text-dark">&#8377; ${text(item.amount)}</td> <td class="fw-semibold text-dark">&#8377; ${text(item.amount)}</td>
<td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td> <td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td>
<td><small class="text-muted">${text(item.remarks)}</small></td> <td><small class="text-muted">${text(item.remarks)}</small></td>
<td><small class="text-muted">${text(item.payment_review)}</small></td>
<td>${formatDateTime(item.updated_at)}</td> <td>${formatDateTime(item.updated_at)}</td>
<td class="fw-semibold text-secondary">${text(item.updated_by_name || '-')}</td> <td class="fw-semibold text-secondary">${text(item.updated_by_name || '-')}</td>
<td class="text-center">${docBtnHtml}</td> <td class="text-center">${docBtnHtml}</td>
@ -4826,6 +4869,7 @@
<td>${calculateAgeOfBill(row.received_date)}</td> <td>${calculateAgeOfBill(row.received_date)}</td>
<td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td> <td><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}">${text(dispStatus)}</span></td>
<td><small class="text-danger fw-semibold">${text(row.remarks)}</small></td> <td><small class="text-danger fw-semibold">${text(row.remarks)}</small></td>
<td><small class="text-muted">${text(row.payment_review)}</small></td>
<td>${formatDateTime(row.updated_date)}</td> <td>${formatDateTime(row.updated_date)}</td>
<td class="fw-semibold text-secondary">${text(row.updated_by_name)}</td> <td class="fw-semibold text-secondary">${text(row.updated_by_name)}</td>
`; `;

35
src/main/resources/templates/gccservicepaymenttracker/details.html

@ -446,6 +446,7 @@
style="max-width: 90px;">STATUS <span id="sort-resolved_status-icon" style="max-width: 90px;">STATUS <span id="sort-resolved_status-icon"
class="sort-icon"><i class="fa-solid fa-sort"></i></span></th> class="sort-icon"><i class="fa-solid fa-sort"></i></span></th>
<th style="min-width: 150px; max-width: 220px;">REMARKS</th> <th style="min-width: 150px; max-width: 220px;">REMARKS</th>
<th style="min-width: 150px; max-width: 220px;">PAYMENT REVIEW</th>
<th onclick="sortTable('updated_date')" class="cursor-pointer" style="max-width: 90px;"> <th onclick="sortTable('updated_date')" class="cursor-pointer" style="max-width: 90px;">
LAST<br>UPDATED AT <span id="sort-updated_date-icon" class="sort-icon"><i LAST<br>UPDATED AT <span id="sort-updated_date-icon" class="sort-icon"><i
class="fa-solid fa-sort"></i></span></th> class="fa-solid fa-sort"></i></span></th>
@ -586,6 +587,12 @@
<textarea class="form-control" id="form-remarks" rows="2" <textarea class="form-control" id="form-remarks" rows="2"
placeholder="Additional remarks..."></textarea> placeholder="Additional remarks..."></textarea>
</div> </div>
<div class="col-12 mt-3">
<label for="form-payment-review" class="form-label fw-semibold text-secondary">Payment
Review</label>
<textarea class="form-control" id="form-payment-review" rows="2"
placeholder="Enter payment review details..."></textarea>
</div>
</div> </div>
<div class="d-flex justify-content-end gap-2 mt-4"> <div class="d-flex justify-content-end gap-2 mt-4">
@ -626,6 +633,7 @@
<th style="white-space: nowrap;">AMOUNT</th> <th style="white-space: nowrap;">AMOUNT</th>
<th style="white-space: nowrap;">STATUS</th> <th style="white-space: nowrap;">STATUS</th>
<th style="min-width: 140px; max-width: 220px;">REMARKS</th> <th style="min-width: 140px; max-width: 220px;">REMARKS</th>
<th style="min-width: 140px; max-width: 220px;">PAYMENT REVIEW</th>
<th style="white-space: nowrap;">LAST<br>UPDATED AT</th> <th style="white-space: nowrap;">LAST<br>UPDATED AT</th>
<th style="white-space: nowrap;">LAST UPDATED<br>BY</th> <th style="white-space: nowrap;">LAST UPDATED<br>BY</th>
<th class="text-center" style="width: 70px;">ACTIONS</th> <th class="text-center" style="width: 70px;">ACTIONS</th>
@ -707,6 +715,7 @@
<th class="text-center">AGE OF<br>BILL<br>(DAYS)</th> <th class="text-center">AGE OF<br>BILL<br>(DAYS)</th>
<th class="text-center">STATUS</th> <th class="text-center">STATUS</th>
<th style="min-width: 140px; max-width: 220px;">DELETION<br>REMARKS</th> <th style="min-width: 140px; max-width: 220px;">DELETION<br>REMARKS</th>
<th style="min-width: 140px; max-width: 220px;">PAYMENT REVIEW</th>
<th>DELETED<br>DATE</th> <th>DELETED<br>DATE</th>
<th>DELETED<br>BY</th> <th>DELETED<br>BY</th>
<th class="text-center" style="width: 70px;">ACTIONS</th> <th class="text-center" style="width: 70px;">ACTIONS</th>
@ -735,6 +744,20 @@
// Data Caches // Data Caches
let allRecords = []; let allRecords = [];
function getLocalDateString(dateTimeStr) {
if (!dateTimeStr) return null;
try {
const d = new Date(dateTimeStr);
if (isNaN(d.getTime())) return null;
const dd = String(d.getDate()).padStart(2, '0');
const MM = String(d.getMonth() + 1).padStart(2, '0');
const yyyy = d.getFullYear();
return `${yyyy}-${MM}-${dd}`;
} catch (e) {
return null;
}
}
let clickFilteredRecords = []; let clickFilteredRecords = [];
let filteredRecords = []; let filteredRecords = [];
let activeTypes = []; let activeTypes = [];
@ -1115,7 +1138,7 @@
} }
if (urlClickType === 'latest_updates') { if (urlClickType === 'latest_updates') {
if (cleanStr(row.updated_date) !== cleanStr(parsedClickValue)) return false;
if (getLocalDateString(row.updated_date) !== parsedClickValue) return false;
} }
return true; return true;
@ -1497,6 +1520,7 @@
<td class="text-center fw-bold ${ageDays > 60 ? 'text-danger' : 'text-dark'}" style="max-width: 50px; white-space: nowrap;">${ageDays}</td> <td class="text-center fw-bold ${ageDays > 60 ? 'text-danger' : 'text-dark'}" style="max-width: 50px; white-space: nowrap;">${ageDays}</td>
<td class="text-center" style="max-width: 95px;">${statusBadge}</td> <td class="text-center" style="max-width: 95px;">${statusBadge}</td>
<td style="min-width: 150px; max-width: 220px; word-break: break-word; line-height: 1.35;" title="${escapeHtml(text(row.remarks))}"><small class="text-muted">${escapeHtml(text(row.remarks))}</small></td> <td style="min-width: 150px; max-width: 220px; word-break: break-word; line-height: 1.35;" title="${escapeHtml(text(row.remarks))}"><small class="text-muted">${escapeHtml(text(row.remarks))}</small></td>
<td style="min-width: 150px; max-width: 220px; word-break: break-word; line-height: 1.35;" title="${escapeHtml(text(row.payment_review))}"><small class="text-muted">${escapeHtml(text(row.payment_review))}</small></td>
<td style="max-width: 90px; white-space: nowrap;">${updatedSplit}</td> <td style="max-width: 90px; white-space: nowrap;">${updatedSplit}</td>
<td class="fw-semibold text-secondary" style="max-width: 90px; word-break: break-word;">${escapeHtml(text(row.updated_by_name || row.updated_by))}</td> <td class="fw-semibold text-secondary" style="max-width: 90px; word-break: break-word;">${escapeHtml(text(row.updated_by_name || row.updated_by))}</td>
`; `;
@ -1693,6 +1717,8 @@
const remEl = document.getElementById('form-remarks'); const remEl = document.getElementById('form-remarks');
if (remEl) remEl.value = row.remarks || ''; if (remEl) remEl.value = row.remarks || '';
const revEl = document.getElementById('form-payment-review');
if (revEl) revEl.value = row.payment_review || '';
// Set Vendor Select // Set Vendor Select
const select = document.getElementById('form-vendor-select'); const select = document.getElementById('form-vendor-select');
@ -1786,6 +1812,7 @@
const physical_file_no = document.getElementById('form-physical-file-no')?.value; const physical_file_no = document.getElementById('form-physical-file-no')?.value;
const status = document.getElementById('form-status')?.value; const status = document.getElementById('form-status')?.value;
const remarks = document.getElementById('form-remarks')?.value; const remarks = document.getElementById('form-remarks')?.value;
const payment_review = document.getElementById('form-payment-review')?.value;
let originalRecord = id ? allRecords.find(r => String(r.id) === String(id)) : null; let originalRecord = id ? allRecords.find(r => String(r.id) === String(id)) : null;
@ -1826,7 +1853,7 @@
const payload = { const payload = {
id, received_date, type, vendor_name: vendorName, subject, amount, invoice_no, id, received_date, type, vendor_name: vendorName, subject, amount, invoice_no,
receiptno, invoice_date, physical_file_no, status, remarks, file_path: filePath
receiptno, invoice_date, physical_file_no, status, remarks, payment_review, file_path: filePath
}; };
const csrfHeaders = getCsrfHeaders(); const csrfHeaders = getCsrfHeaders();
@ -1908,6 +1935,7 @@
<td class="fw-semibold text-dark" style="white-space: nowrap;">&#8377; ${text(item.amount)}</td> <td class="fw-semibold text-dark" style="white-space: nowrap;">&#8377; ${text(item.amount)}</td>
<td class="text-center"><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}" style="background-color: ${colorInfo.bg}; color: ${colorInfo.text};">${escapeHtml(dispStatus)}</span></td> <td class="text-center"><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}" style="background-color: ${colorInfo.bg}; color: ${colorInfo.text};">${escapeHtml(dispStatus)}</span></td>
<td style="min-width: 140px; max-width: 220px; word-break: break-word; line-height: 1.35;"><small class="text-muted">${text(item.remarks)}</small></td> <td style="min-width: 140px; max-width: 220px; word-break: break-word; line-height: 1.35;"><small class="text-muted">${text(item.remarks)}</small></td>
<td style="min-width: 140px; max-width: 220px; word-break: break-word; line-height: 1.35;"><small class="text-muted">${text(item.payment_review)}</small></td>
<td style="white-space: nowrap;">${formatDateTimeSplit(item.updated_at)}</td> <td style="white-space: nowrap;">${formatDateTimeSplit(item.updated_at)}</td>
<td class="fw-semibold text-secondary" style="max-width: 90px; word-break: break-word;">${text(item.updated_by_name || '-')}</td> <td class="fw-semibold text-secondary" style="max-width: 90px; word-break: break-word;">${text(item.updated_by_name || '-')}</td>
<td class="text-center" style="white-space: nowrap;">${docBtnHtml}</td> <td class="text-center" style="white-space: nowrap;">${docBtnHtml}</td>
@ -2028,6 +2056,7 @@
<td class="text-center" style="white-space: nowrap;">${calculateAgeOfBill(row.received_date)}</td> <td class="text-center" style="white-space: nowrap;">${calculateAgeOfBill(row.received_date)}</td>
<td class="text-center"><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}" style="background-color: ${getStatusStyle(dispStatus).bg}; color: ${getStatusStyle(dispStatus).text};">${escapeHtml(dispStatus)}</span></td> <td class="text-center"><span class="status-badge status-${dispStatus.toLowerCase().replace(/\s+/g, '-')}" style="background-color: ${getStatusStyle(dispStatus).bg}; color: ${getStatusStyle(dispStatus).text};">${escapeHtml(dispStatus)}</span></td>
<td class="text-danger fw-semibold" style="min-width: 140px; max-width: 220px; word-break: break-word; line-height: 1.35;"><small>${text(row.remarks)}</small></td> <td class="text-danger fw-semibold" style="min-width: 140px; max-width: 220px; word-break: break-word; line-height: 1.35;"><small>${text(row.remarks)}</small></td>
<td style="min-width: 140px; max-width: 220px; word-break: break-word; line-height: 1.35;"><small class="text-muted">${text(row.payment_review)}</small></td>
<td style="white-space: nowrap;">${formatDateTimeSplit(row.deleted_at)}</td> <td style="white-space: nowrap;">${formatDateTimeSplit(row.deleted_at)}</td>
<td class="fw-semibold text-secondary" style="max-width: 90px; word-break: break-word;">${text(row.deleted_by)}</td> <td class="fw-semibold text-secondary" style="max-width: 90px; word-break: break-word;">${text(row.deleted_by)}</td>
`; `;
@ -2228,6 +2257,7 @@
"AGE OF BILL (DAYS)": ageDays, "AGE OF BILL (DAYS)": ageDays,
"STATUS": statusName, "STATUS": statusName,
"REMARKS": row.remarks || '-', "REMARKS": row.remarks || '-',
"PAYMENT REVIEW": row.payment_review || '-',
"LAST UPDATED AT": updatedDateTime, "LAST UPDATED AT": updatedDateTime,
"LAST UPDATED BY": row.updated_by_name || row.updated_by || '-' "LAST UPDATED BY": row.updated_by_name || row.updated_by || '-'
}; };
@ -2250,6 +2280,7 @@
{ wch: 18 }, // AGE OF BILL { wch: 18 }, // AGE OF BILL
{ wch: 25 }, // STATUS { wch: 25 }, // STATUS
{ wch: 35 }, // REMARKS { wch: 35 }, // REMARKS
{ wch: 35 }, // PAYMENT REVIEW
{ wch: 20 }, // LAST UPDATED AT { wch: 20 }, // LAST UPDATED AT
{ wch: 18 } // LAST UPDATED BY { wch: 18 } // LAST UPDATED BY
]; ];

82
src/main/resources/templates/home/home.html

@ -323,6 +323,18 @@
<span th:text="${vendor.actual_employee_count}">0</span> <span th:text="${vendor.actual_employee_count}">0</span>
</button> </button>
</div> </div>
<div class="d-flex justify-content-between align-items-center"
th:if="${vendor.standby_employee_count != null and vendor.standby_employee_count > 0}">
<span style="font-weight: 600; color: #7f8c8d; font-size: 0.85rem;">
<i class="fa-solid fa-user-gear text-secondary"></i> Standby Count:
</span>
<button class="emp-count-badge-standby"
style="padding: 6px 14px; font-size: 0.9rem; border-radius: 20px;"
th:attr="onclick=|fetchAndShowEmployees('${vendor.vendor_name}', null, true)|">
<i class="fa-solid fa-user-shield" style="font-size: 0.8rem;"></i>
<span th:text="${vendor.standby_employee_count}">0</span>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -338,8 +350,8 @@
style="border-radius: 14px; border: none; box-shadow: 0 10px 40px rgba(0,0,0,0.2);"> style="border-radius: 14px; border: none; box-shadow: 0 10px 40px rgba(0,0,0,0.2);">
<div class="modal-custom-header"> <div class="modal-custom-header">
<h5 class="modal-custom-title" id="employeeDetailsModalLabel"> <h5 class="modal-custom-title" id="employeeDetailsModalLabel">
<i class="fa-solid fa-users-viewfinder"></i> Employee Details - <span id="modal-vendor-name"
style="text-decoration: underline;">Vendor</span>
<i class="fa-solid fa-users-viewfinder"></i> <span id="modal-title-text">Employee Details</span>
- <span id="modal-vendor-name" style="text-decoration: underline;">Vendor</span>
</h5> </h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Close"></button> aria-label="Close"></button>
@ -381,6 +393,13 @@
</button> </button>
</div> </div>
<select id="modal-employee-category-filter" class="form-select form-select-sm"
style="height: 44px; border-radius: 8px; border: 1px solid #cbd5e1; padding: 0 35px 0 15px; font-weight: 600; min-width: 180px;"
onchange="onCategoryFilterChange()">
<option value="regular">OnRoll Employees</option>
<option value="standby">Standby Employees</option>
</select>
<select id="modal-designation-filter" class="form-select form-select-sm" <select id="modal-designation-filter" class="form-select form-select-sm"
style="height: 44px; border-radius: 8px; border: 1px solid #cbd5e1; padding: 0 35px 0 15px; font-weight: 600; min-width: 200px;" style="height: 44px; border-radius: 8px; border: 1px solid #cbd5e1; padding: 0 35px 0 15px; font-weight: 600; min-width: 200px;"
onchange="filterEmployees()"> onchange="filterEmployees()">
@ -1586,6 +1605,7 @@
const vendorDetailsData = /*[[${vendorDetails}]]*/[]; const vendorDetailsData = /*[[${vendorDetails}]]*/[];
const allDesignations = /*[[${designations}]]*/[]; const allDesignations = /*[[${designations}]]*/[];
var isAdmin = /*[[${isAdmin}]]*/ false; var isAdmin = /*[[${isAdmin}]]*/ false;
let isStandbyViewMode = false;
function showLoadingOverlay(text) { function showLoadingOverlay(text) {
const overlay = document.getElementById("loading-overlay"); const overlay = document.getElementById("loading-overlay");
@ -2554,14 +2574,24 @@
document.body.removeChild(link); document.body.removeChild(link);
} }
// Fetch employee details from API dynamically and render in Modal
function fetchAndShowEmployees(vendorName, designationFilter = null) {
function fetchAndShowEmployees(vendorName, designationFilter = null, isStandby = false) {
isStandbyViewMode = isStandby;
currentVendorName = vendorName; currentVendorName = vendorName;
currentDesignationFilter = designationFilter; currentDesignationFilter = designationFilter;
let displayTitle = vendorName; let displayTitle = vendorName;
document.getElementById("modal-vendor-name").textContent = displayTitle; document.getElementById("modal-vendor-name").textContent = displayTitle;
const titleTextEl = document.getElementById("modal-title-text");
if (titleTextEl) {
titleTextEl.textContent = isStandby ? "Standby Employees Details" : "Employee Details";
}
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
categoryFilter.value = isStandby ? "standby" : "regular";
}
// Populate modal designation dropdown based on selected vendor ID // Populate modal designation dropdown based on selected vendor ID
const modalDesigFilter = document.getElementById("modal-designation-filter"); const modalDesigFilter = document.getElementById("modal-designation-filter");
modalDesigFilter.innerHTML = '<option value="">- All Work Orders -</option>'; modalDesigFilter.innerHTML = '<option value="">- All Work Orders -</option>';
@ -2652,10 +2682,20 @@
// Fetch ALL employee details from API and render in Modal // Fetch ALL employee details from API and render in Modal
function fetchAndShowAllEmployees() { function fetchAndShowAllEmployees() {
isStandbyViewMode = false;
const titleTextEl = document.getElementById("modal-title-text");
if (titleTextEl) {
titleTextEl.textContent = "Employee Details";
}
currentVendorName = "All Active Employees"; currentVendorName = "All Active Employees";
currentDesignationFilter = null; currentDesignationFilter = null;
document.getElementById("modal-vendor-name").textContent = "All Active Employees"; document.getElementById("modal-vendor-name").textContent = "All Active Employees";
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
categoryFilter.value = "regular";
}
const modalDesigFilter = document.getElementById("modal-designation-filter"); const modalDesigFilter = document.getElementById("modal-designation-filter");
modalDesigFilter.innerHTML = '<option value="">- All Work Orders -</option>'; modalDesigFilter.innerHTML = '<option value="">- All Work Orders -</option>';
allDesignations.forEach(des => { allDesignations.forEach(des => {
@ -2778,6 +2818,28 @@
}); });
} }
function isStandbyEmployee(emp) {
const utId = String(emp.user_type_id || '').trim();
const utName = String(emp.user_type || '').trim();
return utId === '3' ||
utId.toLowerCase() === 'standby employees' ||
utId.toLowerCase() === 'standby' ||
utName.toLowerCase() === 'standby employees' ||
utName.toLowerCase() === 'standby';
}
function onCategoryFilterChange() {
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
isStandbyViewMode = (categoryFilter.value === "standby");
}
const titleTextEl = document.getElementById("modal-title-text");
if (titleTextEl) {
titleTextEl.textContent = isStandbyViewMode ? "Standby Employees Details" : "Employee Details";
}
filterEmployees();
}
// Instant fuzzy client-side search inside the modal // Instant fuzzy client-side search inside the modal
function filterEmployees() { function filterEmployees() {
const query = document.getElementById("employee-search-input").value.toLowerCase().trim(); const query = document.getElementById("employee-search-input").value.toLowerCase().trim();
@ -2785,6 +2847,18 @@
let list = currentVendorEmployees; let list = currentVendorEmployees;
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
isStandbyViewMode = (categoryFilter.value === "standby");
}
// Filter by standby view mode
if (isStandbyViewMode) {
list = list.filter(emp => isStandbyEmployee(emp) && (currentVendorName === "All Active Employees" || (emp.vendor_name && emp.vendor_name.toLowerCase() === currentVendorName.toLowerCase())));
} else {
list = list.filter(emp => !isStandbyEmployee(emp));
}
// Apply date range filter if active // Apply date range filter if active
const fromDateVal = document.getElementById("modal-from-date").value; const fromDateVal = document.getElementById("modal-from-date").value;
const toDateVal = document.getElementById("modal-to-date").value; const toDateVal = document.getElementById("modal-to-date").value;

75
src/main/resources/templates/home/view-login-details.html

@ -481,7 +481,9 @@
<p class="vendor-card-desc" <p class="vendor-card-desc"
th:text="${vendor.description != null ? vendor.description : 'No description provided.'}"> th:text="${vendor.description != null ? vendor.description : 'No description provided.'}">
Description</p> Description</p>
<div class="vendor-card-footer">
<div class="vendor-card-footer"
style="display: flex; flex-direction: column; gap: 8px; align-items: stretch;">
<div class="d-flex justify-content-between align-items-center">
<span style="font-weight: 600; color: #7f8c8d; font-size: 0.9rem;"> <span style="font-weight: 600; color: #7f8c8d; font-size: 0.9rem;">
Employees Count: Employees Count:
</span> </span>
@ -491,6 +493,18 @@
<span th:text="${vendor.actual_employee_count}">0</span> <span th:text="${vendor.actual_employee_count}">0</span>
</button> </button>
</div> </div>
<div class="d-flex justify-content-between align-items-center"
th:if="${vendor.standby_employee_count != null and vendor.standby_employee_count > 0}">
<span style="font-weight: 600; color: #7f8c8d; font-size: 0.9rem;">
Standby Count:
</span>
<button class="emp-count-badge-standby"
th:attr="onclick=|fetchAndShowEmployees('${vendor.vendor_name}', null, true)|">
<i class="fa-solid fa-user-clock"></i>
<span th:text="${vendor.standby_employee_count}">0</span>
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -577,8 +591,8 @@
style="border-radius: 14px; border: none; box-shadow: 0 10px 40px rgba(0,0,0,0.2);"> style="border-radius: 14px; border: none; box-shadow: 0 10px 40px rgba(0,0,0,0.2);">
<div class="modal-custom-header"> <div class="modal-custom-header">
<h5 class="modal-custom-title" id="employeeDetailsModalLabel"> <h5 class="modal-custom-title" id="employeeDetailsModalLabel">
<i class="fa-solid fa-users-viewfinder"></i> Employee Details - <span id="modal-vendor-name"
style="text-decoration: underline;">Vendor</span>
<i class="fa-solid fa-users-viewfinder"></i> <span id="modal-title-text">Employee Details</span>
- <span id="modal-vendor-name" style="text-decoration: underline;">Vendor</span>
</h5> </h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Close"></button> aria-label="Close"></button>
@ -600,6 +614,13 @@
<i class="fa-solid fa-file-pdf"></i> Work Order File <i class="fa-solid fa-file-pdf"></i> Work Order File
</a> </a>
<select id="modal-employee-category-filter" class="form-select form-select-sm"
style="height: 44px; border-radius: 8px; border: 1px solid #cbd5e1; padding: 0 35px 0 15px; font-weight: 600; min-width: 180px;"
onchange="onCategoryFilterChange()">
<option value="regular">OnRoll Employees</option>
<option value="standby">Standby Employees</option>
</select>
<select id="modal-designation-filter" class="form-select form-select-sm" <select id="modal-designation-filter" class="form-select form-select-sm"
style="height: 44px; border-radius: 8px; border: 1px solid #cbd5e1; padding: 0 35px 0 15px; font-weight: 600; min-width: 200px;" style="height: 44px; border-radius: 8px; border: 1px solid #cbd5e1; padding: 0 35px 0 15px; font-weight: 600; min-width: 200px;"
onchange="filterEmployees()"> onchange="filterEmployees()">
@ -650,6 +671,7 @@
const contextPath = /*[[@{/}]]*/ ''; const contextPath = /*[[@{/}]]*/ '';
const vendorDetailsData = /*[[${vendorDetails}]]*/[]; const vendorDetailsData = /*[[${vendorDetails}]]*/[];
const allDesignations = /*[[${designations}]]*/[]; const allDesignations = /*[[${designations}]]*/[];
let isStandbyViewMode = false;
// Globals to store current modal state // Globals to store current modal state
let currentVendorName = ""; let currentVendorName = "";
@ -674,12 +696,23 @@
} }
// Fetch employee details from API dynamically and render in Modal // Fetch employee details from API dynamically and render in Modal
function fetchAndShowEmployees(vendorName, designationFilter = null) {
function fetchAndShowEmployees(vendorName, designationFilter = null, isStandby = false) {
isStandbyViewMode = isStandby;
currentVendorName = vendorName; currentVendorName = vendorName;
let displayTitle = vendorName; let displayTitle = vendorName;
document.getElementById("modal-vendor-name").textContent = displayTitle; document.getElementById("modal-vendor-name").textContent = displayTitle;
const titleTextEl = document.getElementById("modal-title-text");
if (titleTextEl) {
titleTextEl.textContent = isStandby ? "Standby Employees Details" : "Employee Details";
}
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
categoryFilter.value = isStandby ? "standby" : "regular";
}
// Populate modal designation dropdown based on selected vendor ID // Populate modal designation dropdown based on selected vendor ID
const modalDesigFilter = document.getElementById("modal-designation-filter"); const modalDesigFilter = document.getElementById("modal-designation-filter");
modalDesigFilter.innerHTML = '<option value="">- All Designations -</option>'; modalDesigFilter.innerHTML = '<option value="">- All Designations -</option>';
@ -777,6 +810,28 @@
}); });
} }
function isStandbyEmployee(emp) {
const utId = String(emp.user_type_id || '').trim();
const utName = String(emp.user_type || '').trim();
return utId === '3' ||
utId.toLowerCase() === 'standby employees' ||
utId.toLowerCase() === 'standby' ||
utName.toLowerCase() === 'standby employees' ||
utName.toLowerCase() === 'standby';
}
function onCategoryFilterChange() {
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
isStandbyViewMode = (categoryFilter.value === "standby");
}
const titleTextEl = document.getElementById("modal-title-text");
if (titleTextEl) {
titleTextEl.textContent = isStandbyViewMode ? "Standby Employees Details" : "Employee Details";
}
filterEmployees();
}
// Instant fuzzy client-side search inside the modal // Instant fuzzy client-side search inside the modal
function filterEmployees() { function filterEmployees() {
const query = document.getElementById("employee-search-input").value.toLowerCase().trim(); const query = document.getElementById("employee-search-input").value.toLowerCase().trim();
@ -784,6 +839,18 @@
let list = currentVendorEmployees; let list = currentVendorEmployees;
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
isStandbyViewMode = (categoryFilter.value === "standby");
}
// Filter by standby view mode
if (isStandbyViewMode) {
list = list.filter(emp => isStandbyEmployee(emp) && (currentVendorName === "All Active Employees" || (emp.vendor_name && emp.vendor_name.toLowerCase() === currentVendorName.toLowerCase())));
} else {
list = list.filter(emp => !isStandbyEmployee(emp));
}
// Apply designation filter // Apply designation filter
if (selectedDesignation !== "") { if (selectedDesignation !== "") {
list = list.filter(emp => emp.designation && emp.designation.trim().toLowerCase() === selectedDesignation); list = list.filter(emp => emp.designation && emp.designation.trim().toLowerCase() === selectedDesignation);

Loading…
Cancel
Save