blob: 5f8ee313c642ba86203c2027cb8b8099b1198258 [file] [log] [blame]
package pl.hackerspace.service;
import com.lowagie.text.pdf.BaseFont;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.stereotype.Service;
import org.xhtmlrenderer.layout.SharedContext;
import org.xhtmlrenderer.pdf.ITextRenderer;
import pl.hackerspace.domain.Client;
import pl.hackerspace.dto.CustomInvoiceDataDto;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Service
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TemplateService {
public static final DecimalFormat MONEY_FORMAT = twoDecimalsFormatter();
public static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM");
public static final String HTML_TEMPLATE_FILE = "invoiceTemplates/invoice_one_service.html";
public static <A> A withTemplate(Function<String, A> execute) throws IOException {
try (InputStream resourceAsStream = TemplateService.class.getClassLoader().getResourceAsStream(HTML_TEMPLATE_FILE)) {
String template = new String(resourceAsStream.readAllBytes());
return execute.apply(template);
}
}
static String populateTemplate(String unprocessedTemplate, Client client, final String subscriptionMonth,
final LocalDateTime invoiceCreationDate, final String invoiceTitle,
CustomInvoiceDataDto customInvoiceData) {
boolean isCustom = customInvoiceData != null;
BigDecimal price = isCustom ? customInvoiceData.getCustomPrice() : client.getPrice();
BigDecimal amount = isCustom ? customInvoiceData.getCustomAmount() : client.getAmount();
BigDecimal vat = isCustom ? customInvoiceData.getCustomVat() : client.getVat();
String serviceName = isCustom ? customInvoiceData.getCustomServiceName() : client.getServiceName();
BigDecimal totalNet = amount.multiply(price);
String processedHtml = unprocessedTemplate
.replace("%client_name%", client.getName())
.replace("%invoice_title%", invoiceTitle)
.replace("%client_price%", formatAsMoney(price))
.replace("%client_addressLine1%", client.getAddressLine1())
.replace("%client_addressLine2%", client.getAddressLine2())
.replace("%client_service_name%", serviceName)
.replace("%client_nip%", Optional.ofNullable(client.getNip()).map(nip -> "NIP: " + nip).orElse(""))
.replace("%client_total_net%", formatAsMoney(totalNet))
.replace("%client_total_gross%", formatAsMoney(totalNet.add(percentageValue(totalNet, vat))))
.replace("%client_amount%", formatAsMoney(amount))
.replace("%client_vat%", formatAsMoney(vat))
.replace("%client_total_tax%", formatAsMoney(percentageValue(totalNet, vat)))
.replace("%invoice_month_string%", subscriptionMonth)
.replace("%client_payment_date%", formatAsDate(invoiceCreationDate.plusDays(client.getPaymentOffsetDays())))
.replace("%invoice_date%", formatAsDate(invoiceCreationDate));
validateAllPlaceholdersSubstituted(processedHtml);
return processedHtml;
}
private static String formatAsMoney(BigDecimal totalNet) {
return MONEY_FORMAT.format(totalNet);
}
private static String formatAsDate(LocalDateTime invoiceCreationDate) {
return invoiceCreationDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
}
private static void validateAllPlaceholdersSubstituted(String processed) {
Set<String> allMatches = new HashSet<>();
Matcher m = Pattern.compile("%(\\w*?)%").matcher(processed);
while (m.find()) {
allMatches.add(m.group());
}
if (!allMatches.isEmpty()) {
throw new IllegalStateException("There are unsubstituted placeholders in the template: " + allMatches);
}
}
private static DecimalFormat twoDecimalsFormatter() {
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(2);
df.setGroupingUsed(false);
return df;
}
public static BigDecimal percentageValue(BigDecimal base, BigDecimal pct) {
return base.multiply(pct).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
}
static byte[] convertHtmlToPdf(String unprocessedTemplate, Client client, final LocalDateTime creationDate,
final String invoiceTitle, final String monthOfInvoice, CustomInvoiceDataDto customInvoiceData) {
String processedHtml = populateTemplate(unprocessedTemplate, client, monthOfInvoice, creationDate, invoiceTitle,
customInvoiceData);
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ITextRenderer renderer = new ITextRenderer();
SharedContext sharedContext = renderer.getSharedContext();
sharedContext.setPrint(true);
sharedContext.setInteractive(false);
renderer.getFontResolver().addFont("fonts/calibri.ttf", BaseFont.IDENTITY_H, true);
renderer.setDocumentFromString(htmlToXhtml(processedHtml));
renderer.layout();
renderer.createPDF(outputStream);
return outputStream.toByteArray();
} catch (IOException e) {
throw new RuntimeException("Exception during pdf conversion", e);
}
}
private static String htmlToXhtml(String html) {
Document document = Jsoup.parse(html);
document.outputSettings().syntax(Document.OutputSettings.Syntax.xml);
return document.html();
}
}