barchart and cutting details and stitching detail

add-reporting-dashboard
Usama Khan 2025-04-10 10:51:45 -07:00
parent 6da953ad69
commit 2cd71d3828
8 changed files with 186 additions and 175 deletions

View File

@ -54,6 +54,7 @@ public class ReportingController {
model.addAttribute("jobCardProgress", reportingService.getJobCardProgress(jobCardId));
model.addAttribute("cuttingDetails", reportingService.getCuttingDetails(jobCardId));
model.addAttribute("stitchingDetails", reportingService.getStitchingDetails(jobCardId));
model.addAttribute("dailyProgress", reportingService.getPhasesProgressDayWise(jobCardId));
return "/reporting/job-card-report";
}

View File

@ -48,6 +48,7 @@ public class InventoryTransactionLegDAO {
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);
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);
public InventoryTransactionLegDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
@ -189,4 +190,13 @@ public class InventoryTransactionLegDAO {
params.addValue("parent_document_type", parentType );
return namedParameterJdbcTemplate.query( SELECT_GROUP_By_TRANSACTION_PARENT_TYPE_PARENT_ID , params, new InventoryTransactionLegRowMapper() );
}
public List<InventoryTransactionLeg> getTransactionByJobCardAndDatesAndType(long jobCardID, String startDate, String endDate, String type){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("job_card_id", jobCardID );
params.addValue("start_date", startDate );
params.addValue("end_date", endDate );
params.addValue("type", type );
return namedParameterJdbcTemplate.query( SELECT_JOB_CARD_DATES , params, new InventoryTransactionLegRowMapper() );
}
}

View File

@ -13,7 +13,7 @@ public class SummaryInventoryReportDao {
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final String TABLE_NAME = "cut_to_pack.inventory_transaction_leg ";
String SELECT_QUERY = "SELECT item_id, account_id, parent_document_id, DATE(transaction_leg_datetime) AS transaction_date, "
String SELECT_QUERY = "SELECT job_card_id, item_id, account_id, parent_document_id, DATE(transaction_leg_datetime) AS transaction_date, "
+ "sku, parent_document_type, parent_document_piece_type, "
+ "SUM(CASE WHEN type = 'IN' THEN 1 ELSE 0 END) AS total_in, "
+ "SUM(CASE WHEN type = 'OUT' THEN 1 ELSE 0 END) AS total_out "

View File

@ -15,6 +15,7 @@ public class SummaryInventoryReportRowMapper implements RowMapper<SummaryInvento
summaryInventoryReport.setAccountId(rs.getString("account_id"));
summaryInventoryReport.setDate(rs.getString("transaction_date"));
summaryInventoryReport.setTotalIn(rs.getLong("total_in"));
summaryInventoryReport.setJobCardID(rs.getLong("job_card_id"));
summaryInventoryReport.setTotalOut(rs.getLong("total_out"));
summaryInventoryReport.setParentDocumentType(rs.getString("parent_document_type"));
summaryInventoryReport.setParentDocumentPieceType(rs.getString("parent_document_piece_type"));

View File

@ -7,6 +7,7 @@ public class SummaryInventoryReport {
private String Date;
private long totalIn;
private long totalOut;
private long jobCardID;
private String parentDocumentType;
private String parentDocumentId;
private String accountId;
@ -91,4 +92,12 @@ public class SummaryInventoryReport {
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public long getJobCardID() {
return jobCardID;
}
public void setJobCardID(long jobCardID) {
this.jobCardID = jobCardID;
}
}

View File

@ -3,6 +3,8 @@ 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;
@ -10,6 +12,7 @@ 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;
@ -207,7 +210,6 @@ public class ReportingService {
}
return phasesTimes;
}
}
@ -320,6 +322,85 @@ public class ReportingService {
return stitchingDetails;
}
public Map<String, List<?>> getPhasesProgressDayWise(String jobCardID) {
if (jobCardID == null) {
return Collections.emptyMap();
}
List<JobCardItem> 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<String, List<?>> barChartData = new HashMap<>();
List<InventoryTransactionLeg> inventoryTransactionLegs = inventoryTransactionLegDAO
.getTransactionByJobCardAndDatesAndType(Long.parseLong(jobCardID), startDate.toString(), LocalDateTime.now().toString(), "IN");
//remove quality multiple transaction entry approved/rejects
List<InventoryTransactionLeg> 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<String> uniqueDates = inventoryTransactionLegs.stream()
.map(leg -> leg.getTransactionLegDateTime().toLocalDate().format(formatter))
.distinct()
.sorted()
.collect(Collectors.toList());
Map<String, Integer> dateIndexMap = new HashMap<>();
for (int i = 0; i < uniqueDates.size(); i++) {
dateIndexMap.put(uniqueDates.get(i), i);
}
List<Integer> cuttingList = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0));
List<Integer> stitchingList = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0));
List<Integer> qualityList = new ArrayList<>(Collections.nCopies(uniqueDates.size(), 0));
List<Integer> 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){

View File

@ -69,12 +69,13 @@ document.addEventListener("DOMContentLoaded", function () {
}
});
}
function createBarChart(config, height, width, title) {
if (!document.getElementById(config.containerId)) {
function createBarChart(divId, height, width, title, aHeading, aData, bHeading, bData, cHeading, cData, dHeading, dData, dates, fontSize, maxValue) {
if (!document.getElementById(divId)) {
return;
}
console.log(maxValue);
Highcharts.chart(config.containerId, {
Highcharts.chart(divId, {
chart: {
type: 'column',
height: height,
@ -82,44 +83,58 @@ document.addEventListener("DOMContentLoaded", function () {
},
title: {
text: title,
align: 'center', // Optionally align title in center
align: 'center',
verticalAlign: 'top',
y: 30 // Offset from top
y: 30,
style: {
fontSize: fontSize,
fontWeight: 'bold',
}
},
xAxis: {
categories: [
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
"Next Monday", "Next Tuesday", "Next Wednesday", "Next Friday", "Next Saturday", "Next Sunday"
],
categories: dates,
labels: {
rotation: -45 // Optional: Rotate labels if needed
rotation: -45,
style: {
fontSize: 10-fontSize,
fontWeight: 'bold'
}
}
},
yAxis: {
min: 0,
max: 100,
max: maxValue,
softMax: maxValue,
softMin: 0,
startOnTick: true,
endOnTick: true,
title: {
text: 'Total Progress'
text: 'Total Progress',
style: {
fontSize: fontSize,
fontWeight: 'bold',
}
},
labels: {
format: '{value}%'
}
},
scrollbar: {
enabled: true // Ensure Highcharts scrollbar is enabled
enabled: true
},
series: [{
name: 'Model A',
data: [100, 75, 78, 79, 77, 81, 80, 76, 79, 80, 76, 79, 80, 76, 79, 80, 80]
name: aHeading,
data: aData
}, {
name: 'Model B',
data: [85, 78, 82, 80, 79, 84, 83, 78, 82, 83, 76, 79, 80, 76, 79, 80, 85]
name: bHeading,
data: bData
}, {
name: 'Model C',
data: [88, 80, 85, 82, 81, 87, 85, 80, 85, 86, 76, 79, 80, 76, 79, 80, 88]
name: cHeading,
data: cData
}, {
name: 'Model D',
data: [90, 83, 87, 84, 83, 89, 88, 83, 87, 88, 76, 79, 80, 76, 79, 80, 90]
name: dHeading,
data: dData
}]
});
}
@ -127,19 +142,19 @@ document.addEventListener("DOMContentLoaded", function () {
initializeGauges();
function initializeGauges() {
const gaugeDivs2 = document.querySelectorAll('.gauge-chart2');
gaugeDivs2.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 divId = div.id;
createGaugeChart(parseInt(progress), color, divId, title, height, width, fontSize, -20, 4, fontColor, total, actual);
});
gaugeDivs2.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 divId = div.id;
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 progress = div.getAttribute('data-progress');
@ -157,26 +172,27 @@ document.addEventListener("DOMContentLoaded", function () {
const divId = div.id;
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 title = div.getAttribute('data-title');
const height = div.getAttribute('data-height');
const width = div.getAttribute('data-width');
const fontSize = div.getAttribute('data-fontSize');
const maxValue = Number(div.getAttribute('data-totalProduction'));
const aHeading = 'Cutting';
const aData = JSON.parse(div.getAttribute('data-cutting'));
const bHeading='Stitching';
const bData =JSON.parse(div.getAttribute('data-stitching'));
const cHeading='End Line Quality Checking';
const cData =JSON.parse(div.getAttribute('data-quality'));
const dHeading="Finish Items";
const dData =JSON.parse(div.getAttribute('data-finishing'));
const dates = div.getAttribute('data-dates');
const datesArray = dates.split(',');
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);
});
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,);
});
}
});

