add PO's based job Card dashboard

add-reporting-dashboard
Usama Khan 2025-04-11 16:56:33 -07:00
parent 9a541b8389
commit 91d6fdaa89
14 changed files with 563 additions and 48 deletions

View File

@ -2,6 +2,7 @@ package com.utopiaindustries.controller;
import com.utopiaindustries.auth.CuttingRole; import com.utopiaindustries.auth.CuttingRole;
import com.utopiaindustries.auth.ReportingRole; import com.utopiaindustries.auth.ReportingRole;
import com.utopiaindustries.model.ctp.JobCardItem;
import com.utopiaindustries.model.ctp.SummaryInventoryReport; import com.utopiaindustries.model.ctp.SummaryInventoryReport;
import com.utopiaindustries.service.ReportingService; import com.utopiaindustries.service.ReportingService;
import com.utopiaindustries.service.SummaryInventoryReportService; import com.utopiaindustries.service.SummaryInventoryReportService;
@ -9,6 +10,7 @@ import com.utopiaindustries.util.StringUtils;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@ -17,6 +19,7 @@ import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
@Controller @Controller
@ReportingRole @ReportingRole
@ -64,6 +67,18 @@ public class ReportingController {
return "/reporting/job-card-report"; return "/reporting/job-card-report";
} }
@GetMapping( "/po-report")
public String poReport( Model model){
model.addAttribute("allPOs", reportingService.getAllPOs());
return "/reporting/po-report";
}
@GetMapping( value = "/po-report-view/{poNo}" )
public String showJobCardDetail( @PathVariable("poNo") String poNo, @RequestParam(value = "select-date", required = false) String selectDate ,
Model model ){
model.addAttribute("allJobCard", reportingService.getAllPoJobCards(poNo, selectDate));
return "/reporting/po-job-card-report";
}
private ArrayList<LocalDate> generateDateList(LocalDate start, LocalDate end) { private ArrayList<LocalDate> generateDateList(LocalDate start, LocalDate end) {
ArrayList<LocalDate> localDates = new ArrayList<>(); ArrayList<LocalDate> localDates = new ArrayList<>();

View File

@ -31,6 +31,7 @@ public class BundleDAO {
private final String SELECT_LIKE_BARCODE = String.format("SELECT * FROM %s WHERE barcode LIKE :barcode", 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_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); 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);
private final String SELECT_BY_JOB_CARD_AND_DATE = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id AND (:start_date IS NULL OR :end_date IS NULL OR created_at BETWEEN :start_date AND :end_date)", TABLE_NAME );
public BundleDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { public BundleDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -161,4 +162,12 @@ public class BundleDAO {
.findFirst().orElse(new Bundle()); .findFirst().orElse(new Bundle());
} }
public List<Bundle> findByCardIdAndDATE( long cardId, String startDate, String endDate){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("job_card_id", cardId );
params.addValue( "start_date", startDate );
params.addValue( "end_date", endDate );
return namedParameterJdbcTemplate.query( SELECT_BY_JOB_CARD_AND_DATE, params, new BundleRowMapper() );
}
} }

View File

@ -33,6 +33,7 @@ public class FinishedItemDAO {
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_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 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); 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);
private final String SELECT_BY_JOB_CARD_AND_DATE = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id AND (:start_date IS NULL OR :end_date IS NULL OR created_at BETWEEN :start_date AND :end_date)", TABLE_NAME );
public FinishedItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { public FinishedItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -176,4 +177,13 @@ public class FinishedItemDAO {
Long count = namedParameterJdbcTemplate.queryForObject(COUNT_TOTAL_FINISH_ITEM, params, Long.class); Long count = namedParameterJdbcTemplate.queryForObject(COUNT_TOTAL_FINISH_ITEM, params, Long.class);
return count != null ? count : 0; return count != null ? count : 0;
} }
public List<FinishedItem> calculateTotalFinishItem( long jobCardId, String startDate, String endDate ){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("job_card_id", jobCardId );
params.addValue( "start_date", startDate );
params.addValue( "end_date", endDate );
return namedParameterJdbcTemplate.query( SELECT_BY_JOB_CARD_AND_DATE, params, new FinishedItemRowMapper() );
}
} }

View File

