invoicer initial version
Change-Id: Ib20a96c224f5c055874f72f8f9a04a4dc8bbbc24
diff --git a/personal/arsenicum/invoicer/src/main/java/pl/hackerspace/service/InvoiceService.java b/personal/arsenicum/invoicer/src/main/java/pl/hackerspace/service/InvoiceService.java
new file mode 100644
index 0000000..2cdb88e
--- /dev/null
+++ b/personal/arsenicum/invoicer/src/main/java/pl/hackerspace/service/InvoiceService.java
@@ -0,0 +1,143 @@
+package pl.hackerspace.service;
+
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.ContentDisposition;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StreamUtils;
+import pl.hackerspace.domain.Client;
+import pl.hackerspace.domain.Invoice;
+import pl.hackerspace.dto.CustomInvoiceDataDto;
+import pl.hackerspace.dto.InvoiceGenerationDto;
+import pl.hackerspace.repository.ClientRepository;
+import pl.hackerspace.repository.InvoiceRepository;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import static pl.hackerspace.service.TemplateService.withTemplate;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class InvoiceService {
+
+ private final ClientRepository clientRepository;
+
+ private final InvoiceRepository invoiceRepository;
+
+ @Transactional
+ public ResponseEntity<Resource> generateNewInvoice(InvoiceGenerationDto generationRequest) throws IOException {
+ Client client = clientRepository.findById(generationRequest.getNip()).orElseThrow(() -> new RuntimeException("Not found"));
+ return withTemplate(template -> {
+ byte[] invoice;
+ if (!client.isSubscriber() || generationRequest.getCustomInvoiceData() != null) {
+ invoice = createPdfInvoice(client, template, null, generationRequest.getCustomInvoiceData());
+ } else {
+ invoice = getOrCreateSubscriptionPdfInvoice(template, client, generationRequest.getMonthOfInvoice());
+ }
+ return ResponseEntity.ok()
+ .headers(getHttpHeaders(client.getName() + " " + generationRequest.getMonthOfInvoice()))
+ .contentLength(-1)
+ .contentType(MediaType.APPLICATION_PDF)
+ .body(new ByteArrayResource(invoice));
+ });
+ }
+
+
+ @Transactional
+ public void generateInvoicesForAllSubscribers(HttpServletResponse response, String monthOfInvoice) throws IOException {
+ setHeaders(response, "application/zip", LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE) + ".zip");
+ List<Client> subscribers = clientRepository.findBySubscriberTrue();
+ withTemplate(template -> {
+ try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
+ for (Client client : subscribers) {
+ try (InputStream inputStream = new ByteArrayInputStream(
+ getOrCreateSubscriptionPdfInvoice(template, client, monthOfInvoice))) {
+ zipOutputStream.putNextEntry(new ZipEntry(getPdfFilename(client.getName() + " "
+ + monthOfInvoice)));
+ StreamUtils.copy(inputStream, zipOutputStream);
+ zipOutputStream.flush();
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ });
+ }
+
+ private byte[] getOrCreateSubscriptionPdfInvoice(String template, Client client, final String monthOfInvoice) {
+ byte[] invoice = client.getInvoiceForSubscriptionMonth(monthOfInvoice);
+ if (invoice == null) {
+ invoice = createPdfInvoice(client, template, monthOfInvoice, null);
+ }
+ return invoice;
+ }
+
+ private static void setHeaders(HttpServletResponse response, String contentType, String filename) {
+ response.setContentType(contentType);
+ response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
+ .filename(filename, StandardCharsets.UTF_8)
+ .build()
+ .toString());
+ }
+
+ private byte[] createPdfInvoice(Client client, String template, String monthOfSubscription,
+ CustomInvoiceDataDto customInvoiceData) {
+ LocalDateTime creationDate = LocalDateTime.now();
+ Invoice newInvoice = createNewInvoice(client, creationDate, monthOfSubscription);
+ byte[] bytes = TemplateService.convertHtmlToPdf(template, client, creationDate,
+ newInvoice.getInvoiceTitle(), monthOfSubscription, customInvoiceData);
+ newInvoice.setPdfContent(bytes);
+ save(newInvoice);
+ return bytes;
+ }
+
+ private static HttpHeaders getHttpHeaders(final String filename) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
+ headers.add(HttpHeaders.CONTENT_TYPE, "application/octet-stream");
+ headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=" + getPdfFilename(filename));
+ return headers;
+ }
+
+ private static String getPdfFilename(String filename) {
+ return filename + ".pdf";
+ }
+
+ public Invoice createNewInvoice(Client client, final LocalDateTime creationDate, String monthOfSubscription) {
+ long nextInvoiceId = invoiceRepository.getMaxId() + 1;
+ return Invoice.builder().id(nextInvoiceId)
+ .invoiceTitle("FV" + nextInvoiceId)
+ .creationDate(creationDate)
+ .monthOfSubscription(monthOfSubscription)
+ .client(client)
+ .build();
+ }
+
+ public void save(Invoice newInvoice) {
+ invoiceRepository.save(newInvoice);
+ }
+
+ public List<Invoice> findAll() {
+ return invoiceRepository.findAll();
+ }
+}