package com.utopiaindustries.service; import com.utopiaindustries.dao.ctp.*; import com.utopiaindustries.model.ctp.*; import com.utopiaindustries.util.CTPDateTimeFormat; import com.utopiaindustries.util.StringUtils; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @Service public class ReportingService { private final JobCardItemDAO jobCardItemDAO; private final CutPieceDAO cutPieceDAO; private final BundleDAO bundleDAO; private final InventoryTransactionLegDAO inventoryTransactionLegDAO; private final InventoryTransactionDAO inventoryTransactionDAO; private final JobCardDAO jobCardDAO; private final CryptographyService cryptographyService; private final MasterBundleDAO masterBundleDAO; private final FinishedItemDAO finishedItemDAO; private final StitchingOfflineItemDAO stitchingOfflineItemDAO; private final InventoryAccountDAO inventoryAccountDAO; public ReportingService( JobCardItemDAO jobCardItemDAO, CutPieceDAO cutPieceDAO, BundleDAO bundleDAO, InventoryTransactionLegDAO inventoryTransactionLegDAO, InventoryTransactionDAO inventoryTransactionDAO, JobCardDAO jobCardDAO, CryptographyService cryptographyService, MasterBundleDAO masterBundleDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO, InventoryAccountDAO inventoryAccountDAO) { this.jobCardItemDAO = jobCardItemDAO; this.cutPieceDAO = cutPieceDAO; this.bundleDAO = bundleDAO; this.inventoryTransactionLegDAO = inventoryTransactionLegDAO; this.inventoryTransactionDAO = inventoryTransactionDAO; this.jobCardDAO = jobCardDAO; this.cryptographyService = cryptographyService; this.masterBundleDAO = masterBundleDAO; this.finishedItemDAO = finishedItemDAO; this.stitchingOfflineItemDAO = stitchingOfflineItemDAO; this.inventoryAccountDAO = inventoryAccountDAO; } public Map getJobCardProgress(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); if (actualProduction.compareTo(BigDecimal.ZERO) == 0 ) { totalProgress.put("Cutting Progress", BigDecimal.ZERO.intValue()); } else if (actualProduction.compareTo(expectedProduction) < 0) { BigDecimal cuttingProgress = expectedProduction .divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .multiply(BigDecimal.valueOf(100)); totalProgress.put("Cutting Progress", cuttingProgress.intValue()); }else if (actualProduction.compareTo(expectedProduction) > 0 || actualProduction.compareTo(expectedProduction) == 0){ totalProgress.put("Cutting Progress", 100); } if (totalProduction.compareTo(BigDecimal.ZERO) == 0){ totalProgress.put("Stitching Progress", BigDecimal.ZERO.intValue()); }else { BigDecimal stitchingProgress = totalProduction .divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .multiply(BigDecimal.valueOf(100)); totalProgress.put("Stitching Progress", stitchingProgress.intValue()); } Long qaProgressItems = stitchingOfflineItemDAO.CalculateTotalQA(Long.parseLong(jobCardID)); if (qaProgressItems == 0){ totalProgress.put("QA Progress", 0); }else { BigDecimal qaProgress = BigDecimal.valueOf(qaProgressItems) .divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .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; } public Map> getPhasesProgressDayWise(String jobCardID) { if (jobCardID == null) { return Collections.emptyMap(); } 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); LocalDateTime startDate = jobCardDAO.find(Long.parseLong(jobCardID)).getCreatedAt(); HashMap> barChartData = new HashMap<>(); List inventoryTransactionLegs = inventoryTransactionLegDAO .getTransactionByJobCardAndDatesAndType(Long.parseLong(jobCardID), startDate.toString(), LocalDateTime.now().toString(), "IN"); //remove quality multiple transaction entry approved/rejects List filteredTransactions = inventoryTransactionLegs.stream() .collect(Collectors.toMap( leg -> leg.getAccountId() + "-" + leg.getParentDocumentType() + "-" + leg.getParentDocumentId(), leg -> leg, // Value (existing, replacement) -> existing.getTransactionLegDateTime().isBefore(replacement.getTransactionLegDateTime()) ? existing : replacement // Keep Oldest )) .values() .stream() .collect(Collectors.toList()); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); List uniqueDates = inventoryTransactionLegs.stream() .map(leg -> leg.getTransactionLegDateTime().toLocalDate().format(formatter)) .distinct() .sorted() .collect(Collectors.toList()); Map dateIndexMap = new HashMap<>(); for (int i = 0; i < uniqueDates.size(); i++) { dateIndexMap.put(uniqueDates.get(i), i); } List cuttingList = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0)); List stitchingList = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0)); List qualityList = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0)); List finishItems = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0)); for (InventoryTransactionLeg leg : filteredTransactions) { String dateKey = leg.getTransactionLegDateTime().format(formatter); int index = dateIndexMap.get(dateKey); if ("BUNDLE".equals(leg.getParentDocumentType())) { Bundle bundle = bundleDAO.find(leg.getParentDocumentId()); cuttingList.set(index, cuttingList.get(index) + bundle.getWrapQuantity().intValue()); } else if ("STITCHING_OFFLINE".equals(leg.getParentDocumentType())) { stitchingList.set(index, stitchingList.get(index) + leg.getQuantity().intValue()); } else if ("FINISHED_ITEM".equals(leg.getParentDocumentType()) && (leg.getAccountId().equals(7) || leg.getAccountId().equals(12))) { if (index == 0 || !dateIndexMap.containsKey(dateKey)) { qualityList.set(index, 0); } qualityList.set(index, qualityList.get(index) + leg.getQuantity().intValue()); }else if ("FINISHED_ITEM".equals(leg.getParentDocumentType()) && (leg.getAccountId().equals(8) || leg.getAccountId().equals(9) || leg.getAccountId().equals(10))) { if (index == 0 || !dateIndexMap.containsKey(dateKey)) { finishItems.set(index, 0); } finishItems.set(index, finishItems.get(index) + leg.getQuantity().intValue()); } } barChartData.put("dates", uniqueDates); barChartData.put("cutting", cuttingList); barChartData.put("stitching", stitchingList); barChartData.put("quality", qualityList); barChartData.put("finishing", finishItems); return barChartData; } 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(""); } }