View File

@ -127,14 +127,21 @@
</table>
</td>
</tr>
<tr>
<tr th:if="${dailyProgress.get('dates') != null}">
<td style="padding-left: 150px;">
<div style="border: 2px solid #d5d8dc; padding-top: 10px; border-radius: 10px; height: 460px; width: 80%; overflow-x: auto;">
<div id="barChart" class="barChart" style="height: 400px; width: 1600px;"
<div style="border: 2px solid #d5d8dc; padding-top: 10px; border-radius: 10px; height: 560px; width: 80%; overflow-x: auto;">
<div id="barChart" class="barChart" style="height: 500px; width: 1600px;"
th:data-width="1600"
th:data-height="400"
th:data-height="500"
th:data-title="'Days Wise Progress'"
></div> <!-- 100% width for responsiveness -->
th:data-dates="${dailyProgress.get('dates')}"
th:data-cutting="${dailyProgress.get('cutting')}"
th:data-stitching="${dailyProgress.get('stitching')}"
th:data-quality="${dailyProgress.get('quality')}"
th:data-finishing="${dailyProgress.get('finishing')}"
th:data-totalProduction="${completeProduction.get('Cutting Progress')}"
th:data-fontSize="30"
></div>
</div>
@ -146,120 +153,6 @@
</main>
</div>
<script>
var gaugeConfigs = [
{
containerId: "jobCard",
width: 350,
height: 350,
minValue: 0,
maxValue: 100,
speed: 10,
innerRadius: "80%",
outerRadius: "20%",
color: "#566573",
title: "Job Card Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100,
CirclePosition:-20
},
{
containerId: "cutting",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#5dade2",
title: "Cutting Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},
{
containerId: "stitching",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#76d7c4",
title: "Stitching Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},
{
containerId: "quality",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#73c6b6",
title: "Quality Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},{
containerId: "finishing",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#58d68d",
title: "Finishing Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
completedPiece:100
},
{
containerId: "Packaging",
width: 200,
height: 200,
minValue: 0,
maxValue: 300,
speed: 250,
innerRadius: "60%",
outerRadius: "100%",
color: "#f5cba7",
title: "Finishing Report",
fontSize:10,
InnerTextYPosition:-20,
totalPiece:200,
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" }
]
};
</script>
<!-- Load JavaScript file -->
<script th:src="@{/js/charts.js}"></script>
</body>