Browse Source

added new adjustment management api's service and controller , html pages and javascript and some UI changes in payment tracker and manpower

master
yoganandhank 2 months ago
parent
commit
4ec8b33328
  1. 9
      src/main/java/com/gcc_manpower/config/DataSourceConfig.java
  2. 3
      src/main/java/com/gcc_manpower/config/WebConfig.java
  3. 2
      src/main/java/com/gcc_manpower/controller/HomeController.java
  4. 9
      src/main/java/com/gcc_manpower/gccadjustmentmanagement/controller/AdjustmentController.java
  5. 18
      src/main/java/com/gcc_manpower/gccadjustmentmanagement/service/AdjustmentService.java
  6. 27
      src/main/java/com/gcc_manpower/gccservicepaymenttracker/service/GccServicePaymentTrackerService.java
  7. 68
      src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java
  8. 222
      src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java
  9. 11
      src/main/java/com/gcc_manpower/service/AppUserService.java
  10. 9
      src/main/resources/application-local.properties
  11. 4
      src/main/resources/static/assets/css/home-styles.css
  12. 865
      src/main/resources/templates/gccadjustmentmanagement/dashboard.html
  13. 210
      src/main/resources/templates/gccservicepaymenttracker/dashboard.html
  14. 158
      src/main/resources/templates/gccservicepaymenttracker/details.html
  15. 1036
      src/main/resources/templates/gcctonermanagement/upload.html
  16. 95
      src/main/resources/templates/home/home.html

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