@ -49,6 +49,7 @@ public class InventoryTransactionLegDAO {
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_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); 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);
private final String SELECT_JOB_CARD_DATES = String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id AND (:start_date IS NULL OR :end_date IS NULL OR transaction_leg_datetime BETWEEN :start_date AND :end_date) AND type = :type ", TABLE_NAME); private final String SELECT_JOB_CARD_DATES = String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id AND (:start_date IS NULL OR :end_date IS NULL OR transaction_leg_datetime BETWEEN :start_date AND :end_date) AND type = :type ", TABLE_NAME);
private final String SELECT_JOB_CARD_And_Date_Type_Account_Id = String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id AND (:start_date IS NULL OR :end_date IS NULL OR transaction_leg_datetime BETWEEN :start_date AND :end_date) AND type = :type AND account_id IN (:account_ids)", TABLE_NAME);
public InventoryTransactionLegDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { public InventoryTransactionLegDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -197,6 +198,20 @@ public class InventoryTransactionLegDAO {
params.addValue("start_date", startDate ); params.addValue("start_date", startDate );
params.addValue("end_date", endDate ); params.addValue("end_date", endDate );
params.addValue("type", type ); params.addValue("type", type );
System.out.println("Start Date: " + startDate);
System.out.println("End Date: " + endDate);
System.out.println("Params: " + params.getValues());
return namedParameterJdbcTemplate.query( SELECT_JOB_CARD_DATES , params, new InventoryTransactionLegRowMapper() ); return namedParameterJdbcTemplate.query( SELECT_JOB_CARD_DATES , params, new InventoryTransactionLegRowMapper() );
} }
public List<InventoryTransactionLeg> getTransactionByJobCardAndDatesAndTypeAndAccountID(long jobCardID, String startDate, String endDate, String type, List<Integer> accountId){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("job_card_id", jobCardID );
params.addValue("start_date", startDate );
params.addValue("end_date", endDate );
params.addValue("type", type );
params.addValue("account_ids", accountId );
return namedParameterJdbcTemplate.query( SELECT_JOB_CARD_And_Date_Type_Account_Id , params, new InventoryTransactionLegRowMapper() );
}
} }

View File

