add job card print

pull/13/head
Usama Khan 2025-03-04 04:03:41 -08:00
parent f70ddecfa6
commit 18c92a362f
11 changed files with 504 additions and 2 deletions

View File

@ -8,6 +8,9 @@ import com.utopiaindustries.service.InventoryAccountService;
import com.utopiaindustries.service.JobCardService; import com.utopiaindustries.service.JobCardService;
import com.utopiaindustries.service.LocationService; import com.utopiaindustries.service.LocationService;
import com.utopiaindustries.util.StringUtils; import com.utopiaindustries.util.StringUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.*; import org.springframework.web.bind.annotation.*;
@ -173,6 +176,11 @@ public class JobCardController {
return "job-card-view"; return "job-card-view";
} }
@GetMapping( value = "/pdf/{id}", produces = MediaType.APPLICATION_PDF_VALUE )
public ResponseEntity<InputStreamResource> showGinReceivingPDF(@PathVariable long id, Model model ) throws Exception {
return jobCardService.getJobCardReceivingPdf(id,model);
}
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<>();
while (start.isBefore(end)) { while (start.isBefore(end)) {

View File

@ -7,14 +7,21 @@ import com.utopiaindustries.dao.uind.PurchaseOrderDAO;
import com.utopiaindustries.model.ctp.*; import com.utopiaindustries.model.ctp.*;
import com.utopiaindustries.model.uind.Item; import com.utopiaindustries.model.uind.Item;
import com.utopiaindustries.querybuilder.ctp.JobCardQueryBuilder; import com.utopiaindustries.querybuilder.ctp.JobCardQueryBuilder;
import com.utopiaindustries.util.HTMLBuilder;
import com.utopiaindustries.util.PDFResponseEntityInputStreamResource;
import com.utopiaindustries.util.StringUtils; import com.utopiaindustries.util.StringUtils;
import com.utopiaindustries.util.URLUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -36,8 +43,10 @@ public class JobCardService {
private final FinishedItemDAO finishedItemDAO; private final FinishedItemDAO finishedItemDAO;
private final StitchingOfflineItemDAO stitchingOfflineItemDAO; private final StitchingOfflineItemDAO stitchingOfflineItemDAO;
private final SkuCutPiecesDAO skuCutPiecesDAO; private final SkuCutPiecesDAO skuCutPiecesDAO;
private final HTMLBuilder htmlBuilder;
private PDFResponseEntityInputStreamResource pdfGenerator;
public JobCardService(JobCardDAO jobCardDAO, CutPieceTypeDAO cutPieceTypeDAO, JobCardItemDAO jobCardItemDAO, CutPieceDAO cutPieceDAO, ItemDAO itemDAO, LocationSiteDAO locationSiteDAO, PurchaseOrderDAO purchaseOrderDAO, UserInventoryAccountDAO userInventoryAccountDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO, SkuCutPiecesDAO skuCutPiecesDAO) { public JobCardService(JobCardDAO jobCardDAO, CutPieceTypeDAO cutPieceTypeDAO, JobCardItemDAO jobCardItemDAO, CutPieceDAO cutPieceDAO, ItemDAO itemDAO, LocationSiteDAO locationSiteDAO, PurchaseOrderDAO purchaseOrderDAO, UserInventoryAccountDAO userInventoryAccountDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO, SkuCutPiecesDAO skuCutPiecesDAO, HTMLBuilder htmlBuilder, PDFResponseEntityInputStreamResource pdfGenerator) {
this.skuCutPiecesDAO = skuCutPiecesDAO; this.skuCutPiecesDAO = skuCutPiecesDAO;
this.jobCardDAO = jobCardDAO; this.jobCardDAO = jobCardDAO;
this.cutPieceTypeDAO = cutPieceTypeDAO; this.cutPieceTypeDAO = cutPieceTypeDAO;
@ -49,6 +58,8 @@ public class JobCardService {
this.userInventoryAccountDAO = userInventoryAccountDAO; this.userInventoryAccountDAO = userInventoryAccountDAO;
this.finishedItemDAO = finishedItemDAO; this.finishedItemDAO = finishedItemDAO;
this.stitchingOfflineItemDAO = stitchingOfflineItemDAO; this.stitchingOfflineItemDAO = stitchingOfflineItemDAO;
this.htmlBuilder = htmlBuilder;
this.pdfGenerator = pdfGenerator;
} }
/* /*
@ -300,4 +311,31 @@ public class JobCardService {
public Map<Long, Long> totalFinishItem(List<Long> itemIds, long jobCardId ){ public Map<Long, Long> totalFinishItem(List<Long> itemIds, long jobCardId ){
return finishedItemDAO.findTotalFinishedItems( itemIds, jobCardId ); return finishedItemDAO.findTotalFinishedItems( itemIds, jobCardId );
} }
/**
* Print Job card *
* **/
public ResponseEntity<InputStreamResource> getJobCardReceivingPdf(long id, Model model ) throws Exception {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd,yyyy h:m a");
List<JobCardItem> jobCardItems = this.findJobCardItemByJobCardId(id);
List<Long> jobCardItemIds = jobCardItems.stream()
.map(JobCardItem::getItemId)
.collect(Collectors.toList());
List<Long> itemIds = jobCardItems.stream()
.map(JobCardItem::getId)
.collect(Collectors.toList());
model.addAttribute( "baseUrl", URLUtils.getCurrentBaseUrl() );
model.addAttribute( "card", this.findByID(id));
model.addAttribute("jobCardItems", jobCardItems);
model.addAttribute("cutPiece",this.findCutPieceByJobCardItemIds(itemIds));
model.addAttribute("totalFinishItem",this.totalFinishItem(jobCardItemIds, id));
model.addAttribute("totalStitchingItem",this.totalStitchingItem(jobCardItemIds, id));
// html str
String htmlStr = htmlBuilder.buildHTML( "job-card-view-pdf", model );
// return pdf
return pdfGenerator.generatePdf( htmlStr, "Job-Card", "inline" );
}
} }

View File

@ -0,0 +1,16 @@
package com.utopiaindustries.util;
import com.itextpdf.html2pdf.attach.ITagWorker;
import com.itextpdf.html2pdf.attach.ProcessorContext;
import com.itextpdf.html2pdf.attach.impl.DefaultTagWorkerFactory;
import com.itextpdf.html2pdf.html.TagConstants;
import com.itextpdf.styledxmlparser.node.IElementNode;
public class CustomTagWorkerFactory extends DefaultTagWorkerFactory {
public ITagWorker getCustomTagWorker( IElementNode tag, ProcessorContext context) {
if ( TagConstants.HTML.equals(tag.name())) {
return new ZeroMarginHtmlTagWorker(tag, context);
}
return null;
}
}

View File

@ -0,0 +1,90 @@
package com.utopiaindustries.util;
import com.utopiaindustries.dialect.CTPDialect;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.w3c.tidy.Tidy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Component
public class HTMLBuilder {
private final String UTF_8 = StandardCharsets.UTF_8.displayName();
public String buildHTML( String templateName, Model model ) throws Exception {
// template resolver
ClassLoaderTemplateResolver pdfTemplateResolver = createPDFTemplateResolver();
// template engine
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver( pdfTemplateResolver );
templateEngine.addDialect( new CTPDialect() );
// context
Context ctx = configureContext( model );
// render the template
String renderedHTMLContent = templateEngine.process( templateName, ctx );
return convertToXhtml( renderedHTMLContent );
}
public String buildHTMLMap( String templateName, ModelMap model ) throws Exception {
// template resolver
ClassLoaderTemplateResolver pdfTemplateResolver = createPDFTemplateResolver();
// template engine
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver( pdfTemplateResolver );
templateEngine.addDialect( new CTPDialect() );
// context
Context ctx = configureContextMap( model );
// render the template
String renderedHTMLContent = templateEngine.process( templateName, ctx );
return convertToXhtml( renderedHTMLContent );
}
private String convertToXhtml( String html ) throws UnsupportedEncodingException {
Tidy tidy = new Tidy();
tidy.setInputEncoding( UTF_8 );
tidy.setOutputEncoding( UTF_8 );
tidy.setXHTML( true );
ByteArrayInputStream inputStream = new ByteArrayInputStream( html.getBytes( UTF_8 ) );
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
tidy.parseDOM( inputStream, outputStream );
return outputStream.toString( UTF_8 );
}
private ClassLoaderTemplateResolver createPDFTemplateResolver() {
ClassLoaderTemplateResolver pdfTemplateResolver = new ClassLoaderTemplateResolver();
pdfTemplateResolver.setPrefix( "/templates/" );
pdfTemplateResolver.setTemplateMode( TemplateMode.HTML );
pdfTemplateResolver.setSuffix( ".html" );
pdfTemplateResolver.setCharacterEncoding( UTF_8 );
pdfTemplateResolver.setOrder( 1 );
return pdfTemplateResolver;
}
private Context configureContext( Model model ) {
Context ctx = new Context();
// adding model attributes to context
for ( Map.Entry<String, Object> entry: model.asMap().entrySet() ) {
ctx.setVariable( entry.getKey(), entry.getValue() );
}
return ctx;
}
private Context configureContextMap( ModelMap model ) {
Context ctx = new Context();
// adding model attributes to context
for ( Map.Entry<String, Object> entry: model.entrySet() ) {
ctx.setVariable( entry.getKey(), entry.getValue() );
}
return ctx;
}
}

View File

@ -0,0 +1,116 @@
package com.utopiaindustries.util;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@Component
public class PDFResponseEntityInputStreamResource {
/**
* prepare pdf document from html string
*/
private ResponseEntity<InputStreamResource> createPDFResponseEntityInputStreamResource( String htmlStr, String filename, String method ) throws IOException {
// output stream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// converter properties
ConverterProperties properties = new ConverterProperties();
properties.setFontProvider( new DefaultFontProvider( false, false, true ) );
// pdf document
PdfWriter writer = new PdfWriter( outputStream );
//temp Code
/* PdfDocument temp = new PdfDocument(writer);
Rectangle rectangle3x5 = new Rectangle(216, 360);
PageSize pageSize =new PageSize( rectangle3x5 );
temp.setDefaultPageSize( pageSize);
properties.setTagWorkerFactory( new CustomTagWorkerFactory() );*/
//temp code end
Document document = HtmlConverter.convertToDocument( htmlStr, writer, properties );
document.close();
// input stream
ByteArrayInputStream inputStream = new ByteArrayInputStream( outputStream.toByteArray() );
// content disposition header
String headerContentDispositionStr = String.format( "%s; filename=%s.pdf", method, filename.replaceAll( ",", "-" ) );
// return response
return ResponseEntity
.ok()
.header( HttpHeaders.CONTENT_DISPOSITION, headerContentDispositionStr )
.contentType( MediaType.APPLICATION_PDF )
.body( new InputStreamResource( inputStream ) );
}
/**
* prepare pdf document from html string
*/
private ResponseEntity<InputStreamResource> createPDFResponseEntityInvoiceInputStreamResource( String htmlStr, String filename, String method, long height, long width )
throws IOException {
// output stream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// converter properties
ConverterProperties properties = new ConverterProperties();
properties.setFontProvider( new DefaultFontProvider( false, true, false ) );
// pdf document
PdfWriter writer = new PdfWriter( outputStream );
//Code for invoice slips
PdfDocument temp = new PdfDocument( writer );
Rectangle rectangle3x5 = new Rectangle( width, height );
PageSize pageSize = new PageSize( rectangle3x5 );
temp.setDefaultPageSize( pageSize );
properties.setTagWorkerFactory( new CustomTagWorkerFactory() );
//temp code end
Document document = HtmlConverter.convertToDocument( htmlStr, temp, properties );
//document.setFontSize( 200.0f );
document.close();
// input stream
ByteArrayInputStream inputStream = new ByteArrayInputStream( outputStream.toByteArray() );
// content disposition header
String headerContentDispositionStr = String.format( "%s; filename=%s.pdf", method, filename.replaceAll( ",", "-" ) );
// return response
return ResponseEntity
.ok()
.header( HttpHeaders.CONTENT_DISPOSITION, headerContentDispositionStr )
.contentType( MediaType.APPLICATION_PDF )
.body( new InputStreamResource( inputStream ) );
}
/**
* with inline as default content disposition "inline"
*/
public ResponseEntity<InputStreamResource> generatePdf( String htmlStr, String filename ) throws IOException {
return this.createPDFResponseEntityInputStreamResource( htmlStr, filename, "inline" );
}
/**
* manual content disposition
*/
public ResponseEntity<InputStreamResource> generatePdf( String htmlStr, String filename, String method ) throws IOException {
return this.createPDFResponseEntityInputStreamResource( htmlStr, filename, method );
}
/**
* manual content disposition
*/
public ResponseEntity<InputStreamResource> generateInvoicePdf( String htmlStr, String filename, String method, long height, long width ) throws IOException {
return this.createPDFResponseEntityInvoiceInputStreamResource( htmlStr, filename, method, height, width );
}
}

View File

@ -0,0 +1,63 @@
package com.utopiaindustries.util;
public class TemporaryDocument {
private long id;
private long typeId;
private String parentDocumentType;
private long parentDocumentId;
private String photoBlob;
public TemporaryDocument() {
}
public long getId() {
return id;
}
public void setId( long id ) {
this.id = id;
}
public long getTypeId() {
return typeId;
}
public void setTypeId( long typeId ) {
this.typeId = typeId;
}
public String getParentDocumentType() {
return parentDocumentType;
}
public void setParentDocumentType( String parentDocumentType ) {
this.parentDocumentType = parentDocumentType;
}
public long getParentDocumentId() {
return parentDocumentId;
}
public void setParentDocumentId( long parentDocumentId ) {
this.parentDocumentId = parentDocumentId;
}
public String getPhotoBlob() {
return photoBlob;
}
public void setPhotoBlob( String photoBlob ) {
this.photoBlob = photoBlob;
}
@Override
public String toString() {
return "TemporaryDocument{" +
"id=" + id +
", typeId=" + typeId +
", parentDocumentType='" + parentDocumentType + '\'' +
", parentDocumentId=" + parentDocumentId +
", photoBlob='" + photoBlob + '\'' +
'}';
}
}

View File

@ -0,0 +1,16 @@
package com.utopiaindustries.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class URLUtils {
public static String getCurrentBaseUrl() {
ServletRequestAttributes sra = ( ServletRequestAttributes ) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = sra.getRequest();
return req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath();
}
}

View File

@ -0,0 +1,15 @@
package com.utopiaindustries.util;
import com.itextpdf.html2pdf.attach.ProcessorContext;
import com.itextpdf.html2pdf.attach.impl.tags.HtmlTagWorker;
import com.itextpdf.layout.Document;
import com.itextpdf.styledxmlparser.node.IElementNode;
public class ZeroMarginHtmlTagWorker extends HtmlTagWorker {
public ZeroMarginHtmlTagWorker( IElementNode element, ProcessorContext context) {
super(element, context);
Document doc = (Document) getElementResult();
doc.setMargins(0, 0, 0, 0);
}
}

View File

@ -0,0 +1 @@
*,*:before,*:after{padding:0;margin:0;box-sizing:border-box}body{font-family:"Open Sans",sans-serif;font-size:7pt;font-weight:400;line-height:1.2em;background:#fff !important;color:#111;position:relative;background:rgba(0,0,0,0) !important}body.draft:before,body.draft:after,body.terminated:before,body.terminated:after,body.not-approved:before,body.not-approved:after,body.duplicate:before,body.duplicate:after,body.cancel:before,body.cancel:after{font-size:70pt;font-weight:bold;color:red;text-align:center;display:block;opacity:.6;position:absolute;top:250pt;left:0;transform:rotate(-45deg)}body.duplicate:before,body.duplicate:after{content:"DUPLICATE"}body.cancel:before,body.cancel:after{content:"CANCEL"}body.draft:before,body.draft:after{content:"DRAFT"}body.terminated:before,body.terminated:after{content:"TERMINATED"}body.not-approved:before,body.not-approved:after{content:"NOT APPROVED"}@page{font-family:"Open Sans",sans-serif;font-size:6.65pt;font-style:italic;margin:1.25cm 1.25cm;@bottom-left{content:"Utopia Industries Pvt. Limited"}@bottom-center{content:"Page " counter(page) " of " counter(pages)}@bottom-right{width:126px;height:122px;margin-top:-130px}}h1{font-size:2em}h2{font-size:2.2857142857em}h3{font-size:1.8571428571em}h4{font-size:1.7142857143em}h5{font-size:1.5714285714em}h6{font-size:1.4285714286em}h1,h2,h3,h4,h5,h6{font-family:"Open Sans Condensed",sans-serif;font-weight:700;page-break-after:avoid;page-break-inside:avoid;text-transform:uppercase;margin:1.5em 0 1em}h1.no-margin-top,h2.no-margin-top,h3.no-margin-top,h4.no-margin-top,h5.no-margin-top,h6.no-margin-top{margin-top:0}h1.no-margin-bottom,h2.no-margin-bottom,h3.no-margin-bottom,h4.no-margin-bottom,h5.no-margin-bottom,h6.no-margin-bottom{margin-bottom:0}.text-right{text-align:right}.text-uppercase{text-transform:uppercase}.text-center{text-align:center}.font-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}b{font-weight:700}img{page-break-inside:avoid;page-break-after:avoid}ul,ol,dl{page-break-before:avoid}table{width:100%;border-collapse:collapse;border:none}table td{border:none}table.bordered,table.bordered td{border:1px solid #111}table.borderless,table.borderless td{border:none;padding:0}.tr-footer{text-transform:uppercase}td{padding:.4285714286em .8571428571em;vertical-align:top}td.spacer{padding:1em 0}.tr-header td{font-family:"Open Sans Condensed",sans-serif;font-size:1em;font-weight:bold;text-transform:uppercase;color:#fff;background:#111}td.pd-0{padding:0}ul,ol{padding:0 1.4285714286em;margin:0}ul{list-style-type:disc}.spacer{width:100%;height:1px;margin:2em 0 1em}.row{overflow:hidden;width:100%}.col{float:left}.col.col-half{width:49.96%}.col.col-half.col-padded:first-child{padding-right:3.5pt}.col.col-half.col-padded:last-child{padding-left:3.5pt}.col.col-two-third{width:66.6666%}.col.col-one-third{width:33.3333%}.col.col-one-third.col-padded{padding-left:3.5pt;padding-right:3.5pt}.col.col-one-third.col-padded:first-child{padding-left:0}.col.col-one-third.col-padded:last-child{padding-right:0}.visitor-photo{text-align:center;width:23%;height:77pt;line-height:77pt;margin-left:5pt;border:1px solid #111}.visitor-photo img{display:inline-block;width:90%;height:auto}/*# sourceMappingURL=print.css.map */

View File

@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml" xmlns:uind="http://www.w3.org/1999/xhtml"> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"
xmlns:uind="http://www.w3.org/1999/xhtml" xmlns:ctp="http://www.w3.org/1999/xhtml">
<head th:replace="_fragments :: head('Job Cards')"></head> <head th:replace="_fragments :: head('Job Cards')"></head>
<body> <body>
<div class="container-fluid"> <div class="container-fluid">
@ -60,6 +61,9 @@
<i class="bi bi-pencil"></i> <i class="bi bi-pencil"></i>
</a> </a>
</th:block> </th:block>
<a th:href="@{'/job-cards/pdf/' + *{id}}" target="_blank" class="btn btn-sm btn-secondary" title="View PDF">
<i class="bi bi-filetype-pdf"></i>
</a>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:uind="http://www.w3.org/1999/xhtml"
xml:lang="en"
lang="en"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Gate Inward Note Receipt</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans+Condensed:700|Open+Sans:400,400i&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" th:href="@{|${baseUrl}/css/print.css|}">
</head>
<body>
<div class="container-fluid">
<table>
<tr>
<td width="400">
<img width="200" th:src="@{|${baseUrl}/img/utopia-industries.png|}" alt="Utopia Industries">
</td>
<td >
<table class="bordered">
<tr class="tr-header">
<td colspan="2" th:text="'JOB CARD DETAIL'"></td>
</tr>
<tbody>
<tr>
<td style="width: 40%; border: 1px solid black;"><i>Job Card ID</i></td>
<td style="width: 60%; border: 1px solid black;">
<a class="text-reset" target="_blank" th:text="${card.getCode()}"></a>
</td>
</tr>
<tr>
<td style="width: 40%; border: 1px solid black;"><i>Job Order ID</i></td>
<td style="width: 60%; border: 1px solid black;">
<a class="text-reset" target="_blank" th:text="${card.getJobOrderId()}"></a>
</td>
</tr>
<tr>
<td class="align-middle" style="border: 1px solid black;"><i>Card Status</i></td>
<td style="border: 1px solid black;"><span th:text="${card.getStatus()}"></span></td>
</tr>
<tr>
<td class="align-middle" style="border: 1px solid black;"><i>Inventory Status</i></td>
<td style="border: 1px solid black;"><span th:text="${card.getInventoryStatus()}"></span></td>
</tr>
<tr>
<td class="align-middle" style="border: 1px solid black;"><i>Customer</i></td>
<td style="border: 1px solid black;"><span th:text="${card.getCustomer()}"></span></td>
</tr>
<tr>
<td class="align-middle" style="border: 1px solid black;"><i>Lot Number</i></td>
<td style="border: 1px solid black;"><span th:text="${card.getLotNumber()}"></span></td>
</tr>
<tr>
<td class="align-middle" style="border: 1px solid black;"><i>Purchase Order ID</i></td>
<td style="border: 1px solid black;"><span th:text="${card.getPurchaseOrderId()}"></span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<table class="bordered" style="width: 100%; margin-top: 20px; border-collapse: collapse; ">
<h5 class="no-margin-top no-margin-bottom" style="margin-top: 20px;">CUTTING DETAILS</h5>
<thead >
<tr class="tr-header">
<td th:text="'Sku'"></td>
<td th:text="'Length'"></td>
<td th:text="'Width'"></td>
<td th:text="'Gsm'"></td>
<td th:text="'WT_Ply'"></td>
<td th:text="'Ply'"></td>
</tr>
</thead>
<tbody>
<tr th:each="cardItem : ${jobCardItems}">
<td style="border: 1px solid black;" th:text="${cardItem.getSku()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getLength()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getWidth()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getGsm()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getWtPly()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getPly()}"></td>
</tr>
</tbody>
</table>
<table class="bordered" style="width: 100%; margin-top: 20px; border-collapse: collapse; ">
<h5 class="no-margin-top no-margin-bottom" style="margin-top: 20px;">ITEMS</h5>
<thead >
<tr class="tr-header">
<td th:text="'ID'"></td>
<td th:text="'Item ID'"></td>
<td th:text="'Sku'"></td>
<td th:text="'Expected Production'"></td>
<td th:text="'Actual Production'"></td>
<td th:text="'Total Production'"></td>
<td th:text="'Total Stitching Item'"></td>
<td th:text="'Total Finish Item'"></td>
<td th:text="'Cut Piece Items'"></td>
</tr>
</thead>
<!-- Table Body -->
<tbody>
<tr th:each="cardItem : ${jobCardItems}">
<td style="border: 1px solid black;" th:text="${cardItem.getId()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getItemId()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getSku()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getExpectedProduction()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getActualProduction()}"></td>
<td style="border: 1px solid black;" th:text="${cardItem.getTotalProduction()}"></td>
<td style="border: 1px solid black;" th:text="${totalStitchingItem.get(cardItem.getItemId())}"></td>
<td style="border: 1px solid black;" th:text="${totalFinishItem.get(cardItem.getItemId())}"></td>
<td style="margin:0px; padding:0px">
<table class="bordered">
<tbody>
<tr th:each="cutPieceItem : ${cutPiece}"
th:if="*{cardItem.getId() == cutPieceItem.getJobCardItemId()}">
<td style="border: 1px solid black;" th:text="${cutPieceItem.getType()}"></td>
<td style="border: 1px solid black;" th:text="${cutPieceItem.getQuantity()}"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>