diff --git a/src/main/java/com/utopiaindustries/controller/JobCardController.java b/src/main/java/com/utopiaindustries/controller/JobCardController.java index 13f0c68..67ceb59 100644 --- a/src/main/java/com/utopiaindustries/controller/JobCardController.java +++ b/src/main/java/com/utopiaindustries/controller/JobCardController.java @@ -8,6 +8,9 @@ import com.utopiaindustries.service.InventoryAccountService; import com.utopiaindustries.service.JobCardService; import com.utopiaindustries.service.LocationService; 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.ui.Model; import org.springframework.web.bind.annotation.*; @@ -173,6 +176,11 @@ public class JobCardController { return "job-card-view"; } + @GetMapping( value = "/pdf/{id}", produces = MediaType.APPLICATION_PDF_VALUE ) + public ResponseEntity generateJobCardPdf(@PathVariable long id, Model model ) throws Exception { + return jobCardService.getJobCardReceivingPdf(id,model); + } + private ArrayList generateDateList(LocalDate start, LocalDate end) { ArrayList localDates = new ArrayList<>(); while (start.isBefore(end)) { diff --git a/src/main/java/com/utopiaindustries/service/JobCardService.java b/src/main/java/com/utopiaindustries/service/JobCardService.java index b28c3e4..b3e1966 100644 --- a/src/main/java/com/utopiaindustries/service/JobCardService.java +++ b/src/main/java/com/utopiaindustries/service/JobCardService.java @@ -7,14 +7,21 @@ import com.utopiaindustries.dao.uind.PurchaseOrderDAO; import com.utopiaindustries.model.ctp.*; import com.utopiaindustries.model.uind.Item; 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.URLUtils; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.ui.Model; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -36,8 +43,10 @@ public class JobCardService { private final FinishedItemDAO finishedItemDAO; private final StitchingOfflineItemDAO stitchingOfflineItemDAO; 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.jobCardDAO = jobCardDAO; this.cutPieceTypeDAO = cutPieceTypeDAO; @@ -49,6 +58,8 @@ public class JobCardService { this.userInventoryAccountDAO = userInventoryAccountDAO; this.finishedItemDAO = finishedItemDAO; this.stitchingOfflineItemDAO = stitchingOfflineItemDAO; + this.htmlBuilder = htmlBuilder; + this.pdfGenerator = pdfGenerator; } /* @@ -300,4 +311,29 @@ public class JobCardService { public Map totalFinishItem(List itemIds, long jobCardId ){ return finishedItemDAO.findTotalFinishedItems( itemIds, jobCardId ); } + + /** + * Print Job card * + * **/ + public ResponseEntity getJobCardReceivingPdf(long id, Model model ) throws Exception { + List jobCardItems = findJobCardItemByJobCardId(id); + List jobCardItemIds = jobCardItems.stream() + .map(JobCardItem::getItemId) + .collect(Collectors.toList()); + + List itemIds = jobCardItems.stream() + .map(JobCardItem::getId) + .collect(Collectors.toList()); + + model.addAttribute( "baseUrl", URLUtils.getCurrentBaseUrl() ); + model.addAttribute( "card", findByID(id)); + model.addAttribute("jobCardItems", jobCardItems); + model.addAttribute("cutPiece", findCutPieceByJobCardItemIds(itemIds)); + model.addAttribute("totalFinishItem", totalFinishItem(jobCardItemIds, id)); + model.addAttribute("totalStitchingItem", totalStitchingItem(jobCardItemIds, id)); + // html str + String htmlStr = htmlBuilder.buildHTML( "job-card-view-pdf", model ); + // return pdf + return pdfGenerator.generatePdf( htmlStr, "Job-Card", "inline" ); + } } diff --git a/src/main/java/com/utopiaindustries/util/CustomTagWorkerFactory.java b/src/main/java/com/utopiaindustries/util/CustomTagWorkerFactory.java new file mode 100644 index 0000000..3558e24 --- /dev/null +++ b/src/main/java/com/utopiaindustries/util/CustomTagWorkerFactory.java @@ -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; + } +} \ No newline at end of file diff --git a/src/main/java/com/utopiaindustries/util/HTMLBuilder.java b/src/main/java/com/utopiaindustries/util/HTMLBuilder.java new file mode 100644 index 0000000..d9a6c85 --- /dev/null +++ b/src/main/java/com/utopiaindustries/util/HTMLBuilder.java @@ -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 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 entry: model.entrySet() ) { + ctx.setVariable( entry.getKey(), entry.getValue() ); + } + return ctx; + } +} + diff --git a/src/main/java/com/utopiaindustries/util/PDFResponseEntityInputStreamResource.java b/src/main/java/com/utopiaindustries/util/PDFResponseEntityInputStreamResource.java new file mode 100644 index 0000000..204e12d --- /dev/null +++ b/src/main/java/com/utopiaindustries/util/PDFResponseEntityInputStreamResource.java @@ -0,0 +1,110 @@ +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 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 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 generatePdf( String htmlStr, String filename ) throws IOException { + return this.createPDFResponseEntityInputStreamResource( htmlStr, filename, "inline" ); + } + + /** + * manual content disposition + */ + public ResponseEntity generatePdf( String htmlStr, String filename, String method ) throws IOException { + return this.createPDFResponseEntityInputStreamResource( htmlStr, filename, method ); + } + + /** + * manual content disposition + */ + public ResponseEntity generateInvoicePdf( String htmlStr, String filename, String method, long height, long width ) throws IOException { + return this.createPDFResponseEntityInvoiceInputStreamResource( htmlStr, filename, method, height, width ); + } +} \ No newline at end of file diff --git a/src/main/java/com/utopiaindustries/util/URLUtils.java b/src/main/java/com/utopiaindustries/util/URLUtils.java new file mode 100644 index 0000000..77fcc76 --- /dev/null +++ b/src/main/java/com/utopiaindustries/util/URLUtils.java @@ -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(); + } +} + diff --git a/src/main/java/com/utopiaindustries/util/ZeroMarginHtmlTagWorker.java b/src/main/java/com/utopiaindustries/util/ZeroMarginHtmlTagWorker.java new file mode 100644 index 0000000..5d813db --- /dev/null +++ b/src/main/java/com/utopiaindustries/util/ZeroMarginHtmlTagWorker.java @@ -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); + } +} + diff --git a/src/main/resources/static/css/print.css b/src/main/resources/static/css/print.css new file mode 100644 index 0000000..ee1f96b --- /dev/null +++ b/src/main/resources/static/css/print.css @@ -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 */ diff --git a/src/main/resources/templates/job-card-list.html b/src/main/resources/templates/job-card-list.html index c0cba3f..bd4757a 100644 --- a/src/main/resources/templates/job-card-list.html +++ b/src/main/resources/templates/job-card-list.html @@ -1,5 +1,6 @@ - +
@@ -60,6 +61,9 @@ + + + diff --git a/src/main/resources/templates/job-card-view-pdf.html b/src/main/resources/templates/job-card-view-pdf.html new file mode 100644 index 0000000..06bb654 --- /dev/null +++ b/src/main/resources/templates/job-card-view-pdf.html @@ -0,0 +1,131 @@ + + + + + Job Card + + + + +
+ + + + + +
+ Utopia Industries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Job Card ID + +
Job Order ID + +
Card Status
Inventory Status
Customer
Lot Number
Purchase Order ID
+
+ + +
CUTTING DETAILS
+ + + + + + + + + + + + + + + + + + + + +
+ + +
ITEMS
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+
+ + \ No newline at end of file