@ -28,7 +28,6 @@ public class JobCardDAO {
private final String SELECT_BY_LIKE_CODE = String.format( "SELECT * FROM %s WHERE code like :code", TABLE_NAME ); private final String SELECT_BY_LIKE_CODE = String.format( "SELECT * FROM %s WHERE code like :code", TABLE_NAME );
private final String SELECT_BY_LIMIT = String.format( "SELECT * FROM %s WHERE created_by = :created_by ORDER BY id DESC limit :limit", TABLE_NAME ); private final String SELECT_BY_LIMIT = String.format( "SELECT * FROM %s WHERE created_by = :created_by ORDER BY id DESC limit :limit", TABLE_NAME );
// prepare query params // prepare query params
private MapSqlParameterSource prepareInsertQueryParams( JobCard jobCard ) { private MapSqlParameterSource prepareInsertQueryParams( JobCard jobCard ) {
MapSqlParameterSource params = new MapSqlParameterSource(); MapSqlParameterSource params = new MapSqlParameterSource();

View File

@ -31,6 +31,7 @@ public class StitchingOfflineItemDAO {
private final String SELECT_BY_MASTER_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME ); private final String SELECT_BY_MASTER_ID = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id", TABLE_NAME );
private final String COUNT_TOTAL_QA_ITEMS= String.format("SELECT COUNT(*) FROM %s WHERE job_card_id = :job_card_id AND is_qa IS TRUE",TABLE_NAME); private final String COUNT_TOTAL_QA_ITEMS= String.format("SELECT COUNT(*) FROM %s WHERE job_card_id = :job_card_id AND is_qa IS TRUE",TABLE_NAME);
private final String SELECT_BY_TIME_AND_CARD_ID= String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id ORDER BY created_at DESC LIMIT 1;",TABLE_NAME); private final String SELECT_BY_TIME_AND_CARD_ID= String.format("SELECT * FROM %s WHERE job_card_id = :job_card_id ORDER BY created_at DESC LIMIT 1;",TABLE_NAME);
private final String SELECT_BY_JOB_CARD_AND_DATE = String.format( "SELECT * FROM %s WHERE job_card_id = :job_card_id AND (:start_date IS NULL OR :end_date IS NULL OR created_at BETWEEN :start_date AND :end_date)", TABLE_NAME );
public StitchingOfflineItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { public StitchingOfflineItemDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -160,4 +161,12 @@ public class StitchingOfflineItemDAO {
.findFirst() .findFirst()
.orElse( new StitchingOfflineItem() ); .orElse( new StitchingOfflineItem() );
} }
public List<StitchingOfflineItem> findByJobCardIdAndDate(long jobCardId, String startDate, String endDate){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue( "job_card_id", jobCardId );
params.addValue( "start_date", startDate );
params.addValue( "end_date", endDate );
return namedParameterJdbcTemplate.query( SELECT_BY_JOB_CARD_AND_DATE , params, new StitchingOfflineItemRowMapper() );
}
} }

View File

@ -0,0 +1,147 @@
package com.utopiaindustries.model.ctp;
public class POsDetails {
private String poNumber;
private String articleTitle;
private long poQuantity;
private long totalCutting;
private long remainingCutting;
private long totalStitching;
private long remainingStitching;
private long totalEndLineQC;
private long remainingEndLineQC;
private long totalFinishing;
private long remainingFinishing;
private long totalAGradeItem;
private long totalBGradeItem;
private long totalCGradeItem;
public long getPoQuantity() {
return poQuantity;
}
public void setPoQuantity(long poQuantity) {
this.poQuantity = poQuantity;
}
public String getPoNumber() {
return poNumber;
}
public void setPoNumber(String poNumber) {
this.poNumber = poNumber;
}
public String getArticleTitle() {
return articleTitle;
}
public void setArticleTitle(String articleTitle) {
this.articleTitle = articleTitle;
}
public long getTotalCutting() {
return totalCutting;
}
public void setTotalCutting(long totalCutting) {
this.totalCutting = totalCutting;
}
public long getRemainingCutting() {
return remainingCutting;
}
public void setRemainingCutting(long remainingCutting) {
this.remainingCutting = remainingCutting;
}
public long getTotalStitching() {
return totalStitching;
}
public void setTotalStitching(long totalStitching) {
this.totalStitching = totalStitching;
}
public long getRemainingStitching() {
return remainingStitching;
}
public void setRemainingStitching(long remainingStitching) {
this.remainingStitching = remainingStitching;
}
public long getTotalEndLineQC() {
return totalEndLineQC;
}
public void setTotalEndLineQC(long totalEndLineQC) {
this.totalEndLineQC = totalEndLineQC;
}
public long getRemainingEndLineQC() {
return remainingEndLineQC;
}
public void setRemainingEndLineQC(long remainingEndLineQC) {
this.remainingEndLineQC = remainingEndLineQC;
}
public long getTotalFinishing() {
return totalFinishing;
}
public void setTotalFinishing(long totalFinishing) {
this.totalFinishing = totalFinishing;
}
public long getRemainingFinishing() {
return remainingFinishing;
}
public void setRemainingFinishing(long remainingFinishing) {
this.remainingFinishing = remainingFinishing;
}
public long getTotalAGradeItem() {
return totalAGradeItem;
}
public void setTotalAGradeItem(long totalAGradeItem) {
this.totalAGradeItem = totalAGradeItem;
}
public long getTotalBGradeItem() {
return totalBGradeItem;
}
public void setTotalBGradeItem(long totalBGradeItem) {
this.totalBGradeItem = totalBGradeItem;
}
public long getTotalCGradeItem() {
return totalCGradeItem;
}
public void setTotalCGradeItem(long totalCGradeItem) {
this.totalCGradeItem = totalCGradeItem;
}
@Override
public String toString() {
return "POsDetails{" +
"totalCutting=" + totalCutting +
", remainingCutting=" + remainingCutting +
", totalStitching=" + totalStitching +
", remainingStitching=" + remainingStitching +
", totalEndLineQC=" + totalEndLineQC +
", remainingEndLineQC=" + remainingEndLineQC +
", totalFinishing=" + totalFinishing +
", remainingFinishing=" + remainingFinishing +
", totalAGradeItem=" + totalAGradeItem +
", totalBGradeItem=" + totalBGradeItem +
", totalCGradeItem=" + totalCGradeItem +
'}';
}
}

View File

@ -46,12 +46,13 @@ public class ReportingService {
this.inventoryAccountDAO = inventoryAccountDAO; this.inventoryAccountDAO = inventoryAccountDAO;
} }
public Map<String,Integer> getJobCardProgress(String jobCardID){ public Map<String, Integer> getJobCardProgress(String jobCardID) {
if (jobCardID == null){ if (jobCardID == null) {
return new HashMap<>(); return new LinkedHashMap<>();
}else { } else {
HashMap<String,Integer> totalProgress = new HashMap<>(); Map<String, Integer> totalProgress = new HashMap<>();
List<JobCardItem> jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID)); List<JobCardItem> jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID));
BigDecimal totalProduction = jobCardItems.stream() BigDecimal totalProduction = jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO)) .map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
@ -64,64 +65,87 @@ public class ReportingService {
.map(item -> Optional.ofNullable(item.getExpectedProduction()).orElse(BigDecimal.ZERO)) .map(item -> Optional.ofNullable(item.getExpectedProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
if (actualProduction.compareTo(BigDecimal.ZERO) == 0 ) { // Cutting Progress Calculation
totalProgress.put("Cutting Progress", BigDecimal.ZERO.intValue()); if (actualProduction.compareTo(BigDecimal.ZERO) == 0) {
totalProgress.put("Cutting Progress", 0);
} else if (actualProduction.compareTo(expectedProduction) < 0) { } else if (actualProduction.compareTo(expectedProduction) < 0) {
BigDecimal cuttingProgress = expectedProduction BigDecimal cuttingProgress = actualProduction
.divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .divide(expectedProduction, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
totalProgress.put("Cutting Progress", cuttingProgress.intValue()); totalProgress.put("Cutting Progress", cuttingProgress.intValue());
}else if (actualProduction.compareTo(expectedProduction) > 0 || actualProduction.compareTo(expectedProduction) == 0){ } else {
totalProgress.put("Cutting Progress", 100); totalProgress.put("Cutting Progress", 100);
} }
if (totalProduction.compareTo(BigDecimal.ZERO) == 0){ // Stitching Progress Calculation
totalProgress.put("Stitching Progress", BigDecimal.ZERO.intValue()); if (totalProduction.compareTo(BigDecimal.ZERO) == 0) {
}else { totalProgress.put("Stitching Progress", 0);
} else {
BigDecimal stitchingProgress = totalProduction BigDecimal stitchingProgress = totalProduction
.divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .divide(actualProduction, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
totalProgress.put("Stitching Progress", stitchingProgress.intValue()); totalProgress.put("Stitching Progress", stitchingProgress.intValue());
} }
// QA Progress Calculation
Long qaProgressItems = stitchingOfflineItemDAO.CalculateTotalQA(Long.parseLong(jobCardID)); Long qaProgressItems = stitchingOfflineItemDAO.CalculateTotalQA(Long.parseLong(jobCardID));
if (qaProgressItems == 0){ if (qaProgressItems == 0) {
totalProgress.put("QA Progress", 0); totalProgress.put("QA Progress", 0);
}else { } else {
BigDecimal qaProgress = BigDecimal.valueOf(qaProgressItems) BigDecimal qaProgress = BigDecimal.valueOf(qaProgressItems)
.divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .divide(actualProduction, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
totalProgress.put("QA Progress", qaProgress.intValue()); totalProgress.put("QA Progress", qaProgress.intValue());
} }
// Finishing Progress Calculation
Long totalFinishItem = finishedItemDAO.calculateTotalFinishItem(Long.parseLong(jobCardID)); Long totalFinishItem = finishedItemDAO.calculateTotalFinishItem(Long.parseLong(jobCardID));
if (totalFinishItem == 0){ if (totalFinishItem == 0) {
totalProgress.put("Finishing Progress", BigDecimal.ZERO.intValue()); totalProgress.put("Finishing Progress", 0);
}else { } else {
BigDecimal finishItemProgress = BigDecimal.valueOf(totalFinishItem) BigDecimal finishItemProgress = BigDecimal.valueOf(totalFinishItem)
.divide(actualProduction, 4, BigDecimal.ROUND_HALF_UP) .divide(actualProduction, 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
totalProgress.put("Finishing Progress", finishItemProgress.intValue()); totalProgress.put("Finishing Progress", finishItemProgress.intValue());
} }
if (totalProduction.compareTo(BigDecimal.ZERO) == 0 && actualProduction.compareTo(BigDecimal.ZERO) == 0 ) { // Job Card Progress Calculation
totalProgress.put("Job Card Progress", BigDecimal.ZERO.intValue()); if (totalProduction.compareTo(BigDecimal.ZERO) == 0 && actualProduction.compareTo(BigDecimal.ZERO) == 0) {
totalProgress.put("Job Card Progress", 0);
} else { } else {
if (actualProduction.compareTo(expectedProduction)>0){ BigDecimal progressPercentage;
BigDecimal progressPercentage = actualProduction.add(totalProduction).add(BigDecimal.valueOf(qaProgressItems)).add(BigDecimal.valueOf(totalFinishItem)) if (actualProduction.compareTo(expectedProduction) > 0) {
progressPercentage = actualProduction.add(totalProduction)
.add(BigDecimal.valueOf(qaProgressItems))
.add(BigDecimal.valueOf(totalFinishItem))
.divide(actualProduction.multiply(BigDecimal.valueOf(4)), 4, RoundingMode.HALF_UP) .divide(actualProduction.multiply(BigDecimal.valueOf(4)), 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
totalProgress.put("Job Card Progress", progressPercentage.intValue()); } else {
}else { progressPercentage = actualProduction.add(totalProduction)
BigDecimal progressPercentage = actualProduction.add(totalProduction).add(BigDecimal.valueOf(qaProgressItems)).add(BigDecimal.valueOf(totalFinishItem)) .add(BigDecimal.valueOf(qaProgressItems))
.add(BigDecimal.valueOf(totalFinishItem))
.divide(expectedProduction.multiply(BigDecimal.valueOf(4)), 4, RoundingMode.HALF_UP) .divide(expectedProduction.multiply(BigDecimal.valueOf(4)), 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100)); .multiply(BigDecimal.valueOf(100));
totalProgress.put("Job Card Progress", progressPercentage.intValue());
} }
totalProgress.put("Job Card Progress", progressPercentage.intValue());
} }
return totalProgress;
// Sorting Map based on Custom Order
Map<String, Integer> indexMap = new HashMap<>();
indexMap.put("Cutting Progress", 1);
indexMap.put("Stitching Progress", 2);
indexMap.put("QA Progress", 3);
indexMap.put("Finishing Progress", 4);
indexMap.put("Job Card Progress", 5);
return totalProgress.entrySet()
.stream()
.sorted(Comparator.comparingInt(entry -> indexMap.get(entry.getKey())))
.collect(LinkedHashMap::new, (m, v) -> m.put(v.getKey(), v.getValue()), Map::putAll);
} }
} }
public Integer getTotalProduction(String jobCardID){ public Integer getTotalProduction(String jobCardID){
if (jobCardID == null){ if (jobCardID == null){
return 0; return 0;
@ -135,12 +159,13 @@ public class ReportingService {
} }
} }
public Map<String,Integer> getCompleteProduction(String jobCardID){ public Map<String, Integer> getCompleteProduction(String jobCardID) {
if (jobCardID == null){ if (jobCardID == null) {
return new HashMap<>(); return new LinkedHashMap<>();
}else { } else {
HashMap<String,Integer> totalProgress = new HashMap<>(); HashMap<String, Integer> totalProgress = new HashMap<>();
List<JobCardItem> jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID)); List<JobCardItem> jobCardItems = jobCardItemDAO.findByCardId(Long.parseLong(jobCardID));
BigDecimal totalProduction = jobCardItems.stream() BigDecimal totalProduction = jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO)) .map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
@ -149,22 +174,37 @@ public class ReportingService {
.map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO)) .map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add); .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 qaProgressItems = stitchingOfflineItemDAO.CalculateTotalQA(Long.parseLong(jobCardID));
Long totalFinishItem = finishedItemDAO.calculateTotalFinishItem(Long.parseLong(jobCardID)); Long totalFinishItem = finishedItemDAO.calculateTotalFinishItem(Long.parseLong(jobCardID));
// Get Job card Complete Items
Map<String, Integer> jobCardCompleteItems = getSegregateItems(jobCardID);
Integer segregateTotalItems = jobCardCompleteItems.values().stream().mapToInt(Integer::intValue).sum();
// Add values to totalProgress map
totalProgress.put("Cutting Progress", actualProduction.intValue()); totalProgress.put("Cutting Progress", actualProduction.intValue());
totalProgress.put("Stitching Progress", totalProduction.intValue()); totalProgress.put("Stitching Progress", totalProduction.intValue());
totalProgress.put("QA Progress", qaProgressItems.intValue()); totalProgress.put("QA Progress", qaProgressItems.intValue());
totalProgress.put("Finishing Progress", totalFinishItem.intValue()); totalProgress.put("Finishing Progress", totalFinishItem.intValue());
totalProgress.put("Job Card Progress", segregateTotalItems);
return totalProgress; //custom order
Map<String, Integer> indexMap = new HashMap<>();
indexMap.put("Job Card Progress", 1);
indexMap.put("Cutting Progress", 2);
indexMap.put("Stitching Progress", 3);
indexMap.put("Finishing Progress", 4);
indexMap.put("QA Progress", 5);
// Sort the keys based on custom order
return totalProgress.entrySet()
.stream()
.sorted(Comparator.comparingInt(entry -> indexMap.get(entry.getKey())))
.collect(LinkedHashMap::new, (m, v) -> m.put(v.getKey(), v.getValue()), Map::putAll);
} }
} }
public Map<String,Integer> getSegregateItems(String jobCardID){ public Map<String,Integer> getSegregateItems(String jobCardID){
if (jobCardID == null){ if (jobCardID == null){
return new HashMap<>(); return new HashMap<>();
@ -175,6 +215,9 @@ public class ReportingService {
List<Long> finishItemsIds = finishedItems.stream() List<Long> finishItemsIds = finishedItems.stream()
.map(FinishedItem::getId).collect(Collectors.toList()); .map(FinishedItem::getId).collect(Collectors.toList());
if (finishItemsIds.isEmpty()){ if (finishItemsIds.isEmpty()){
gradingItems.put("A GRADE",0);
gradingItems.put("B GRADE",0);
gradingItems.put("C GRADE",0);
return gradingItems; return gradingItems;
}else { }else {
for (InventoryAccount inventoryAccount : inventoryAccounts){ for (InventoryAccount inventoryAccount : inventoryAccounts){
@ -399,7 +442,139 @@ public class ReportingService {
return barChartData; return barChartData;
} }
public List<POsDetails> getAllPOs() {
List<POsDetails> pOsDetailsList = new ArrayList<>();
List<JobCard> jobCards = jobCardDAO.findAll() ;
HashMap<String, List<JobCard>> filterJobCardsByPos = jobCards.stream()
.collect(Collectors.groupingBy(
JobCard::getPurchaseOrderId,
HashMap::new,
Collectors.toList()
));
Map<String,Integer> jobCardCompleteItems = new HashMap<>();
for (String pos : filterJobCardsByPos.keySet()) {
BigDecimal totalProduction = BigDecimal.ZERO;
BigDecimal actualProduction = BigDecimal.ZERO;
Long qaProgressItems = 0L;
Long totalFinishItem = 0L;
POsDetails pOsDetails = new POsDetails();
for (JobCard jobCard : filterJobCardsByPos.get(pos)) {
List<JobCardItem> jobCardItems = jobCardItemDAO.findByCardId(jobCard.getId());
totalProduction = totalProduction.add(jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getTotalProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add));
actualProduction = actualProduction.add(jobCardItems.stream()
.map(item -> Optional.ofNullable(item.getActualProduction()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add));
qaProgressItems += Optional.ofNullable(stitchingOfflineItemDAO.CalculateTotalQA(jobCard.getId())).orElse(0L);
totalFinishItem += Optional.ofNullable(finishedItemDAO.calculateTotalFinishItem(jobCard.getId())).orElse(0L);
jobCardCompleteItems = getSegregateItems(String.valueOf(jobCard.getId()));
if (jobCardCompleteItems == null) {
jobCardCompleteItems = new HashMap<>();
}
}
pOsDetails.setPoNumber(pos);
pOsDetails.setPoQuantity(100);
pOsDetails.setTotalCutting(actualProduction.intValue());
pOsDetails.setTotalStitching(totalProduction.intValue());
pOsDetails.setTotalEndLineQC(qaProgressItems.intValue());
pOsDetails.setTotalFinishing(totalFinishItem);
pOsDetails.setRemainingCutting(100 - actualProduction.intValue());
pOsDetails.setRemainingStitching(100 - totalProduction.intValue());
pOsDetails.setRemainingEndLineQC(100 - qaProgressItems);
pOsDetails.setRemainingFinishing(100 - totalFinishItem);
pOsDetails.setTotalAGradeItem(jobCardCompleteItems.getOrDefault("A GRADE", 0));
pOsDetails.setTotalBGradeItem(jobCardCompleteItems.getOrDefault("B GRADE", 0));
pOsDetails.setTotalCGradeItem(jobCardCompleteItems.getOrDefault("C GRADE", 0));
pOsDetailsList.add(pOsDetails);
}
return pOsDetailsList;
}
public HashMap<String, Map<String, Integer>> getAllPoJobCards(String PONumber, String selectDate) {
String startDate = selectDate != null && !selectDate.isEmpty() ? selectDate + " 00:00:01": null;
String endDate = selectDate != null && !selectDate.isEmpty() ? selectDate + " 23:59:59": null;
HashMap<String, Map<String, Integer>> poJobCardItemsProgress = new HashMap<>();
List<JobCard> jobCards = jobCardDAO.findAll();
// Filter JobCards by Purchase Order ID
List<JobCard> filterJobCardsByPos = jobCards.stream()
.filter(e -> e.getPurchaseOrderId().equals(PONumber))
.collect(Collectors.toList());
List<InventoryAccount> inventoryAccounts = inventoryAccountDAO.getPackagingAccounts();
List<Integer> gradingAccounts = inventoryAccounts.stream().map(e-> (int)(e.getId())).collect(Collectors.toList());
for (JobCard jobCard : filterJobCardsByPos) {
List<Bundle> bundles = bundleDAO.findByCardIdAndDATE(jobCard.getId(),startDate,endDate);
List<StitchingOfflineItem> stitchingOfflineItems = stitchingOfflineItemDAO.findByJobCardIdAndDate(jobCard.getId(),startDate,endDate);
List<FinishedItem> finishedItems = finishedItemDAO.calculateTotalFinishItem(jobCard.getId(),startDate,endDate);
List<InventoryTransactionLeg> inventoryTransactionLegs = inventoryTransactionLegDAO.getTransactionByJobCardAndDatesAndTypeAndAccountID(jobCard.getId(),startDate,endDate,"IN", gradingAccounts);
//cutting days wise
BigDecimal cutting = bundles.stream()
.map(bundle -> Optional.ofNullable(bundle.getWrapQuantity()).orElse(BigDecimal.ZERO))
.reduce(BigDecimal.ZERO, BigDecimal::add);
//stitching day wise
Integer stitching = stitchingOfflineItems.size();
Integer qa = finishedItems.size();
Map<String, Integer> segregateItems = inventoryTransactionLegs.stream()
.filter(leg -> inventoryAccounts.stream()
.anyMatch(account -> (int) account.getId() == (leg.getAccountId())))
.collect(Collectors.toMap(
leg -> inventoryAccounts.stream()
.filter(account -> (int) account.getId() == (leg.getAccountId()))
.findFirst()
.map(InventoryAccount::getTitle)
.orElse("Unknown"),
leg -> leg.getQuantity().intValue(),
Integer::sum,
HashMap::new
));
Map<String, Integer> items = getCompleteProduction(String.valueOf(jobCard.getId()));
items.put("Cutting Progress",cutting.intValue());
items.put("Stitching Progress",stitching);
items.put("QA Progress",qa);
items.put("A Grade",segregateItems.get("A GRADE") != null ? segregateItems.get("A GRADE") : 0);
items.put("B Grade",segregateItems.get("B GRADE") != null ? segregateItems.get("B GRADE") : 0);
items.put("C Grade",segregateItems.get("C GRADE") != null ? segregateItems.get("C GRADE") : 0);
// Define sorting order
Map<String, Integer> indexMap = new HashMap<>();
indexMap.put("Job Card Progress", 1);
indexMap.put("Cutting Progress", 2);
indexMap.put("Stitching Progress", 3);
indexMap.put("QA Progress", 4);
indexMap.put("Finishing Progress", 5);
indexMap.put("A GRADE", 6);
indexMap.put("B GRADE", 7);
indexMap.put("C GRADE", 8);
// Sort items based on indexMap order
Map<String, Integer> sortedItems = items.entrySet()
.stream()
.sorted(Comparator.comparingInt(entry -> indexMap.getOrDefault(entry.getKey(), Integer.MAX_VALUE)))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldValue, newValue) -> oldValue,
LinkedHashMap::new
));
// Add sorted items to the result map
poJobCardItemsProgress.put(jobCard.getCode(), sortedItems);
}
return poJobCardItemsProgress;
}
private StringBuilder generateTime(LocalDateTime startDate, LocalDateTime endDate){ private StringBuilder generateTime(LocalDateTime startDate, LocalDateTime endDate){
StringBuilder totalTime = new StringBuilder(); StringBuilder totalTime = new StringBuilder();

View File

@ -73,8 +73,6 @@ document.addEventListener("DOMContentLoaded", function () {
if (!document.getElementById(divId)) { if (!document.getElementById(divId)) {
return; return;
} }
console.log(maxValue);
Highcharts.chart(divId, { Highcharts.chart(divId, {
chart: { chart: {
type: 'column', type: 'column',
@ -153,6 +151,7 @@ document.addEventListener("DOMContentLoaded", function () {
const total = div.getAttribute('data-totalProduction'); const total = div.getAttribute('data-totalProduction');
const actual = div.getAttribute('data-actualProduction'); const actual = div.getAttribute('data-actualProduction');
const divId = div.id; const divId = div.id;
console.log(actual)
createGaugeChart(parseInt(progress), 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'); const gaugeDivs = document.querySelectorAll('.gauge-chart');
@ -191,7 +190,6 @@ document.addEventListener("DOMContentLoaded", function () {
const dates = div.getAttribute('data-dates'); const dates = div.getAttribute('data-dates');
const datesArray = dates.split(','); const datesArray = dates.split(',');
const divId = div.id; const divId = div.id;
console.log(maxValue, typeof maxValue)
createBarChart( divId, height, width, title,aHeading,aData,bHeading,bData,cHeading,cData,dHeading,dData,datesArray,fontSize,maxValue); createBarChart( divId, height, width, title,aHeading,aData,bHeading,bData,cHeading,cData,dHeading,dData,datesArray,fontSize,maxValue);
}); });
} }

View File

@ -132,6 +132,10 @@
<nav class="navbar navbar-light bg-light navbar-expand-lg justify-content-between" <nav class="navbar navbar-light bg-light navbar-expand-lg justify-content-between"
th:if="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting')}"> th:if="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting')}">
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item"
th:classappend="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting/po-report') ? 'active' : ''}">
<a th:href="@{/reporting/po-report}" class="nav-link">PO Report</a>
</li>
<li class="nav-item" <li class="nav-item"
th:classappend="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting/job-card-report') ? 'active' : ''}"> th:classappend="${#strings.startsWith(#httpServletRequest.getRequestURI(), '/ctp/reporting/job-card-report') ? 'active' : ''}">
<a th:href="@{/reporting/job-card-report}" class="nav-link">Job Card Report</a> <a th:href="@{/reporting/job-card-report}" class="nav-link">Job Card Report</a>

View File

@ -26,7 +26,7 @@
th:data-width="350" th:data-width="350"
th:data-height="350" th:data-height="350"
th:data-totalProduction="${totalProduction}" th:data-totalProduction="${totalProduction}"
th:data-actualProduction="${jobCardProgress.get('actualProduction')}" th:data-actualProduction="${completeProduction.get('Job Card Progress')}"
th:data-fontSize="30" th:data-fontSize="30"
th:data-fontColor="'#17202a'" th:data-fontColor="'#17202a'"
th:data-color="'#566573'"></div> th:data-color="'#566573'"></div>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml">
<head th:replace="_fragments :: head('Home Page')"></head>
<body>
<!-- sidebar starts -->
<aside class="col-sm-2" th:fragment="sidebar">
<div class="page-filters-sidebar">
<form th:action="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}">
<h5 class="mb-4">Refine Your Search</h5>
<div class="form-group">
<label>Select Date</label>
<input type="date" class="form-control" name="select-date" th:value="${param['select-date'] ?: selectDate}">
</div>
<input type="submit" class="btn btn-secondary btn-block" value="Search">
<a th:href="@{${#strings.replace(#httpServletRequest.requestURI, #request.getContextPath(), '')}}"
class="btn btn-secondary btn-block">Reset</a>
</form>
<div th:replace="_fragments :: page-footer-scripts"></div>
</div>
</aside>
</body>
</html>

View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:uind="http://www.w3.org/1999/xhtml"
xmlns:ctp="http://www.w3.org/1999/xhtml">
<head th:replace="_fragments :: head('Job Card Detail')"></head>
<body>
<div class="container-fluid">
<header class="row page-header" th:replace="_fragments :: page-header"></header>
<main class="row page-main">
<aside class="col-sm-2" th:replace="/reporting/po-job-card-report-sidebar :: sidebar"></aside>
<div class="col-sm">
<table class="table">
<tbody>
<tr th:if="${allJobCard != null}" th:each="jobCard : ${allJobCard.keySet()}" style="padding-bottom:10px">
<td class="m-0 pb-3">
<table class="table mb-0 table-bordered text-center">
<thead>
<tr>
<th colspan="10" class="text-center bg-dark text-white">
<span th:text="${jobCard}"></span>
</th>
</tr>
<tr>
<th th:each="headings : ${allJobCard.get(jobCard).keySet()}" th:text="${headings}"></th>
</tr>
</thead>
<tbody>
<tr>
<td >
<span style="font-size: 14px; font-weight: bold;" th:text="'Total : ' + ${allJobCard.get(jobCard).get('Cutting Progress')}"></span>
<br>
<span style="font-size: 14px; font-weight: bold;" th:text="'Complete Products : ' + ${allJobCard.get(jobCard).get('Job Card Progress')}"></span>
</td>
<td style="font-size: 14px;" th:if="${values != 'Job Card Progress'}" th:each="values : ${allJobCard.get(jobCard).keySet()}" th:text="${allJobCard.get(jobCard).get(values)}"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
<div th:replace="_fragments :: page-footer-scripts"></div>
</body>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"
xmlns:ctp="http://www.w3.org/1999/xhtml">
<head th:replace="_fragments :: head('Summary')"></head>
<body>
<div class="container-fluid">
<header class="row page-header" th:replace="_fragments :: page-header"></header>
<main class="row page-main">
<div class="col-sm">
<h3>PO's Report</h3>
<div th:replace="_notices :: page-notices"></div>
<div th:replace="_fragments :: table-loading-skeleton"></div>
<table class="table table-striped font-sm" data-table data-order="[[ 0, &quot;asc&quot; ]]">
<thead>
<tr>
<th>PO Number</th>
<th>PO Article</th>
<th>PO Quantity</th>
<th>Total Cutting</th>
<th>Remaining Cutting</th>
<th>Total Stitching</th>
<th>Remaining Stitching</th>
<th>Total End QC</th>
<th>Remaining QC</th>
<th>Total Finishing Items</th>
<th>Remaining Finishing Items</th>
<th>A Grade Items</th>
<th>B Grade Items</th>
<th>C Grade Items</th>
</tr>
</thead>
<tbody>
<!-- Dummy data for testing purposes -->
<tr th:each="poDetail : ${allPOs}">
<td><a class="text-reset" th:href="@{'/reporting/po-report-view/' + ${poDetail.poNumber}}" th:text="${poDetail.poNumber}"></a></td>
<td th:text="${poDetail.articleTitle}"></td>
<td th:text="${poDetail.poQuantity}"></td>
<td th:text="${poDetail.totalCutting}"></td>
<td th:text="${poDetail.remainingCutting}"></td>
<td th:text="${poDetail.totalStitching}"></td>
<td th:text="${poDetail.remainingStitching}"></td>
<td th:text="${poDetail.totalEndLineQC}"></td>
<td th:text="${poDetail.remainingEndLineQC}"></td>
<td th:text="${poDetail.totalFinishing}"></td>
<td th:text="${poDetail.remainingFinishing}"></td>
<td th:text="${poDetail.totalAGradeItem}"></td>
<td th:text="${poDetail.totalBGradeItem}"></td>
<td th:text="${poDetail.totalCGradeItem}"></td>
</tr>
</tbody>
</table>
<!-- <h4 th:if="${#lists.size(cards) == 0 }">No cards found.</h4>-->
</div>
</main>
</div>
<div th:replace="_fragments :: page-footer-scripts"></div>
<script th:src="@{/js/summary.js}"></script>
</body>
</html>