From 6da953ad693bed5bfa7786583f718aca3b949ed5 Mon Sep 17 00:00:00 2001 From: Usama Khan Date: Wed, 9 Apr 2025 12:09:01 -0700 Subject: [PATCH] add barchart and add jobcard id in transactionleg table --- .../controller/ReportingController.java | 8 + .../utopiaindustries/dao/ctp/BundleDAO.java | 25 +- .../dao/ctp/FinishedItemDAO.java | 8 + .../dao/ctp/InventoryAccountDAO.java | 6 + .../dao/ctp/InventoryTransactionLegDAO.java | 40 ++- .../ctp/InventoryTransactionLegRowMapper.java | 1 + .../dao/ctp/StitchingOfflineItemDAO.java | 14 +- .../model/ctp/InventoryTransactionLeg.java | 10 +- .../service/InventoryService.java | 1 + .../service/ReportingService.java | 281 ++++++++++++++++-- src/main/resources/static/js/charts.js | 97 +++++- .../templates/reporting/job-card-report.html | 125 ++++---- 12 files changed, 516 insertions(+), 100 deletions(-) diff --git a/src/main/java/com/utopiaindustries/controller/ReportingController.java b/src/main/java/com/utopiaindustries/controller/ReportingController.java index 490db07..9da4f1c 100644 --- a/src/main/java/com/utopiaindustries/controller/ReportingController.java +++ b/src/main/java/com/utopiaindustries/controller/ReportingController.java @@ -46,7 +46,15 @@ public class ReportingController { @GetMapping( "/job-card-report") public String jobCardReport(@RequestParam( value = "job-card-id", required = false ) String jobCardId, Model model ){ + model.addAttribute("totalProduction", reportingService.getTotalProduction(jobCardId)); + model.addAttribute("phasesTimes", reportingService.getEachPhaseTotalTime(jobCardId)); + model.addAttribute("segregateItems", reportingService.getSegregateItems(jobCardId)); + model.addAttribute("pendingStatus", reportingService.countPendingItemsOnDifferentPhases(jobCardId)); + model.addAttribute("completeProduction", reportingService.getCompleteProduction(jobCardId)); model.addAttribute("jobCardProgress", reportingService.getJobCardProgress(jobCardId)); + model.addAttribute("cuttingDetails", reportingService.getCuttingDetails(jobCardId)); + model.addAttribute("stitchingDetails", reportingService.getStitchingDetails(jobCardId)); + return "/reporting/job-card-report"; } diff --git a/src/main/java/com/utopiaindustries/dao/ctp/BundleDAO.java b/src/main/java/com/utopiaindustries/dao/ctp/BundleDAO.java index 90e0121..7ee6db9 100644 --- a/src/main/java/com/utopiaindustries/dao/ctp/BundleDAO.java +++ b/src/main/java/com/utopiaindustries/dao/ctp/BundleDAO.java @@ -26,10 +26,11 @@ public class BundleDAO { private final String SELECT_BY_LIMIT = String.format( "SELECT * FROM %s ORDER BY id DESC LIMIT :limit", TABLE_NAME ); private final String SELECT_BY_MASTER_ID = String.format( "SELECT * FROM %s WHERE master_bundle_id = :master_bundle_id ORDER BY id DESC", TABLE_NAME ); private final String SELECT_BY_IDS = String.format( "SELECT * FROM %s WHERE id IN (:ids)", TABLE_NAME ); - private final String SELECT_BY_ITEM_ID_AND_JOB_CARD = String.format( "SELECT * FROM %s WHERE item_id = :item_id AND job_card_id = :job_card_id", TABLE_NAME ); + private final String SELECT_BY_JOB_CARD = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME ); private final String SELECT_BY_ITEM_IDS_AND_JOB_CARD = String.format( "SELECT * FROM %s WHERE item_id IN (:item_ids) AND job_card_id = :job_card_id", TABLE_NAME ); private final String SELECT_LIKE_BARCODE = String.format("SELECT * FROM %s WHERE barcode LIKE :barcode", TABLE_NAME); - + private final String SELECT_FIRST_BUNDLE_BY_JOB_CARD = String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id ORDER BY created_at ASC LIMIT 1", TABLE_NAME); + private final String SELECT_LAST_BUNDLE_BY_JOB_CARD = String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id ORDER BY created_at DESC LIMIT 1", TABLE_NAME); public BundleDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; @@ -132,11 +133,10 @@ public class BundleDAO { return namedParameterJdbcTemplate.query( SELECT_BY_IDS , params, new BundleRowMapper() ); } - public List findByItemIdAndCardId( long itemId, long cardId ){ + public List findByCardId( long cardId ){ MapSqlParameterSource params = new MapSqlParameterSource(); - params.addValue("item_id", itemId ); params.addValue("job_card_id", cardId ); - return namedParameterJdbcTemplate.query( SELECT_BY_ITEM_ID_AND_JOB_CARD, params, new BundleRowMapper() ); + return namedParameterJdbcTemplate.query( SELECT_BY_JOB_CARD, params, new BundleRowMapper() ); } public List findByItemIdsAndCardId( List itemIds, long cardId ){ @@ -146,4 +146,19 @@ public class BundleDAO { params.addValue("job_card_id", cardId ); return namedParameterJdbcTemplate.query( SELECT_BY_ITEM_IDS_AND_JOB_CARD, params, new BundleRowMapper() ); } + + public Bundle findFirstBundleByCardId(long cardId) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("job_card_id", cardId); + return namedParameterJdbcTemplate.queryForObject(SELECT_FIRST_BUNDLE_BY_JOB_CARD, params, new BundleRowMapper()); + } + + public Bundle findLastBundleByCardId(long cardId) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("job_card_id", cardId); + return namedParameterJdbcTemplate.query(SELECT_LAST_BUNDLE_BY_JOB_CARD, params, new BundleRowMapper()) + .stream() + .findFirst().orElse(new Bundle()); + } + } \ No newline at end of file diff --git a/src/main/java/com/utopiaindustries/dao/ctp/FinishedItemDAO.java b/src/main/java/com/utopiaindustries/dao/ctp/FinishedItemDAO.java index cfbfdd3..0063843 100644 --- a/src/main/java/com/utopiaindustries/dao/ctp/FinishedItemDAO.java +++ b/src/main/java/com/utopiaindustries/dao/ctp/FinishedItemDAO.java @@ -32,6 +32,7 @@ public class FinishedItemDAO { private final String SELECT_BY_TERM = String.format( "SELECT * FROM %s WHERE barcode LIKE :term AND is_segregated = :is_segregated ORDER BY ID DESC", TABLE_NAME ); private final String SELECT_BY_STITCHED_ITEM_ID = String.format( "SELECT * FROM %s WHERE stitched_item_id = :stitched_item_id", TABLE_NAME ); private final String SELECT_BY_STITCHED_ITEM_IDS = String.format( "SELECT * FROM %s WHERE stitched_item_id IN (:stitched_item_ids)", TABLE_NAME ); + private final String COUNT_TOTAL_FINISH_ITEM= String.format("SELECT COUNT(*) FROM %s WHERE job_card_id = :job_card_id AND is_segregated IS TRUE ",TABLE_NAME); public FinishedItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; @@ -168,4 +169,11 @@ public class FinishedItemDAO { } return items; } + + public Long calculateTotalFinishItem( long jobCardId ){ + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("job_card_id", jobCardId ); + Long count = namedParameterJdbcTemplate.queryForObject(COUNT_TOTAL_FINISH_ITEM, params, Long.class); + return count != null ? count : 0; + } } \ No newline at end of file diff --git a/src/main/java/com/utopiaindustries/dao/ctp/InventoryAccountDAO.java b/src/main/java/com/utopiaindustries/dao/ctp/InventoryAccountDAO.java index 43aa3c1..1719428 100644 --- a/src/main/java/com/utopiaindustries/dao/ctp/InventoryAccountDAO.java +++ b/src/main/java/com/utopiaindustries/dao/ctp/InventoryAccountDAO.java @@ -27,6 +27,7 @@ public class InventoryAccountDAO { private final String SELECT_BY_IDS_AND_PARENT_ENTITY_TYPE_AND_PARENT_ID_AND_COUNT = String.format( "SELECT * FROM %s WHERE id IN (:ids) AND parent_entity_id = :parent_entity_id AND parent_entity_type = :parent_entity_type LIMIT :limit", TABLE_NAME ); private final String SELECT_BY_IDS_PACKAGING_AND_COUNT = String.format( "SELECT * FROM %s WHERE id IN (:ids) AND is_packaging = :is_packaging LIMIT :limit", TABLE_NAME ); private final String SELECT_BY_PARENT_TYPE_AND_PARENT_ID = String.format( "SELECT * FROM %s WHERE active = TRUE AND parent_entity_type = :parent_entity_type AND parent_entity_id = :parent_entity_id" , TABLE_NAME ); + private final String SELECT_BY_IS_PACKAGING_TRUE = String.format( "SELECT * FROM %s WHERE is_packaging IS TRUE", TABLE_NAME ); public InventoryAccountDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; @@ -144,4 +145,9 @@ public class InventoryAccountDAO { params.addValue("parent_entity_id", parentEntityId ); return namedParameterJdbcTemplate.query( SELECT_BY_PARENT_TYPE_AND_PARENT_ID, params, new InventoryAccountRowMapper() ); } + + public List getPackagingAccounts(){ + MapSqlParameterSource params = new MapSqlParameterSource(); + return namedParameterJdbcTemplate.query( SELECT_BY_IS_PACKAGING_TRUE, params, new InventoryAccountRowMapper() ); + } } \ No newline at end of file diff --git a/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegDAO.java b/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegDAO.java index ac4191f..e29293a 100644 --- a/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegDAO.java +++ b/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegDAO.java @@ -1,5 +1,6 @@ package com.utopiaindustries.dao.ctp; +import com.utopiaindustries.model.ctp.Bundle; import com.utopiaindustries.model.ctp.InventorySummary; import com.utopiaindustries.model.ctp.InventoryTransactionLeg; import com.utopiaindustries.util.KeyHolderFunctions; @@ -21,7 +22,7 @@ public class InventoryTransactionLegDAO { private final String SELECT_QUERY = String.format( "SELECT * FROM %s WHERE id = :id", TABLE_NAME ); private final String SELECT_ALL_QUERY = String.format( "SELECT * FROM %s ORDER BY id DESC", TABLE_NAME ); private final String DELETE_QUERY = String.format( "DELETE FROM %s WHERE id = :id", TABLE_NAME ); - private final String INSERT_QUERY = String.format( "INSERT INTO %s (id, transaction_id, item_id, sku, type, quantity, account_id, balance, transaction_leg_datetime, parent_document_type, parent_document_id, parent_document_piece_type) VALUES (:id, :transaction_id, :item_id, :sku, :type, :quantity, :account_id, :balance, :transaction_leg_datetime, :parent_document_type, :parent_document_id, :parent_document_piece_type) ON DUPLICATE KEY UPDATE transaction_id = VALUES(transaction_id), item_id = VALUES(item_id), sku = VALUES(sku), type = VALUES(type), quantity = VALUES(quantity), account_id = VALUES(account_id), balance = VALUES(balance), transaction_leg_datetime = VALUES(transaction_leg_datetime), parent_document_type = VALUES(parent_document_type), parent_document_id = VALUES(parent_document_id), parent_document_piece_type = VALUES(parent_document_piece_type)", TABLE_NAME ); + private final String INSERT_QUERY = String.format( "INSERT INTO %s (id, transaction_id, item_id, sku, type, quantity, account_id, balance, transaction_leg_datetime, parent_document_type, parent_document_id, parent_document_piece_type, job_card_id) VALUES (:id, :transaction_id, :item_id, :sku, :type, :quantity, :account_id, :balance, :transaction_leg_datetime, :parent_document_type, :parent_document_id, :parent_document_piece_type, :job_card_id) ON DUPLICATE KEY UPDATE transaction_id = VALUES(transaction_id), item_id = VALUES(item_id), sku = VALUES(sku), type = VALUES(type), quantity = VALUES(quantity), account_id = VALUES(account_id), balance = VALUES(balance), transaction_leg_datetime = VALUES(transaction_leg_datetime), parent_document_type = VALUES(parent_document_type), parent_document_id = VALUES(parent_document_id), parent_document_piece_type = VALUES(parent_document_piece_type), job_card_id = VALUES(job_card_id)", TABLE_NAME ); private final String SELECT_LAST_ACCOUNT_ID_AND_ITEM_ID = String.format( "SELECT * FROM %s WHERE account_id = :account_id AND item_id = :item_id AND parent_document_type= :parent_document_type AND parent_document_piece_type = :parent_document_piece_type ORDER BY id DESC LIMIT 1", TABLE_NAME ); private final String SELECT_BY_ACCOUNT_ID = String.format( "SELECT * FROM %s WHERE account_id = :account_id ORDER BY id DESC" , TABLE_NAME ); private final String SELECT_SUMMARY_BY_ACCOUNT_ID = String.format( "SELECT item_id, sku, parent_document_type,parent_document_piece_type," + @@ -44,6 +45,9 @@ public class InventoryTransactionLegDAO { " WHERE itl.type = :type" + " AND itl.parent_document_type = :parent_document_type" , TABLE_NAME, TABLE_NAME ); + private final String COUNT_TOTAL_SEGREGATE_ITEMS = String.format("SELECT COUNT(*) FROM %s WHERE parent_document_id IN (:parent_document_id) AND account_id = :account_id", TABLE_NAME); + private final String SELECT_FIRST_TRANSACTION_PARENT_TYPE_PARENT_ID = String.format("SELECT * FROM %s WHERE parent_document_id IN (:parent_document_id) AND parent_document_type = :parent_document_type ORDER BY transaction_leg_datetime ASC LIMIT 1", TABLE_NAME); + private final String SELECT_GROUP_By_TRANSACTION_PARENT_TYPE_PARENT_ID = String.format("SELECT * FROM %s WHERE parent_document_id IN (:parent_document_id) AND parent_document_type = :parent_document_type GROUP BY account_id", TABLE_NAME); public InventoryTransactionLegDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; @@ -63,7 +67,9 @@ public class InventoryTransactionLegDAO { .addValue( "transaction_leg_datetime", inventoryTransactionLeg.getTransactionLegDateTime() ) .addValue( "parent_document_type", inventoryTransactionLeg.getParentDocumentType() ) .addValue( "parent_document_id", inventoryTransactionLeg.getParentDocumentId() ) - .addValue("parent_document_piece_type", inventoryTransactionLeg.getParentDocumentPieceType() ); + .addValue("parent_document_piece_type", inventoryTransactionLeg.getParentDocumentPieceType() ) + .addValue("job_card_id", inventoryTransactionLeg.getJobCardId() ); + return params; } @@ -153,4 +159,34 @@ public class InventoryTransactionLegDAO { params.addValue("parent_document_type", parentType ); return namedParameterJdbcTemplate.query( SELECT_LAST_LEG_BY_TYPE_AND_PARENT_ID , params, new InventoryTransactionLegRowMapper() ); } + + public Long CalculateTotalGradingItems(List finishItemIds, Integer accountId) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("parent_document_id", finishItemIds); + params.addValue("account_id", accountId); + + Long count = namedParameterJdbcTemplate.queryForObject(COUNT_TOTAL_SEGREGATE_ITEMS, params, Long.class); + return count != null ? count : 0; + } + + public InventoryTransactionLeg getFirstStitchBundleTime(List parentsIds, String parentType) { + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("parent_document_id", parentsIds); + params.addValue("parent_document_type", parentType); + + return namedParameterJdbcTemplate.query(SELECT_FIRST_TRANSACTION_PARENT_TYPE_PARENT_ID, params,new InventoryTransactionLegRowMapper()) + .stream() + .findFirst() + .orElse( new InventoryTransactionLeg() ); + } + + public List getTransactionByParentIdAndType(List parentIds , String parentType){ + if ( parentIds == null || parentIds.isEmpty() ){ + return new ArrayList<>(); + } + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("parent_document_id", parentIds ); + params.addValue("parent_document_type", parentType ); + return namedParameterJdbcTemplate.query( SELECT_GROUP_By_TRANSACTION_PARENT_TYPE_PARENT_ID , params, new InventoryTransactionLegRowMapper() ); + } } \ No newline at end of file diff --git a/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegRowMapper.java b/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegRowMapper.java index da599ea..b632d9d 100644 --- a/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegRowMapper.java +++ b/src/main/java/com/utopiaindustries/dao/ctp/InventoryTransactionLegRowMapper.java @@ -23,6 +23,7 @@ public class InventoryTransactionLegRowMapper implements RowMapper getJobCardProgress(String jobCardID){ @@ -57,22 +61,6 @@ public class ReportingService { .map(item -> Optional.ofNullable(item.getExpectedProduction()).orElse(BigDecimal.ZERO)) .reduce(BigDecimal.ZERO, BigDecimal::add); - if (totalProduction.compareTo(BigDecimal.ZERO) == 0 && actualProduction.compareTo(BigDecimal.ZERO) == 0 ) { - totalProgress.put("Job Card Progress", BigDecimal.ZERO.intValue()); - } else { - if (actualProduction.compareTo(expectedProduction)>0){ - BigDecimal progressPercentage = actualProduction.add(totalProduction) - .divide(actualProduction.multiply(BigDecimal.valueOf(2)), 4, RoundingMode.HALF_UP) - .multiply(BigDecimal.valueOf(100)); - totalProgress.put("Job Card Progress", progressPercentage.intValue()); - }else { - BigDecimal progressPercentage = actualProduction.add(totalProduction) - .divide(expectedProduction.multiply(BigDecimal.valueOf(2)), 4, RoundingMode.HALF_UP) - .multiply(BigDecimal.valueOf(100)); - totalProgress.put("Job Card Progress", progressPercentage.intValue()); - } - } - if (actualProduction.compareTo(BigDecimal.ZERO) == 0 ) { totalProgress.put("Cutting Progress", BigDecimal.ZERO.intValue()); @@ -88,8 +76,8 @@ public class ReportingService { if (totalProduction.compareTo(BigDecimal.ZERO) == 0){ totalProgress.put("Stitching Progress", BigDecimal.ZERO.intValue()); }else { - BigDecimal stitchingProgress = expectedProduction - .divide(totalProduction, 4, BigDecimal.ROUND_HALF_UP) + BigDecimal stitchingProgress = totalProduction + .divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .multiply(BigDecimal.valueOf(100)); totalProgress.put("Stitching Progress", stitchingProgress.intValue()); } @@ -102,8 +90,253 @@ public class ReportingService { .multiply(BigDecimal.valueOf(100)); totalProgress.put("QA Progress", qaProgress.intValue()); } + Long totalFinishItem = finishedItemDAO.calculateTotalFinishItem(Long.parseLong(jobCardID)); + if (totalFinishItem == 0){ + totalProgress.put("Finishing Progress", BigDecimal.ZERO.intValue()); + }else { + BigDecimal finishItemProgress = BigDecimal.valueOf(totalFinishItem) + .divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) + .multiply(BigDecimal.valueOf(100)); + totalProgress.put("Finishing Progress", finishItemProgress.intValue()); + } + + if (totalProduction.compareTo(BigDecimal.ZERO) == 0 && actualProduction.compareTo(BigDecimal.ZERO) == 0 ) { + totalProgress.put("Job Card Progress", BigDecimal.ZERO.intValue()); + } else { + if (actualProduction.compareTo(expectedProduction)>0){ + BigDecimal progressPercentage = actualProduction.add(totalProduction).add(BigDecimal.valueOf(qaProgressItems)).add(BigDecimal.valueOf(totalFinishItem)) + .divide(actualProduction.multiply(BigDecimal.valueOf(4)), 4, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(100)); + totalProgress.put("Job Card Progress", progressPercentage.intValue()); + }else { + BigDecimal progressPercentage = actualProduction.add(totalProduction).add(BigDecimal.valueOf(qaProgressItems)).add(BigDecimal.valueOf(totalFinishItem)) + .divide(expectedProduction.multiply(BigDecimal.valueOf(4)), 4, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(100)); + totalProgress.put("Job Card Progress", progressPercentage.intValue()); + } + } return totalProgress; } } + public Integer getTotalProduction(String jobCardID){ + if (jobCardID == null){ + return 0; + }else { + List jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID)); + BigDecimal actualProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + return actualProduction.intValue(); + } + } + + public Map getCompleteProduction(String jobCardID){ + if (jobCardID == null){ + return new HashMap<>(); + }else { + HashMap totalProgress = new HashMap<>(); + List jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID)); + BigDecimal totalProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal actualProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal expectedProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getExpectedProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + Long qaProgressItems = stitchingOfflineItemDAO.CalculateTotalQA(Long.parseLong(jobCardID)); + Long totalFinishItem = finishedItemDAO.calculateTotalFinishItem(Long.parseLong(jobCardID)); + + totalProgress.put("Cutting Progress", actualProduction.intValue()); + totalProgress.put("Stitching Progress", totalProduction.intValue()); + totalProgress.put("QA Progress", qaProgressItems.intValue()); + totalProgress.put("Finishing Progress", totalFinishItem.intValue()); + + return totalProgress; + } + } + + public Map getSegregateItems(String jobCardID){ + if (jobCardID == null){ + return new HashMap<>(); + }else { + HashMap gradingItems = new HashMap<>(); + List inventoryAccounts = inventoryAccountDAO.getPackagingAccounts(); + List finishedItems = finishedItemDAO.findByJobCardId(Long.parseLong(jobCardID)); + List finishItemsIds = finishedItems.stream() + .map(FinishedItem::getId).collect(Collectors.toList()); + if (finishItemsIds.isEmpty()){ + return gradingItems; + }else { + for (InventoryAccount inventoryAccount : inventoryAccounts){ + long totalGradingItems = inventoryTransactionLegDAO.CalculateTotalGradingItems(finishItemsIds,(int) inventoryAccount.getId()); + gradingItems.put(inventoryAccount.getTitle(), (int) totalGradingItems); + } + return gradingItems; + } + } + } + + public HashMap getEachPhaseTotalTime(String jobCardID){ + if (jobCardID == null){ + return new HashMap<>(); + }else { + HashMap phasesTimes = new HashMap<>(); + JobCard jobCard = jobCardDAO.find(Long.parseLong(jobCardID)); + Bundle lastBundle = bundleDAO.findLastBundleByCardId(Long.parseLong(jobCardID)); + + //Cutting time + StringBuilder cuttingTime = generateTime(jobCard.getCreatedAt(),lastBundle.getCreatedAt()); + phasesTimes.put("Cutting Total Time",cuttingTime.toString()); + + //Stitching time + List bundlesIds = bundleDAO.findByCardId(Long.parseLong(jobCardID)).stream().map(Bundle::getId).collect(Collectors.toList()); + if (!bundlesIds.isEmpty()){ + InventoryTransactionLeg inventoryTransactionLeg = inventoryTransactionLegDAO.getFirstStitchBundleTime(bundlesIds,"STITCH_BUNDLE"); + StitchingOfflineItem lastStitchItem = stitchingOfflineItemDAO.getLastStitchItemByCardIdAndTime(Long.parseLong(jobCardID)); + StringBuilder stitchingTime = generateTime(inventoryTransactionLeg.getTransactionLegDateTime(),lastStitchItem.getCreatedAt()); + phasesTimes.put("Stitching Total Time",stitchingTime.toString()); + }else { + phasesTimes.put("Stitching Total Time",null); + + } + + + return phasesTimes; + } + } + + public HashMap countPendingItemsOnDifferentPhases(String jobCardID){ + if (jobCardID == null){ + return new HashMap<>(); + }else { + HashMap phasePending = new HashMap(); + List jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID)); + BigDecimal totalProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal actualProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal expectedProduction = jobCardItems.stream() + .map(item -> Optional.ofNullable(item.getExpectedProduction()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if(actualProduction.compareTo(totalProduction) == 0) { + phasePending.put("Stitching Total Time", null); + }else { + phasePending.put("Stitching Total Time", "Pending"); + } + return phasePending; + } + } + + public Map> getCuttingDetails(String jobCardID) { + if (jobCardID == null) { + return Collections.emptyMap(); + } + + Map> cuttingDetails = new HashMap<>(); + Map cuttingAccount = new HashMap<>(); + Map cuttingPersonName = new HashMap<>(); + Map cuttingDate = new HashMap<>(); + + List bundles = bundleDAO.findByCardId(Long.parseLong(jobCardID)); + if (bundles.isEmpty()) { + return cuttingDetails; + } + + Map bundleMap = bundles.stream() + .collect(Collectors.toMap(Bundle::getId, Function.identity())); + + List bundleIds = new ArrayList<>(bundleMap.keySet()); + List inventoryTransactionLegs = inventoryTransactionLegDAO + .getTransactionByParentIdAndType(bundleIds, "BUNDLE"); + + for (InventoryTransactionLeg inventoryTransactionLeg : inventoryTransactionLegs) { + Integer accountId = inventoryTransactionLeg.getAccountId(); + InventoryAccount inventoryAccount = inventoryAccountDAO.find(accountId.longValue()); + if (inventoryAccount != null) { + String accountTitle = inventoryAccount.getTitle(); + cuttingAccount.put(accountTitle, inventoryAccount); + Bundle bundle = bundleMap.get(inventoryTransactionLeg.getParentDocumentId()); + cuttingPersonName.put(accountTitle, (bundle != null) ? bundle.getCreatedBy() : null); + cuttingDate.put(accountTitle,(bundle != null) ? bundle.getCreatedAt().toLocalDate() : null); + } + } + + cuttingDetails.put("accounts", new HashMap<>(cuttingAccount)); + cuttingDetails.put("personName", new HashMap<>(cuttingPersonName)); + cuttingDetails.put("date", new HashMap<>(cuttingDate)); + + return cuttingDetails; + } + + public Map> getStitchingDetails(String jobCardID) { + if (jobCardID == null) { + return Collections.emptyMap(); + } + + Map> stitchingDetails = new HashMap<>(); + Map stitchingAccount = new HashMap<>(); + Map stitchingPersonName = new HashMap<>(); + Map stitchingDate = new HashMap<>(); + + List stitchingOfflineItems = stitchingOfflineItemDAO.findByJobCardId(Long.parseLong(jobCardID)); + if (stitchingOfflineItems.isEmpty()) { + return stitchingDetails; + } + + Map stitchingOfflineItemMap = stitchingOfflineItems.stream() + .collect(Collectors.toMap(StitchingOfflineItem::getId, Function.identity())); + + List stitchingItemsArray = new ArrayList<>(stitchingOfflineItemMap.keySet()); + List inventoryTransactionLegs = inventoryTransactionLegDAO + .getTransactionByParentIdAndType(stitchingItemsArray, "STITCHING_OFFLINE"); + + for (InventoryTransactionLeg inventoryTransactionLeg : inventoryTransactionLegs) { + Integer accountId = inventoryTransactionLeg.getAccountId(); + InventoryAccount inventoryAccount = inventoryAccountDAO.find(accountId.longValue()); + if (inventoryAccount != null) { + String accountTitle = inventoryAccount.getTitle(); + stitchingAccount.put(accountTitle, inventoryAccount); + StitchingOfflineItem stitchingOfflineItem = stitchingOfflineItemMap.get(inventoryTransactionLeg.getParentDocumentId()); + stitchingPersonName.put(accountTitle, (stitchingOfflineItem != null) ? stitchingOfflineItem.getCreatedBy() : null); + stitchingDate.put(accountTitle,(stitchingOfflineItem != null) ? stitchingOfflineItem.getCreatedAt().toLocalDate() : null); + } + } + + stitchingDetails.put("accounts", new HashMap<>(stitchingAccount)); + stitchingDetails.put("personName", new HashMap<>(stitchingPersonName)); + stitchingDetails.put("date", new HashMap<>(stitchingDate)); + + return stitchingDetails; + } + + private StringBuilder generateTime(LocalDateTime startDate, LocalDateTime endDate){ + StringBuilder totalTime = new StringBuilder(); + if(startDate != null && endDate != null){ + Duration duration = Duration.between(startDate, endDate); + if (duration.toDays() > 0) { + totalTime.append(String.format("%d days, ", duration.toDays())); + } + + if (duration.toHours() % 24 > 0) { + totalTime.append(String.format("%d hours, ", duration.toHours() % 24)); + } + + if (duration.toMinutes() % 60 > 0) { + totalTime.append(String.format("%d minutes ", duration.toMinutes() % 60)); + } + return totalTime; + }else return totalTime.append(""); + } + } diff --git a/src/main/resources/static/js/charts.js b/src/main/resources/static/js/charts.js index d716511..8ec6c0f 100644 --- a/src/main/resources/static/js/charts.js +++ b/src/main/resources/static/js/charts.js @@ -4,8 +4,7 @@ document.addEventListener("DOMContentLoaded", function () { return; } - function createGaugeChart(speed, color, divId, title, height, width , fontSize, fontYAxis, fontXAxis, fontColor, total, actual) { - console.log(fontSize) + function createGaugeChart(progress, color, divId, title, height, width , fontSize, fontYAxis, fontXAxis, fontColor, total, actual, aGrade, bGrade, cGrade) { Highcharts.chart(divId, { chart: { type: 'solidgauge', @@ -45,10 +44,9 @@ document.addEventListener("DOMContentLoaded", function () { }, series: [{ name: 'Percentage', - data: [speed], + data: [progress], dataLabels: { format: '{y}% ', -// format: '{y}%
' + "Total: "+ total+ "
Complete: " + actual, y: fontYAxis, x: fontXAxis, borderWidth: 0, @@ -60,6 +58,9 @@ document.addEventListener("DOMContentLoaded", function () { }, zIndex: 10 }, + tooltip: { + pointFormat: title == 'Finishing Progress' ? 'Percentage: ' + progress + '%
Total: ' + total + '
Completed: ' + actual + '
A Grade: ' + aGrade + '
B Grade: ' + bGrade + '
C Grade: ' + cGrade : 'Percentage: ' + progress + '%
Total: ' + total + '
Completed:'+actual + }, color: color, showInLegend: false }], @@ -68,11 +69,66 @@ document.addEventListener("DOMContentLoaded", function () { } }); } + function createBarChart(config, height, width, title) { + if (!document.getElementById(config.containerId)) { + return; + } + + Highcharts.chart(config.containerId, { + chart: { + type: 'column', + height: height, + width: width, + }, + title: { + text: title, + align: 'center', // Optionally align title in center + verticalAlign: 'top', + y: 30 // Offset from top + }, + xAxis: { + categories: [ + "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", + "Next Monday", "Next Tuesday", "Next Wednesday", "Next Friday", "Next Saturday", "Next Sunday" + ], + labels: { + rotation: -45 // Optional: Rotate labels if needed + } + }, + yAxis: { + min: 0, + max: 100, + title: { + text: 'Total Progress' + }, + labels: { + format: '{value}%' + } + }, + scrollbar: { + enabled: true // Ensure Highcharts scrollbar is enabled + }, + series: [{ + name: 'Model A', + data: [100, 75, 78, 79, 77, 81, 80, 76, 79, 80, 76, 79, 80, 76, 79, 80, 80] + }, { + name: 'Model B', + data: [85, 78, 82, 80, 79, 84, 83, 78, 82, 83, 76, 79, 80, 76, 79, 80, 85] + }, { + name: 'Model C', + data: [88, 80, 85, 82, 81, 87, 85, 80, 85, 86, 76, 79, 80, 76, 79, 80, 88] + }, { + name: 'Model D', + data: [90, 83, 87, 84, 83, 89, 88, 83, 87, 88, 76, 79, 80, 76, 79, 80, 90] + }] + }); + } + initializeGauges(); function initializeGauges() { const gaugeDivs2 = document.querySelectorAll('.gauge-chart2'); gaugeDivs2.forEach(function (div) { - const speed = div.getAttribute('data-speed'); + const progress = div.getAttribute('data-progress'); const color = div.getAttribute('data-color'); const title = div.getAttribute('data-title'); const height = div.getAttribute('data-height'); @@ -82,11 +138,11 @@ document.addEventListener("DOMContentLoaded", function () { const total = div.getAttribute('data-totalProduction'); const actual = div.getAttribute('data-actualProduction'); const divId = div.id; - createGaugeChart(parseInt(speed), color, divId, title, height, width, fontSize, -20, 4, fontColor, total, actual); + createGaugeChart(parseInt(progress), color, divId, title, height, width, fontSize, -20, 4, fontColor, total, actual); }); const gaugeDivs = document.querySelectorAll('.gauge-chart'); gaugeDivs.forEach(function (div) { - const speed = div.getAttribute('data-speed'); + const progress = div.getAttribute('data-progress'); const color = div.getAttribute('data-color'); const title = div.getAttribute('data-title'); const height = div.getAttribute('data-height'); @@ -95,9 +151,32 @@ document.addEventListener("DOMContentLoaded", function () { const fontColor = div.getAttribute('data-fontColor'); const total = div.getAttribute('data-totalProduction'); const actual = div.getAttribute('data-actualProduction'); + const aGrade = div.getAttribute('data-aGrade'); + const bGrade = div.getAttribute('data-bGrade'); + const cGrade = div.getAttribute('data-cGrade'); const divId = div.id; - console.log(title) - createGaugeChart(parseInt(speed), color, divId, title, height, width, fontSize, -15, 2, fontColor, total, actual); + createGaugeChart(parseInt(progress), color, divId, title, height, width, fontSize, -15, 2, fontColor, total, actual, aGrade, bGrade, cGrade); + }); + + const barChart = document.querySelectorAll('.barChart'); + barChart.forEach(function (div) { + const progress = div.getAttribute('data-progress'); + const color = div.getAttribute('data-color'); + const title = div.getAttribute('data-title'); + const height = div.getAttribute('data-height'); + const width = div.getAttribute('data-width'); + const fontSize = div.getAttribute('data-fontSize'); + const fontColor = div.getAttribute('data-fontColor'); + const total = div.getAttribute('data-totalProduction'); + const actual = div.getAttribute('data-actualProduction'); + const aGrade = div.getAttribute('data-aGrade'); + const bGrade = div.getAttribute('data-bGrade'); + const cGrade = div.getAttribute('data-cGrade'); + const divId = div.id; + createBarChart(barChartConfigs, height, width, title,); + + }); + } }); diff --git a/src/main/resources/templates/reporting/job-card-report.html b/src/main/resources/templates/reporting/job-card-report.html index 3398dba..5f0d6f4 100644 --- a/src/main/resources/templates/reporting/job-card-report.html +++ b/src/main/resources/templates/reporting/job-card-report.html @@ -21,11 +21,11 @@
+ th:id="'gauge-chart-' + ${index}" class="gauge-chart" style="width: 200px; height: 230px;" + th:data-progress="${jobCardProgress.get(title)}" + th:data-totalProduction="${totalProduction}" + th:data-actualProduction="${completeProduction.get(title)}" + th:data-title="${title}" + th:data-width="230" + th:data-height="230" + th:data-fontSize="20" + th:data-aGrade="${segregateItems.get('A GRADE') == null ? 0 : segregateItems.get('A GRADE')}" + th:data-bGrade="${segregateItems.get('B GRADE') == null ? 0 : segregateItems.get('B GRADE')}" + th:data-cGrade="${segregateItems.get('C GRADE') == null ? 0 : segregateItems.get('C GRADE')}" + th:data-fontColor="'#17202a'" + th:data-color="'#95a5a6'">
@@ -56,18 +57,15 @@
-
-
Cutting Total Time
-
10
+
+
+
+
-
Stitching Total Time
-
Quality Checking Total Time
-
Finishing Total Time
-
Packaging Total Time
- +
Cutting Detail @@ -78,30 +76,33 @@ ID Title Cutting - Cutting Day + Cutting Date Cutting Table Descriptions - - - - - - + + + + + + + + + - +
Stitching Detail
- + @@ -110,27 +111,35 @@ - - - - - - - - + + + + + + + + +
ID TitleStitching Table Descriptions
+ + +
- - - - - + + +
+
+
- - + +
@@ -236,18 +245,18 @@ completedPiece:100 } ]; - - - - - - - - - - - - + const barChartConfigs = { + containerId: "barChart", + title: "Day-wise Performance Metrics", + max:16, + categories: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday", "Friday", "Saturday", "Sunday", "Next Monday", "Next Tuesday", "Next Wednesday"], + series: [ + { name: "Model A", data: [0.8, 0.75, 0.78, 0.76, 0.74, 0.80, 0.77, 0.79, 0.81, 0.76, 0.80, 0.77, 0.79, 0.81, 0.76], color: "#007bff" }, + { name: "Model B", data: [0.85, 0.7, 0.77, 0.79, 0.72, 0.83, 0.78, 0.80, 0.82, 0.75,0.83, 0.78, 0.80, 0.82, 0.75], color: "#28a745" }, + { name: "Model C", data: [0.82, 0.72, 0.79, 0.80, 0.78, 0.85, 0.81, 0.84, 0.86, 0.79, 0.85, 0.81, 0.84, 0.86, 0.79], color: "#dc3545" }, + { name: "Model D", data: [0.88, 0.78, 0.83, 0.85, 0.82, 0.89, 0.85, 0.87, 0.90, 0.83, 0.89, 0.85, 0.87, 0.90, 0.83], color: "#fd7e14" } + ] +};