@ -5,6 +5,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@Configuration
@EnableTransactionManagement
@ -67,4 +70,10 @@ public class DataSourceConfig {
dataSource.setPassword(dbpassword);
return dataSource;
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("mysqlTonerManagementDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}

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

@ -52,7 +52,8 @@ public class WebConfig implements WebMvcConfigurer {
registry.addResourceHandler("/gcctonermanagementfiles/**")
.addResourceLocations(tonerUploadPath);
// Map URL path /gccadjustmentmanagementfiles/** to the configured adjustment folder
// Map URL path /gccadjustmentmanagementfiles/** to the configured adjustment
// folder
String adjustmentUploadPath = Paths.get(adjustmentUploadPathConfig).toAbsolutePath().toUri()
.toString();
registry.addResourceHandler("/gccadjustmentmanagementfiles/**")

2
src/main/java/com/gcc_manpower/controller/HomeController.java

@ -43,6 +43,7 @@ public class HomeController {
int totalVendors = vendorDetails.size();
int totalTenderCount = appUserService.getOverallTenderCount();
int totalExistingCount = appUserService.getOverallActiveEmployeeCount();
int totalStandbyCount = appUserService.getOverallStandbyEmployeeCount();
int totalVacantCount = 0;
for (Map<String, Object> vendor : vendorDetails) {
@ -67,6 +68,7 @@ public class HomeController {
model.addAttribute("totalTenderCount", totalTenderCount);
model.addAttribute("totalExistingCount", totalExistingCount);
model.addAttribute("totalVacantCount", totalVacantCount);
model.addAttribute("totalStandbyCount", totalStandbyCount);
}
@GetMapping("/")

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

@ -143,6 +143,15 @@ public class AdjustmentController {
return adjustmentService.getAllFiles();
}
@GetMapping("/api/list-all")
@ResponseBody
public List<Map<String, Object>> getFilesListIncludingDeleted(HttpSession session) {
if (session.getAttribute("adj_usrName") == null) {
return java.util.Collections.emptyList();
}
return adjustmentService.getAllFilesIncludingDeleted();
}
@PostMapping("/api/save")
@ResponseBody
public Map<String, Object> saveAdjustment(

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

@ -61,6 +61,24 @@ public class AdjustmentService {
}
}
public List<Map<String, Object>> getAllFilesIncludingDeleted() {
String sql = "SELECT a.*, " +
"adv.adv_type AS resolved_adv_type, " +
"f.fund_type AS resolved_fund_type, " +
"u.usr_name AS updated_by_name " +
"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) " +
"LEFT JOIN gcc_manpower.app_user u ON (a.updated_by = CAST(u.usr_id AS CHAR) OR a.updated_by = u.usr_name) " +
"ORDER BY a.updated_date DESC, a.id DESC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {
System.err.println("Error fetching all 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 {

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

@ -193,6 +193,33 @@ public class GccServicePaymentTrackerService {
Object idObj = payload.get("id");
java.sql.Date receivedDate = parseDate(payload.get("received_date"));
String vendorName = (String) payload.get("vendor_name");
if (vendorName != null && !vendorName.trim().isEmpty() && !vendorName.trim().matches("^[0-9]+$")) {
String trimmedVendorName = vendorName.trim();
String checkSql = "SELECT id FROM vendors_list WHERE TRIM(LOWER(vendor_name)) = ? AND is_active = 1 AND is_delete = 0 LIMIT 1";
List<Integer> existingIds = jdbcTemplate.query(checkSql, (rs, rowNum) -> rs.getInt("id"),
trimmedVendorName.toLowerCase());
if (existingIds != null && !existingIds.isEmpty()) {
vendorName = String.valueOf(existingIds.get(0));
} else {
try {
org.springframework.jdbc.support.GeneratedKeyHolder keyHolder = new org.springframework.jdbc.support.GeneratedKeyHolder();
final String finalVendorName = trimmedVendorName;
jdbcTemplate.update(connection -> {
java.sql.PreparedStatement ps = connection.prepareStatement(
"INSERT INTO vendors_list (vendor_name, is_active, is_delete, order_by, cdate) VALUES (?, 1, 0, '0', NOW())",
java.sql.Statement.RETURN_GENERATED_KEYS);
ps.setString(1, finalVendorName);
return ps;
}, keyHolder);
Number key = keyHolder.getKey();
if (key != null) {
vendorName = String.valueOf(key.intValue());
}
} catch (Exception e) {
System.err.println("Error inserting custom vendor '" + trimmedVendorName + "': " + e.getMessage());
}
}
}
String subject = (String) payload.get("subject");
String amount = (String) payload.get("amount");
String invoiceNo = (String) payload.get("invoice_no");

68
src/main/java/com/gcc_manpower/gcctonermanagement/controller/TonerController.java

@ -154,11 +154,8 @@ public class TonerController {
public Map<String, Object> uploadInward(
@RequestParam("refNo") String refNo,
@RequestParam("companyName") String companyName,
@RequestParam("pid") int pid,
@RequestParam("tid") int tid,
@RequestParam("qty") String qty,
@RequestParam("itemsJson") String itemsJson,
@RequestParam(value = "dcFile", required = false) MultipartFile dcFile,
@RequestParam(value = "requestId", required = false) Integer requestId,
HttpSession session) {
Map<String, Object> response = new HashMap<>();
@ -186,6 +183,17 @@ public class TonerController {
}
try {
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> items = mapper.readValue(itemsJson,
new TypeReference<List<Map<String, Object>>>() {
});
if (items == null || items.isEmpty()) {
response.put("success", false);
response.put("error", "Please enter quantity for at least one model.");
return response;
}
File uploadDir = new File(tonerUploadPathConfig).getAbsoluteFile();
if (!uploadDir.exists()) {
uploadDir.mkdirs();
@ -194,7 +202,7 @@ public class TonerController {
String dcPath = saveUploadedFile(dcFile, "dc", uploadDir);
boolean saved = tonerService.saveInwardRegister(
refNo.trim(), companyName.trim(), pid, tid, qty.trim(), dcPath, updatedBy, requestId);
refNo.trim(), companyName.trim(), items, dcPath, updatedBy);
if (saved) {
response.put("success", true);
@ -217,9 +225,7 @@ public class TonerController {
public Map<String, Object> uploadRequest(
@RequestParam("itcellRefNo") String itcellRefNo,
@RequestParam("deptId") String deptId,
@RequestParam("pid") int pid,
@RequestParam("tid") int tid,
@RequestParam("qtyRequested") String qtyRequested,
@RequestParam("itemsJson") String itemsJson,
@RequestParam(value = "userRequestFile", required = false) MultipartFile userRequestFile,
HttpSession session) {
@ -247,23 +253,18 @@ public class TonerController {
return response;
}
int qtyReq;
try {
qtyReq = Integer.parseInt(qtyRequested.trim());
} catch (NumberFormatException e) {
response.put("success", false);
response.put("error", "Quantity requested must be a valid number.");
return response;
}
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> items = mapper.readValue(itemsJson,
new TypeReference<List<Map<String, Object>>>() {
});
int availStock = tonerService.getTonerAvailableStock(tid);
if (qtyReq > availStock) {
response.put("success", false);
response.put("error", "Requested quantity (" + qtyReq + ") exceeds available stock (" + availStock + ").");
return response;
}
if (items == null || items.isEmpty()) {
response.put("success", false);
response.put("error", "Please add at least one printer/toner model request entry.");
return response;
}
try {
File uploadDir = new File(tonerUploadPathConfig).getAbsoluteFile();
if (!uploadDir.exists()) {
uploadDir.mkdirs();
@ -271,12 +272,11 @@ public class TonerController {
String urPath = saveUploadedFile(userRequestFile, "user_request", uploadDir);
String reqNo = tonerService.saveUserRequest(
itcellRefNo.trim(), deptId.trim(), pid, tid, qtyRequested.trim(), urPath, updatedBy);
boolean saved = tonerService.saveUserRequest(
itcellRefNo.trim(), deptId.trim(), items, urPath, updatedBy);
if (reqNo != null) {
if (saved) {
response.put("success", true);
response.put("reqNo", reqNo);
response.put("message", "User request saved successfully.");
} else {
deleteFileIfExists(urPath, uploadDir);
@ -527,8 +527,7 @@ public class TonerController {
@RequestParam("refNo") String refNo,
@RequestParam("companyName") String companyName,
@RequestParam("pid") String pid,
@RequestParam("tid") String tid,
@RequestParam("qtyRequested") String qtyRequested,
@RequestParam("itemsJson") String itemsJson,
@RequestParam("emailCopyFile") MultipartFile emailCopyFile,
HttpSession session) {
@ -557,6 +556,17 @@ public class TonerController {
}
try {
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> items = mapper.readValue(itemsJson,
new TypeReference<List<Map<String, Object>>>() {
});
if (items == null || items.isEmpty()) {
response.put("success", false);
response.put("error", "Please add at least one toner model quantity entry.");
return response;
}
File uploadDir = new File(tonerUploadPathConfig).getAbsoluteFile();
if (!uploadDir.exists()) {
uploadDir.mkdirs();
@ -565,7 +575,7 @@ public class TonerController {
String ecPath = saveUploadedFile(emailCopyFile, "workorder_request_email", uploadDir);
boolean saved = tonerService.saveWorkorderModelRequest(
refNo.trim(), companyName.trim(), pid.trim(), tid.trim(), qtyRequested.trim(), ecPath, updatedBy);
refNo.trim(), companyName.trim(), pid.trim(), items, ecPath, updatedBy);
if (saved) {
response.put("success", true);

222
src/main/java/com/gcc_manpower/gcctonermanagement/service/TonerService.java

@ -13,6 +13,10 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import java.sql.PreparedStatement;
import java.sql.Statement;
@Service
public class TonerService {
@ -74,22 +78,40 @@ public class TonerService {
}
}
public boolean saveInwardRegister(String refNo, String companyName, int pid, int tid, String qty, String dcUpload,
String username, Integer requestId) {
@Transactional
public boolean saveInwardRegister(String refNo, String companyName, List<Map<String, Object>> items,
String dcUpload, String username) {
String sql = "INSERT INTO stock_register_details " +
"(ref_no, company_name, pid, tid, qty_registered, dc_upload, cdate, updated_date, updated_by, is_active, is_delete, request_id) "
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?)";
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
try {
int rows = jdbcTemplate.update(sql, refNo, companyName, String.valueOf(pid), String.valueOf(tid), qty,
dcUpload,
now, now, username, requestId);
return rows > 0;
for (Map<String, Object> item : items) {
int pid = Integer.parseInt(String.valueOf(item.get("pid")));
int tid = Integer.parseInt(String.valueOf(item.get("tid")));
String qty = String.valueOf(item.get("qty"));
Integer requestId = item.get("requestId") != null
? Integer.parseInt(String.valueOf(item.get("requestId")))
: null;
int qtyInt = 0;
try {
qtyInt = Integer.parseInt(qty.trim());
} catch (Exception ignored) {
}
if (qtyInt <= 0)
continue;
jdbcTemplate.update(sql, refNo, companyName, String.valueOf(pid), String.valueOf(tid),
String.valueOf(qtyInt),
dcUpload, now, now, username, requestId);
}
return true;
} catch (Exception e) {
System.err.println("Error saving inward register: " + e.getMessage());
e.printStackTrace();
return false;
throw new RuntimeException("Rollback saveInwardRegister due to: " + e.getMessage());
}
}
@ -103,28 +125,35 @@ public class TonerService {
}
}
public String saveUserRequest(String itCellRefNo, String deptId, int pid, int tid, String qtyRequested,
@Transactional
public boolean saveUserRequest(String itCellRefNo, String deptId, List<Map<String, Object>> items,
String userRequestUpload, String username) {
String tonerModel = getTonerModelName(tid);
String cleanModel = tonerModel.trim().replaceAll("\\s+", "-").replaceAll("[^a-zA-Z0-9-]", "");
int maxId = getMaxRequestId();
int seqNum = 1000 + maxId + 1;
int year = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR);
String reqNo = String.format("%s-%d-%04d", cleanModel, year, seqNum);
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
String sql = "INSERT INTO department_requested_details " +
"(req_no, itcell_ref_no, dept_id, pid, tid, qty_requested, qty_delivered, user_request_upload, cdate, updated_date, updated_by, is_active, is_delete) "
+
"VALUES (?, ?, ?, ?, ?, ?, '0', ?, ?, ?, ?, 1, 0)";
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
try {
int rows = jdbcTemplate.update(sql, reqNo, itCellRefNo, deptId, String.valueOf(pid), String.valueOf(tid),
qtyRequested, userRequestUpload, now, now, username);
return rows > 0 ? reqNo : null;
for (Map<String, Object> item : items) {
int pid = Integer.parseInt(String.valueOf(item.get("pid")));
int tid = Integer.parseInt(String.valueOf(item.get("tid")));
String qtyRequested = String.valueOf(item.get("qty"));
String tonerModel = getTonerModelName(tid);
String cleanModel = tonerModel.trim().replaceAll("\\s+", "-").replaceAll("[^a-zA-Z0-9-]", "");
int maxId = getMaxRequestId();
int seqNum = 1000 + maxId + 1;
int year = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR);
String reqNo = String.format("%s-%d-%04d", cleanModel, year, seqNum);
jdbcTemplate.update(sql, reqNo, itCellRefNo, deptId, String.valueOf(pid), String.valueOf(tid),
qtyRequested, userRequestUpload, now, now, username);
}
return true;
} catch (Exception e) {
System.err.println("Error saving user request: " + e.getMessage());
System.err.println("Error saving user requests: " + e.getMessage());
e.printStackTrace();
return null;
throw new RuntimeException("Rollback saveUserRequest due to: " + e.getMessage());
}
}
@ -241,7 +270,7 @@ public class TonerService {
+ "MAX(s.cdate) AS cdate, COALESCE(MAX(u.usr_name), MAX(s.updated_by)) AS order_by, "
+ "p.printer_name, t.model_name, "
+ "COALESCE((SELECT w.qty_registered FROM workorder_printermodel_qty w WHERE w.ref_no = s.ref_no AND w.pid = s.pid AND w.tid = s.tid AND w.is_active = 1 AND w.is_delete = 0 LIMIT 1), 0) AS qty_workorder_registered, "
+ "COALESCE((SELECT SUM(CAST(mr.qty_requested AS SIGNED)) FROM workorder_model_request_details mr WHERE mr.ref_no = s.ref_no AND mr.pid = s.pid AND mr.tid = s.tid AND mr.is_active = 1 AND mr.is_delete = 0), 0) AS qty_workorder_requested "
+ "COALESCE((SELECT SUM(CAST(mr.qty_registered AS SIGNED)) FROM workorder_model_multiple_request_qty mr INNER JOIN workorder_model_request_details r ON mr.ref_no = r.ref_no WHERE r.ref_no = s.ref_no AND mr.pid = s.pid AND mr.tid = s.tid AND mr.is_active = 1 AND mr.is_delete = 0 AND r.is_active = 1 AND r.is_delete = 0), 0) AS qty_workorder_requested "
+ "FROM stock_register_details s "
+ "LEFT JOIN printer_model_list p ON CAST(s.pid AS SIGNED) = p.id "
+ "LEFT JOIN toner_model_list t ON CAST(s.tid AS SIGNED) = t.id "
@ -343,31 +372,6 @@ public class TonerService {
}
}
@PostConstruct
public void initDatabase() {
try {
jdbcTemplate.execute("ALTER TABLE stock_register_details ADD COLUMN request_id INT DEFAULT NULL");
} catch (Exception e) {
// Ignore
}
try {
jdbcTemplate.execute("ALTER TABLE workorder_register_details DROP COLUMN order_by");
} catch (Exception e) {
// Ignore
}
try {
jdbcTemplate
.execute("ALTER TABLE workorder_printermodel_qty ADD COLUMN updated_by VARCHAR(255) DEFAULT NULL");
} catch (Exception e) {
// Ignore
}
try {
jdbcTemplate.execute("ALTER TABLE workorder_printermodel_qty DROP COLUMN order_by");
} catch (Exception e) {
// Ignore
}
}
public List<String> getActiveWorkorderRefNos() {
String sql = "SELECT ref_no FROM workorder_register_details WHERE is_active = 1 AND is_delete = 0 ORDER BY id DESC";
try {
@ -378,36 +382,88 @@ public class TonerService {
}
}
public boolean saveWorkorderModelRequest(String refNo, String companyName, String pid, String tid,
String qtyRequested, String emailCopyUpload, String username) {
String sql = "INSERT INTO workorder_model_request_details " +
@Transactional
public boolean saveWorkorderModelRequest(String refNo, String companyName, String pid,
List<Map<String, Object>> items, String emailCopyUpload, String username) {
if (items == null || items.isEmpty()) {
return false;
}
// Calculate sum of qty_requested
int totalQtyRequested = 0;
for (Map<String, Object> item : items) {
totalQtyRequested += Integer.parseInt(String.valueOf(item.get("qty")));
}
final int finalTotalQty = totalQtyRequested;
// Use first item's pid and tid for the summary row to maintain compatibility
String firstPid = (pid != null && !pid.trim().isEmpty()) ? pid : String.valueOf(items.get(0).get("pid"));
String firstTid = String.valueOf(items.get(0).get("tid"));
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
// Insert into workorder_model_request_details using KeyHolder to get the
// generated id
String sqlMain = "INSERT INTO workorder_model_request_details " +
"(ref_no, company_name, pid, tid, qty_requested, email_copy_upload, cdate, updated_date, updated_by, is_active, is_delete) "
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0)";
java.sql.Timestamp now = new java.sql.Timestamp(System.currentTimeMillis());
try {
int rows = jdbcTemplate.update(sql, refNo, companyName, pid, tid, qtyRequested, emailCopyUpload, now, now,
username);
return rows > 0;
KeyHolder keyHolder = new GeneratedKeyHolder();
int mainRows = jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sqlMain, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, refNo);
ps.setString(2, companyName);
ps.setString(3, firstPid);
ps.setString(4, firstTid);
ps.setString(5, String.valueOf(finalTotalQty));
ps.setString(6, emailCopyUpload);
ps.setTimestamp(7, now);
ps.setTimestamp(8, now);
ps.setString(9, username);
return ps;
}, keyHolder);
if (mainRows <= 0) {
return false;
}
Number key = keyHolder.getKey();
int requestId = key != null ? key.intValue() : 0;
// Insert into workorder_model_multiple_request_qty in loop
String sqlSub = "INSERT INTO workorder_model_multiple_request_qty " +
"(ref_no, pid, tid, qty_registered, cdate, updated_date, updated_by, is_active, is_delete) "
+
"VALUES (?, ?, ?, ?, ?, ?, ?, 1, 0)";
for (Map<String, Object> item : items) {
String itemPid = item.get("pid") != null ? String.valueOf(item.get("pid")) : firstPid;
String tid = String.valueOf(item.get("tid"));
String qty = String.valueOf(item.get("qty"));
jdbcTemplate.update(sqlSub, refNo, itemPid, tid, qty, now, now, username);
}
return true;
} catch (Exception e) {
System.err.println("Error saving workorder model request: " + e.getMessage());
System.err.println("Error saving workorder model multiple request: " + e.getMessage());
e.printStackTrace();
return false;
throw new RuntimeException("Rollback saveWorkorderModelRequest due to: " + e.getMessage());
}
}
public List<Map<String, Object>> getWorkorderRegisteredItems(String refNo) {
String sql = "SELECT w.pid, w.tid, w.qty_registered, p.printer_name, t.model_name, t.part_code, COALESCE(vl.vendor_name, r.vendor_name) AS vendor_name, "
+
"COALESCE((SELECT SUM(CAST(mr.qty_requested AS SIGNED)) FROM workorder_model_request_details mr " +
"WHERE mr.ref_no = w.ref_no AND mr.pid = w.pid AND mr.tid = w.tid AND mr.is_active = 1 AND mr.is_delete = 0), 0) AS qty_already_requested "
+
"FROM workorder_printermodel_qty w " +
"LEFT JOIN workorder_register_details r ON w.ref_no = r.ref_no " +
"LEFT JOIN gccpayment_processing_tracker.vendors_list vl ON CAST(r.vendor_name AS SIGNED) = vl.id " +
"LEFT JOIN printer_model_list p ON CAST(w.pid AS SIGNED) = p.id " +
"LEFT JOIN toner_model_list t ON CAST(w.tid AS SIGNED) = t.id " +
"WHERE w.ref_no = ? AND w.is_active = 1 AND w.is_delete = 0";
+ "COALESCE((SELECT SUM(CAST(mr.qty_registered AS SIGNED)) FROM workorder_model_multiple_request_qty mr "
+ "WHERE mr.ref_no = w.ref_no AND mr.pid = w.pid AND mr.tid = w.tid AND mr.is_active = 1 AND mr.is_delete = 0), 0) AS qty_already_requested "
+ "FROM workorder_printermodel_qty w "
+ "LEFT JOIN workorder_register_details r ON w.ref_no = r.ref_no "
+ "LEFT JOIN gccpayment_processing_tracker.vendors_list vl ON CAST(r.vendor_name AS SIGNED) = vl.id "
+ "LEFT JOIN printer_model_list p ON CAST(w.pid AS SIGNED) = p.id "
+ "LEFT JOIN toner_model_list t ON CAST(w.tid AS SIGNED) = t.id "
+ "WHERE w.ref_no = ? AND w.is_active = 1 AND w.is_delete = 0";
try {
return jdbcTemplate.queryForList(sql, refNo);
} catch (Exception e) {
@ -427,16 +483,16 @@ public class TonerService {
}
public List<Map<String, Object>> getWorkorderRequests(String refNo) {
String sql = "SELECT r.id, r.pid, r.tid, r.qty_requested, r.company_name AS vendor_id, r.cdate, "
String sql = "SELECT mr.id, mr.pid, mr.tid, mr.qty_registered AS qty_requested, r.company_name AS vendor_id, r.cdate, "
+ "COALESCE(u.usr_name, r.updated_by) AS updated_by, r.email_copy_upload, p.printer_name, t.model_name, t.part_code, "
+ "COALESCE((SELECT SUM(CAST(sr.qty_registered AS SIGNED)) FROM stock_register_details sr " +
"WHERE ((sr.request_id = r.id) OR (sr.request_id IS NULL AND sr.ref_no = r.ref_no AND sr.pid = r.pid AND sr.tid = r.tid)) "
+ "AND sr.is_active = 1 AND sr.is_delete = 0), 0) AS qty_inward_received "
+ "FROM workorder_model_request_details r "
+ "LEFT JOIN printer_model_list p ON CAST(r.pid AS SIGNED) = p.id "
+ "LEFT JOIN toner_model_list t ON CAST(r.tid AS SIGNED) = t.id "
+ "COALESCE((SELECT SUM(CAST(sr.qty_registered AS SIGNED)) FROM stock_register_details sr "
+ "WHERE sr.ref_no = r.ref_no AND sr.pid = mr.pid AND sr.tid = mr.tid AND sr.is_active = 1 AND sr.is_delete = 0), 0) AS qty_inward_received "
+ "FROM workorder_model_multiple_request_qty mr "
+ "LEFT JOIN workorder_model_request_details r ON mr.ref_no = r.ref_no "
+ "LEFT JOIN printer_model_list p ON CAST(mr.pid AS SIGNED) = p.id "
+ "LEFT JOIN toner_model_list t ON CAST(mr.tid AS SIGNED) = t.id "
+ "LEFT JOIN gcc_manpower.app_user u ON CAST(r.updated_by AS SIGNED) = u.usr_id "
+ "WHERE r.ref_no = ? AND r.is_active = 1 AND r.is_delete = 0";
+ "WHERE r.ref_no = ? AND mr.is_active = 1 AND mr.is_delete = 0 AND r.is_active = 1 AND r.is_delete = 0";
try {
return jdbcTemplate.queryForList(sql, refNo);
} catch (Exception e) {
@ -468,17 +524,17 @@ public class TonerService {
}
public List<Map<String, Object>> getWorkorderModelRequestHistory() {
String sql = "SELECT MAX(mr.id) AS id, mr.ref_no, mr.pid, mr.tid, mr.company_name AS vendor_id, "
+ "SUM(CAST(mr.qty_requested AS SIGNED)) AS qty_requested, MAX(mr.email_copy_upload) AS email_copy_upload, "
+ "MAX(mr.cdate) AS cdate, COALESCE(MAX(u.usr_name), MAX(mr.updated_by)) AS order_by, p.printer_name, t.model_name, "
+ "COALESCE((SELECT w.qty_registered FROM workorder_printermodel_qty w WHERE w.ref_no = mr.ref_no AND w.pid = mr.pid AND w.tid = mr.tid AND w.is_active = 1 AND w.is_delete = 0 LIMIT 1), 0) AS qty_registered "
+ "FROM workorder_model_request_details mr "
String sql = "SELECT mr.id, r.ref_no, mr.pid, mr.tid, r.company_name AS vendor_id, "
+ "CAST(mr.qty_registered AS SIGNED) AS qty_requested, r.email_copy_upload, "
+ "r.cdate, COALESCE(u.usr_name, r.updated_by) AS order_by, p.printer_name, t.model_name, "
+ "COALESCE((SELECT w.qty_registered FROM workorder_printermodel_qty w WHERE w.ref_no = r.ref_no AND w.pid = mr.pid AND w.tid = mr.tid AND w.is_active = 1 AND w.is_delete = 0 LIMIT 1), 0) AS qty_registered "
+ "FROM workorder_model_multiple_request_qty mr "
+ "LEFT JOIN workorder_model_request_details r ON mr.ref_no = r.ref_no "
+ "LEFT JOIN printer_model_list p ON CAST(mr.pid AS SIGNED) = p.id "
+ "LEFT JOIN toner_model_list t ON CAST(mr.tid AS SIGNED) = t.id "
+ "LEFT JOIN gcc_manpower.app_user u ON CAST(mr.updated_by AS SIGNED) = u.usr_id "
+ "WHERE mr.is_active = 1 AND mr.is_delete = 0 "
+ "GROUP BY mr.ref_no, mr.pid, mr.tid, mr.company_name, p.printer_name, t.model_name "
+ "ORDER BY MAX(mr.id) DESC";
+ "LEFT JOIN gcc_manpower.app_user u ON CAST(r.updated_by AS SIGNED) = u.usr_id "
+ "WHERE mr.is_active = 1 AND mr.is_delete = 0 AND r.is_active = 1 AND r.is_delete = 0 "
+ "ORDER BY mr.id DESC";
try {
return jdbcTemplate.queryForList(sql);
} catch (Exception e) {

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

@ -180,6 +180,17 @@ public class AppUserService {
}
}
public int getOverallStandbyEmployeeCount() {
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' OR ut.usertype = 'Standby Employees') AND e.isactive = 1 AND e.isdelete = 0";
try {
Integer count = jdbcManpowerTemplate.queryForObject(sql, Integer.class);
return count != null ? count : 0;
} catch (Exception e) {
System.err.println("Error fetching overall standby employee count: " + e.getMessage());
return 0;
}
}
public List<Map<String, Object>> getActiveLocations() {
String sql = "SELECT id, location_name FROM employee_location_details WHERE isactive = 1 AND isdelete = 0 " +
"ORDER BY " +

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

@ -10,7 +10,7 @@ server.port=8084
# Local DB server
spring.datasource.url=jdbc:mysql://localhost:3306/gcc_manpower
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
@ -31,9 +31,10 @@ logging.level.org.hibernate.orm.jdbc.bind=TRACE
# Spring JDBC / JdbcTemplate Logging
# ===============================
# Log queries executed by JdbcTemplate
logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
# Log values bound as placeholders in JdbcTemplate queries
logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE
# logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
# # Log values bound as placeholders in JdbcTemplate queries
# logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE
logging.level.org.springframework.jdbc.core=ERROR
# Production context path
server.servlet.context-path=/gcc_manpower

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

@ -613,6 +613,10 @@
background: linear-gradient(135deg, #d35400, #e67e22);
}
.bg-summary-standby {
background: linear-gradient(135deg, #8e44ad, #9b59b6);
}
/* Vendor Card Designation List Styles */
.vendor-desig-section {
margin-top: 15px;

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

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

@ -1036,13 +1036,26 @@
<div class="main-container">
<!-- As On Date Display Bar (Above Total No of Bills card) -->
<!-- As On Date & Completed View Mode Display Bar (Above Total No of Bills card) -->
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<div class="as-on-date-badge p-2 px-3 rounded-pill bg-white shadow-sm d-inline-flex align-items-center gap-2"
style="border: 1px solid #e2e8f0; font-size: 0.88rem; color: #475569; font-weight: 600;">
<i class="fa-regular fa-calendar-check text-primary fs-6"></i>
<span>As on Date: <strong id="as-on-date-val" style="color: #09303d;"></strong></span>
</div>
<div class="completed-view-badge p-2 px-3 rounded-pill bg-white shadow-sm d-inline-flex align-items-center gap-2"
style="border: 1px solid #e2e8f0; font-size: 0.88rem; color: #475569; font-weight: 600;">
<i class="fa-solid fa-filter text-success fs-6"></i>
<span>Completed View Mode:</span>
<select id="completed-view-mode" class="form-select form-select-sm border-0 py-0 ps-1 pe-4 fw-bold"
style="color: #09303d; width: auto; font-size: 0.88rem; cursor: pointer; box-shadow: none;"
onchange="onCompletedViewModeChange()">
<option value="current_month">Current Month Completed</option>
<option value="last_month_completed">Last Month Completed List</option>
<option value="all_history">All Completed (History)</option>
</select>
</div>
</div>
<!-- Category Grid (3 Cards) -->
@ -1814,6 +1827,7 @@
let activeCategory = 'all';
let allRecords = [];
let allBackendData = [];
function getLocalDateString(dateTimeStr) {
if (!dateTimeStr) return null;
@ -2152,15 +2166,8 @@
return headers;
}
// Category count loaders
function loadCounts() {
fetch(contextPath + 'gccservicepaymenttracker/api/counts?_=' + new Date().getTime())
.then(response => response.json())
.then(data => {
const totalEl = document.getElementById('count-total');
if (totalEl) totalEl.textContent = data.total || 0;
})
.catch(err => console.error("Error loading category counts:", err));
// No-op: counts are dynamically calculated and set by calculateSummaries()
}
// Sorting States and Logic
@ -2279,7 +2286,8 @@
fetch(contextPath + 'gccservicepaymenttracker/api/list?' + params.toString())
.then(response => response.json())
.then(data => {
allRecords = data;
allBackendData = data;
applyCompletedViewFilter();
currentPage = 1;
clearClickFilter(null);
updateAsOnDateDisplay();
@ -2296,6 +2304,47 @@
});
}
function applyCompletedViewFilter() {
const mode = document.getElementById('completed-view-mode')?.value || 'current_month';
const now = new Date();
const firstDayOfCurrentMonth = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
const firstDayOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1, 0, 0, 0, 0);
allRecords = allBackendData.filter(row => {
const statusName = String(row.resolved_status || getStatusName(row.status) || '').trim();
const statusVal = String(row.status || '').trim();
const matchedStatus = activeStatus.find(s =>
String(s.id) === statusVal ||
s.status_name.toLowerCase().trim() === statusName.toLowerCase().trim()
);
const isCompleted = matchedStatus && getStatusKey(matchedStatus.status_name) === 'completed';
if (isCompleted) {
const compDateStr = row.updated_date || row.received_date || row.cdate;
let compDate = compDateStr ? new Date(compDateStr) : null;
if (compDate && !isNaN(compDate.getTime())) {
if (mode === 'current_month') {
if (compDate < firstDayOfCurrentMonth) {
return false; // Exclude completed bills of previous months
}
} else if (mode === 'last_month_completed') {
if (compDate < firstDayOfLastMonth || compDate >= firstDayOfCurrentMonth) {
return false; // Include ONLY completed bills of the previous month
}
}
// For 'all_history', include all completed bills
}
}
return true;
});
}
function onCompletedViewModeChange() {
applyCompletedViewFilter();
calculateSummaries();
renderTable();
}
function renderTable() {
const tbody = document.getElementById('tracking-tbody');
const noRecords = document.getElementById('no-records-found');
@ -3092,10 +3141,6 @@
function calculateSummaries() {
let rawTotal = allRecords.length;
const now = new Date();
const isFirstDayOfMonth = (now.getDate() === 1);
const firstDayOfCurrentMonth = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
// Dynamic Status Counts Map
const statusCounts = {};
activeStatus.forEach(s => {
@ -3110,8 +3155,6 @@
let age_over_120 = 0;
const vendorMap = {};
let completedPrevMonthsCount = 0;
let completedCurrentMonthCount = 0;
let allCompletedCount = 0;
allRecords.forEach(row => {
@ -3131,20 +3174,8 @@
if (isCompleted) {
allCompletedCount++;
const compDateStr = row.updated_date || row.received_date || row.cdate;
let compDate = compDateStr ? new Date(compDateStr) : null;
if (compDate && !isNaN(compDate.getTime()) && compDate < firstDayOfCurrentMonth) {
completedPrevMonthsCount++;
} else {
completedCurrentMonthCount++;
}
if (!isFirstDayOfMonth && statusId !== null) {
if (statusId !== null) {
statusCounts[statusId] = (statusCounts[statusId] || 0) + 1;
} else if (isFirstDayOfMonth && statusId !== null) {
if (compDate && compDate >= firstDayOfCurrentMonth) {
statusCounts[statusId] = (statusCounts[statusId] || 0) + 1;
}
}
} else {
if (statusId !== null) {
@ -3185,30 +3216,19 @@
}
const vData = vendorMap[vendorName];
if (isCompleted) {
const compDateStr = row.updated_date || row.received_date || row.cdate;
let compDate = compDateStr ? new Date(compDateStr) : null;
const isPrevMonthComp = compDate && !isNaN(compDate.getTime()) && compDate < firstDayOfCurrentMonth;
vData.total++;
if (statusId !== null) {
vData.statusCounts[statusId] = (vData.statusCounts[statusId] || 0) + 1;
}
if (isFirstDayOfMonth && isPrevMonthComp) {
// On 1st of month: exclude previous month completed from current month's vendor total
} else {
vData.total++;
if (statusId !== null) {
vData.statusCounts[statusId] = (vData.statusCounts[statusId] || 0) + 1;
}
if (age >= 1 && age <= 15) vData.comp_age_1_15++;
else if (age >= 16 && age <= 30) vData.comp_age_16_30++;
else if (age >= 31 && age <= 60) vData.comp_age_31_60++;
else if (age >= 61 && age <= 90) vData.comp_age_61_90++;
else if (age >= 91 && age <= 120) vData.comp_age_91_120++;
else if (age > 120) vData.comp_age_over_120++;
}
if (isCompleted) {
if (age >= 1 && age <= 15) vData.comp_age_1_15++;
else if (age >= 16 && age <= 30) vData.comp_age_16_30++;
else if (age >= 31 && age <= 60) vData.comp_age_31_60++;
else if (age >= 61 && age <= 90) vData.comp_age_61_90++;
else if (age >= 91 && age <= 120) vData.comp_age_91_120++;
else if (age > 120) vData.comp_age_over_120++;
} else {
vData.total++;
if (statusId !== null) {
vData.statusCounts[statusId] = (vData.statusCounts[statusId] || 0) + 1;
}
const group = getStatusGroup(statusName);
if (group === 'it_cell') {
vData.itCellCount++;
@ -3225,17 +3245,9 @@
}
});
// Apply 1st day of month logic only on day 1
let total, completedTotal, pendingTotal;
if (isFirstDayOfMonth) {
total = rawTotal - completedPrevMonthsCount;
completedTotal = completedCurrentMonthCount;
pendingTotal = total - completedTotal;
} else {
total = rawTotal;
completedTotal = allCompletedCount;
pendingTotal = total - completedTotal;
}
let total = rawTotal;
let completedTotal = allCompletedCount;
let pendingTotal = total - completedTotal;
// Set top dashboard cards counts dynamically
const cardTotalEl = document.getElementById('count-total');
@ -3607,6 +3619,11 @@
if (vendorName) params.append('vendor', vendorName);
if (col) params.append('col', col);
const cvModeSelect = document.getElementById('completed-view-mode');
if (cvModeSelect) {
params.append('completedViewMode', cvModeSelect.value);
}
window.open(contextPath + 'gccservicepaymenttracker/details?' + params.toString(), '_blank');
}
@ -3650,32 +3667,67 @@
const container = document.getElementById('modal-type-badges');
if (!container) return;
// Calculate count for each category in clickFilteredRecords
const counts = {
all: clickFilteredRecords.length,
'tax invoice': 0,
'sd': 0,
'emd': 0,
'asd': 0,
'agreement': 0
const colorMapping = {
'all': { color: '#0f5c78', bgLight: '#f0f9ff', textLight: '#e0f2fe' },
'tax invoice': { color: '#166534', bgLight: '#f0fdf4', textLight: '#dcfce7' },
'sd': { color: '#1e40af', bgLight: '#eff6ff', textLight: '#dbeafe' },
'emd': { color: '#6b21a8', bgLight: '#faf5ff', textLight: '#f3e8ff' },
'asd': { color: '#9a3412', bgLight: '#fff7ed', textLight: '#ffedd5' },
'agreement': { color: '#9d174d', bgLight: '#fdf2f8', textLight: '#fce7f3' },
'bank guarantee': { color: '#0369a1', bgLight: '#f0f9ff', textLight: '#e0f2fe' },
'delivery chellan': { color: '#4f46e5', bgLight: '#e0e7ff', textLight: '#c7d2fe' }
};
const getStylesForType = (key) => {
if (colorMapping[key]) return colorMapping[key];
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = key.charCodeAt(i) + ((hash << 5) - hash);
}
const h = Math.abs(hash) % 360;
return {
color: `hsl(${h}, 70%, 25%)`,
bgLight: `hsl(${h}, 70%, 97%)`,
textLight: `hsl(${h}, 70%, 90%)`
};
};
const quickTypes = [
{ key: 'all', label: 'All Bills', color: '#0f5c78', bgLight: '#f0f9ff', textLight: '#e0f2fe' }
];
activeTypes.forEach(t => {
const key = getCategoryCode(t.type_name);
if (!quickTypes.some(qt => qt.key === key)) {
const styles = getStylesForType(key);
quickTypes.push({
key: key,
label: t.type_name,
color: styles.color,
bgLight: styles.bgLight,
textLight: styles.textLight
});
}
});
const counts = { all: clickFilteredRecords.length };
quickTypes.forEach(t => {
if (t.key !== 'all') counts[t.key] = 0;
});
clickFilteredRecords.forEach(row => {
const cat = getCategoryCode(row.resolved_type || row.type);
if (cat in counts) {
counts[cat]++;
} else {
const matchedType = activeTypes.find(t => String(t.id) === cat);
if (matchedType) {
const resolvedCat = getCategoryCode(matchedType.type_name);
if (resolvedCat in counts) counts[resolvedCat]++;
}
}
});
const quickTypes = [
{ key: 'all', label: 'All Bills', color: '#0f5c78', textLight: '#e0f2fe', bgLight: '#f0f9ff' },
{ key: 'tax invoice', label: 'Tax Invoice', color: '#166534', textLight: '#dcfce7', bgLight: '#f0fdf4' },
{ key: 'sd', label: 'Security Deposit', color: '#1e40af', textLight: '#dbeafe', bgLight: '#eff6ff' },
{ key: 'emd', label: 'EMD', color: '#6b21a8', textLight: '#f3e8ff', bgLight: '#faf5ff' },
{ key: 'asd', label: 'ASD', color: '#9a3412', textLight: '#ffedd5', bgLight: '#fff7ed' },
{ key: 'agreement', label: 'Agreement', color: '#9d174d', textLight: '#fce7f3', bgLight: '#fdf2f8' }
];
let html = '';
quickTypes.forEach(t => {
const count = counts[t.key] || 0;

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

@ -397,24 +397,33 @@
<!-- Dropdowns Control Card -->
<div class="card-custom mb-4">
<div class="row g-3 align-items-end">
<div class="col-md-4">
<div class="col-md-3">
<label class="form-label fw-bold text-secondary" style="font-size: 0.82rem;">Type of Bill</label>
<select id="details-filter-type" class="form-control-custom" onchange="onTypeDropdownChange()">
<option value="all">- All Types -</option>
</select>
</div>
<div class="col-md-4">
<div class="col-md-3">
<label class="form-label fw-bold text-secondary" style="font-size: 0.82rem;">Status</label>
<select id="details-filter-status" class="form-control-custom" onchange="onStatusDropdownChange()">
<option value="all">- All Statuses -</option>
</select>
</div>
<div class="col-md-4">
<div class="col-md-3">
<label class="form-label fw-bold text-secondary" style="font-size: 0.82rem;">Vendor</label>
<select id="details-filter-vendor" class="form-control-custom" onchange="applyDetailsFilters()">
<option value="all">- All Vendors -</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-bold text-secondary" style="font-size: 0.82rem;">Completed View
Mode</label>
<select id="completed-view-mode" class="form-control-custom" onchange="onCompletedViewModeChange()">
<option value="current_month">Current Month Completed</option>
<option value="last_month_completed">Last Month Completed List</option>
<option value="all_history">All Completed (History)</option>
</select>
</div>
</div>
</div>
@ -744,6 +753,7 @@
// Data Caches
let allRecords = [];
let allBackendData = [];
function getLocalDateString(dateTimeStr) {
if (!dateTimeStr) return null;
@ -834,6 +844,12 @@
}
document.title = `Count Details: ${urlTitle} - GCC Service Payment Tracker`;
}
const cvMode = params.get('completedViewMode');
if (cvMode) {
const el = document.getElementById('completed-view-mode');
if (el) el.value = cvMode;
}
}
async function fetchInitialData() {
@ -846,7 +862,7 @@
fetch(contextPath + 'gccservicepaymenttracker/api/vendors?_=' + ts)
]);
allRecords = await listRes.json() || [];
const rawRecords = await listRes.json() || [];
const allFetchedStatus = await statusRes.json() || [];
activeTypes = await typesRes.json() || [];
activeVendors = await vendorsRes.json() || [];
@ -879,6 +895,9 @@
activeStatus = [...completedStatuses, ...nonCompletedStatuses];
allBackendData = rawRecords;
applyCompletedViewFilter();
populateDropdownOptions();
evaluateClickFilter();
} catch (err) {
@ -890,6 +909,46 @@
}
}
function applyCompletedViewFilter() {
const mode = document.getElementById('completed-view-mode')?.value || 'current_month';
const now = new Date();
const firstDayOfCurrentMonth = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
const firstDayOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1, 0, 0, 0, 0);
allRecords = allBackendData.filter(row => {
const statusName = String(row.resolved_status || getStatusName(row.status) || '').trim();
const statusVal = String(row.status || '').trim();
const matchedStatus = activeStatus.find(s =>
String(s.id) === statusVal ||
s.status_name.toLowerCase().trim() === statusName.toLowerCase().trim()
);
const isCompleted = matchedStatus && getStatusKey(matchedStatus.status_name) === 'completed';
if (isCompleted) {
const compDateStr = row.updated_date || row.received_date || row.cdate;
let compDate = compDateStr ? new Date(compDateStr) : null;
if (compDate && !isNaN(compDate.getTime())) {
if (mode === 'current_month') {
if (compDate < firstDayOfCurrentMonth) {
return false; // Exclude completed bills of previous months
}
} else if (mode === 'last_month_completed') {
if (compDate < firstDayOfLastMonth || compDate >= firstDayOfCurrentMonth) {
return false; // Include ONLY completed bills of the previous month
}
}
// For 'all_history', include all completed bills
}
}
return true;
});
}
function onCompletedViewModeChange() {
applyCompletedViewFilter();
evaluateClickFilter();
}
function populateDropdownOptions() {
const typeSelect = document.getElementById('details-filter-type');
const statusSelect = document.getElementById('details-filter-status');
@ -1178,29 +1237,67 @@
const container = document.getElementById('details-type-badges');
if (!container) return;
const counts = {
all: records.length,
'tax invoice': 0,
'sd': 0,
'emd': 0,
'asd': 0,
'agreement': 0
const colorMapping = {
'all': { color: '#0f5c78', bgLight: '#f0f9ff', textLight: '#e0f2fe' },
'tax invoice': { color: '#166534', bgLight: '#f0fdf4', textLight: '#dcfce7' },
'sd': { color: '#1e40af', bgLight: '#eff6ff', textLight: '#dbeafe' },
'emd': { color: '#6b21a8', bgLight: '#faf5ff', textLight: '#f3e8ff' },
'asd': { color: '#9a3412', bgLight: '#fff7ed', textLight: '#ffedd5' },
'agreement': { color: '#9d174d', bgLight: '#fdf2f8', textLight: '#fce7f3' },
'bank guarantee': { color: '#0369a1', bgLight: '#f0f9ff', textLight: '#e0f2fe' },
'delivery chellan': { color: '#4f46e5', bgLight: '#e0e7ff', textLight: '#c7d2fe' }
};
records.forEach(r => {
const cat = getCategoryCode(r.resolved_type || r.type);
if (cat in counts) counts[cat]++;
});
const getStylesForType = (key) => {
if (colorMapping[key]) return colorMapping[key];
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = key.charCodeAt(i) + ((hash << 5) - hash);
}
const h = Math.abs(hash) % 360;
return {
color: `hsl(${h}, 70%, 25%)`,
bgLight: `hsl(${h}, 70%, 97%)`,
textLight: `hsl(${h}, 70%, 90%)`
};
};
const quickTypes = [
{ key: 'all', label: 'All Bills', color: '#0f5c78', bgLight: '#f0f9ff', textLight: '#e0f2fe' },
{ key: 'tax invoice', label: 'Tax Invoice', color: '#166534', bgLight: '#f0fdf4', textLight: '#dcfce7' },
{ key: 'sd', label: 'Security Deposit', color: '#1e40af', bgLight: '#eff6ff', textLight: '#dbeafe' },
{ key: 'emd', label: 'EMD', color: '#6b21a8', bgLight: '#faf5ff', textLight: '#f3e8ff' },
{ key: 'asd', label: 'ASD', color: '#9a3412', bgLight: '#fff7ed', textLight: '#ffedd5' },
{ key: 'agreement', label: 'Agreement', color: '#9d174d', bgLight: '#fdf2f8', textLight: '#fce7f3' }
{ key: 'all', label: 'All Bills', color: '#0f5c78', bgLight: '#f0f9ff', textLight: '#e0f2fe' }
];
activeTypes.forEach(t => {
const key = getCategoryCode(t.type_name);
if (!quickTypes.some(qt => qt.key === key)) {
const styles = getStylesForType(key);
quickTypes.push({
key: key,
label: t.type_name,
color: styles.color,
bgLight: styles.bgLight,
textLight: styles.textLight
});
}
});
const counts = { all: records.length };
quickTypes.forEach(t => {
if (t.key !== 'all') counts[t.key] = 0;
});
records.forEach(r => {
const cat = getCategoryCode(r.resolved_type || r.type);
if (cat in counts) {
counts[cat]++;
} else {
const matchedType = activeTypes.find(t => String(t.id) === cat);
if (matchedType) {
const resolvedCat = getCategoryCode(matchedType.type_name);
if (resolvedCat in counts) counts[resolvedCat]++;
}
}
});
let html = '';
quickTypes.forEach(t => {
const cnt = counts[t.key] || 0;
@ -1562,6 +1659,7 @@
const cleanType = String(typeVal || '').trim().toLowerCase();
let label = cleanType;
let badgeClass = 'type-sd';
let inlineStyle = '';
if (cleanType === 'asd' || cleanType.includes('asd') || cleanType.includes('additional security')) {
label = 'ASD';
@ -1585,13 +1683,27 @@
label = 'Delivery Chellan';
badgeClass = 'type-delivery-chellan';
} else {
const typeObj = activeTypes.find(t => String(t.id) === cleanType);
if (typeObj) {
return getTypeBadge(typeObj.type_name, filePath);
}
// Inline styles using premium pastel HSL styling
const key = cleanType;
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = key.charCodeAt(i) + ((hash << 5) - hash);
}
const h = Math.abs(hash) % 360;
inlineStyle = `style="background-color: hsl(${h}, 70%, 95%); color: hsl(${h}, 70%, 25%); border: 1px solid hsl(${h}, 70%, 85%); cursor: pointer;"`;
label = typeVal || '-';
badgeClass = 'type-sd';
badgeClass = 'custom-type-badge';
}
const onClickAttr = filePath ? `onclick="openPdfFile('${escapeHtml(filePath)}')"` : `onclick="alert('pdf file not uploaded')"`;
return `<span class="type-badge ${badgeClass} cursor-pointer" ${onClickAttr} title="${filePath ? 'Click to view PDF' : 'PDF not uploaded'}">${escapeHtml(label)}</span>`;
return `<span class="type-badge ${badgeClass} cursor-pointer" ${inlineStyle} ${onClickAttr} title="${filePath ? 'Click to view PDF' : 'PDF not uploaded'}">${escapeHtml(label)}</span>`;
}
// Modal Functions: Add / Edit Record

1036
src/main/resources/templates/gcctonermanagement/upload.html
File diff suppressed because it is too large
View File

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

@ -238,6 +238,19 @@
</div>
</div>
<!-- Total Standby Count -->
<div class="summary-card interactive" id="card-total-standby" onclick="fetchAndShowAllStandbyEmployees()">
<div class="summary-icon-container bg-summary-standby">
<i class="fa-solid fa-user-gear"></i>
</div>
<div class="summary-details">
<h4 class="summary-title">Total Standby Count</h4>
<p class="summary-value" id="stat-total-standby" th:attr="data-init=${totalStandbyCount}"
th:text="${totalStandbyCount != null ? totalStandbyCount : '0'}">0
</p>
</div>
</div>
</div>
@ -2680,6 +2693,88 @@
});
}
// Fetch ALL standby employee details from API and render in Modal
function fetchAndShowAllStandbyEmployees() {
isStandbyViewMode = true;
const titleTextEl = document.getElementById("modal-title-text");
if (titleTextEl) {
titleTextEl.textContent = "Standby Employees Details";
}
currentVendorName = "All Active Employees";
currentDesignationFilter = null;
document.getElementById("modal-vendor-name").textContent = "All Active Employees";
const categoryFilter = document.getElementById("modal-employee-category-filter");
if (categoryFilter) {
categoryFilter.value = "standby";
}
const modalDesigFilter = document.getElementById("modal-designation-filter");
modalDesigFilter.innerHTML = '<option value="">- All Work Orders -</option>';
allDesignations.forEach(des => {
const opt = document.createElement("option");
opt.value = des.designation;
opt.textContent = des.designation;
modalDesigFilter.appendChild(opt);
});
modalDesigFilter.value = "";
const tbody = document.getElementById("modal-employee-tbody");
tbody.innerHTML = `
<tr>
<td colspan="13" class="text-center py-5">
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 text-secondary fw-semibold">Fetching all standby employee records...</p>
</td>
</tr>
`;
document.getElementById("no-employees-found").style.display = "none";
document.getElementById("employee-search-input").value = "";
const mainFrom = document.getElementById("from-date").value;
const mainTo = document.getElementById("to-date").value;
const modalFromEl = document.getElementById("modal-from-date");
const modalToEl = document.getElementById("modal-to-date");
if (modalFromEl && modalFromEl._flatpickr) {
modalFromEl._flatpickr.setDate(mainFrom || "");
} else if (modalFromEl) {
modalFromEl.value = mainFrom;
}
if (modalToEl && modalToEl._flatpickr) {
modalToEl._flatpickr.setDate(mainTo || "");
} else if (modalToEl) {
modalToEl.value = mainTo;
}
currentSortKey = "";
currentSortOrder = "asc";
updateSortIcons();
const modalElement = document.getElementById("employeeDetailsModal");
const modal = bootstrap.Modal.getOrCreateInstance(modalElement);
modal.show();
fetch(contextPath + 'api/all-employees')
.then(res => res.json())
.then(data => {
currentVendorEmployees = data;
filterEmployees();
})
.catch(err => {
console.error("Error fetching all employees:", err);
tbody.innerHTML = `
<tr>
<td colspan="13" class="text-center py-5 text-danger fw-bold">
<i class="fa-solid fa-circle-exclamation fs-1 mb-2"></i>
<p>Failed to connect to server and fetch database records. Please try again.</p>
</td>
</tr>
`;
});
}
// Fetch ALL employee details from API and render in Modal
function fetchAndShowAllEmployees() {
isStandbyViewMode = false;

Loading…
Cancel
Save