NAV invoice detail query implemented, import-incoming-invoice workflow updated
authorelgekko <vasary@elgekko.net>
Wed, 4 Oct 2023 12:46:39 +0000 (14:46 +0200)
committerelgekko <vasary@elgekko.net>
Wed, 4 Oct 2023 12:46:39 +0000 (14:46 +0200)
lis-app/src/test/java/hu/user/lis/workflow/TaxOfficeInvoiceApiIT.java
lis-service/src/main/java/hu/user/lis/service/nav/RestTemplateSSLConfiguration.java [moved from lis-service/src/main/java/hu/user/lis/service/nav/CustomRestTemplateConfiguration.java with 97% similarity]
lis-service/src/main/java/hu/user/lis/service/nav/TaxOfficeInvoiceService.java
lis-service/src/main/java/hu/user/lis/service/nav/TaxOfficeRequestBuilder.java
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/DownloadInvoiceData.java
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/ListNewInvoices.java
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/IncomingInvoiceFetcherService.java

index a903d74d9c664eff853dd9f095c4e8220fb53410..d78729e4ccb90802a0737d05c990ce0747ff7452 100644 (file)
@@ -5,9 +5,10 @@
 
 package hu.user.lis.workflow;
 
-import hu.gov.nav.schemas.osa._3_0.api.InvoiceDirectionType;
-import hu.gov.nav.schemas.osa._3_0.api.QueryInvoiceDigestRequest;
-import hu.gov.nav.schemas.osa._3_0.api.QueryInvoiceDigestResponse;
+import hu.gov.nav.schemas.ntca._1_0.common.BasicResultType;
+import hu.gov.nav.schemas.ntca._1_0.common.FunctionCodeType;
+import hu.gov.nav.schemas.osa._3_0.api.*;
+import hu.gov.nav.schemas.osa._3_0.data.InvoiceData;
 import hu.user.lis.service.nav.TaxOfficeInvoiceService;
 import hu.user.lis.service.nav.TaxOfficeRequestBuilder;
 import hu.user.lis.service.nav.TaxOfficeXmlConverter;
@@ -37,6 +38,7 @@ import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.ZonedDateTime;
 import java.util.Collections;
+import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
@@ -144,8 +146,40 @@ public class TaxOfficeInvoiceApiIT {
 
     @Test
     public void testWithConnector() throws Exception {
-        Optional<QueryInvoiceDigestResponse> response = taxOfficeInvoiceService.queryInboundInvoices(1);
-        response.ifPresent(queryInvoiceDigestResponse -> log.info("Response: {}", taxOfficeXmlConverter.toXml(queryInvoiceDigestResponse)));
+        int availablePages = 1;
+        int currentPage = 1;
+        while (currentPage <= availablePages) {
+            Optional<QueryInvoiceDigestResponse> response = taxOfficeInvoiceService.queryInboundInvoices(currentPage);
+            if (!response.isPresent()) {
+                break;
+            }
+
+            Optional<FunctionCodeType> functionCode = response
+                    .map(QueryInvoiceDigestResponse::getResult)
+                    .map(BasicResultType::getFuncCode);
+
+            if (functionCode.isPresent() && FunctionCodeType.OK.equals(functionCode.get())) {
+                if (currentPage == 1) {
+                    availablePages = response
+                            .map(QueryInvoiceDigestResponse::getInvoiceDigestResult)
+                            .map(InvoiceDigestResultType::getAvailablePage).orElse(0);
+                }
+
+                Optional<List<InvoiceDigestType>> invoiceList = response
+                        .map(QueryInvoiceDigestResponse::getInvoiceDigestResult)
+                        .map(InvoiceDigestResultType::getInvoiceDigest);
+
+                if (invoiceList.isPresent()) {
+                    for (InvoiceDigestType invoiceDigest : invoiceList.get()) {
+                        String invoiceNumber = invoiceDigest.getInvoiceNumber();
+                        String supplierTaxNumber = invoiceDigest.getSupplierTaxNumber();
+                        Optional<InvoiceData> invoiceData = taxOfficeInvoiceService.queryInboundInvoiceData(invoiceNumber, supplierTaxNumber);
+                        invoiceData.ifPresent(invoice -> log.info("Found invoice: {}", taxOfficeXmlConverter.toXml(invoice)));
+                    }
+                }
+            }
+            currentPage++;
+        }
     }
 
 }
\ No newline at end of file
similarity index 97%
rename from lis-service/src/main/java/hu/user/lis/service/nav/CustomRestTemplateConfiguration.java
rename to lis-service/src/main/java/hu/user/lis/service/nav/RestTemplateSSLConfiguration.java
index c7cb4c28ef7ea1ce6bb9176970c670246bac40c0..57aa73cd89a0b0825171d487377a18730218845a 100644 (file)
@@ -21,7 +21,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateException;
 
 @Configuration
-public class CustomRestTemplateConfiguration {
+public class RestTemplateSSLConfiguration {
 
     @Value("${service.nav.trust.store}")
     private Resource trustStore;
index 83967374192c96d80ba4250806f5c1a740e571a0..2aab0f740af11226bdb270e1b20be03ee38004ec 100644 (file)
@@ -1,18 +1,21 @@
 package hu.user.lis.service.nav;
 
-import hu.gov.nav.schemas.osa._3_0.api.InvoiceDirectionType;
-import hu.gov.nav.schemas.osa._3_0.api.QueryInvoiceDigestRequest;
-import hu.gov.nav.schemas.osa._3_0.api.QueryInvoiceDigestResponse;
+import hu.gov.nav.schemas.osa._3_0.api.*;
+import hu.gov.nav.schemas.osa._3_0.data.InvoiceData;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.web.reactive.function.client.WebClient;
 import reactor.core.publisher.Mono;
 
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.Optional;
+import java.util.zip.GZIPInputStream;
 
 @Service
 public class TaxOfficeInvoiceService {
-
     @Autowired
     TaxOfficeRequestBuilder taxOfficeRequestBuilder;
 
@@ -22,16 +25,64 @@ public class TaxOfficeInvoiceService {
     @Autowired
     TaxOfficeConnector taxOfficeConnector;
 
+    private String decompress(byte[] bytes) throws Exception {
+        final StringBuilder builder = new StringBuilder();
+        try (GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
+             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gis, StandardCharsets.UTF_8))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                builder.append(line);
+            }
+        }
+        return builder.toString();
+    }
+
     public Optional<QueryInvoiceDigestResponse> queryInboundInvoices(int page) throws Exception {
-        QueryInvoiceDigestRequest queryRequest = taxOfficeRequestBuilder.requestInvoiceDigest();
-        queryRequest.setPage(page);
-        queryRequest.setInvoiceDirection(InvoiceDirectionType.INBOUND);
-        queryRequest.setInvoiceQueryParams(taxOfficeRequestBuilder.params());
+        QueryInvoiceDigestRequest request = taxOfficeRequestBuilder.requestInvoiceDigest();
+        request.setPage(page);
+        request.setInvoiceDirection(InvoiceDirectionType.INBOUND);
+        request.setInvoiceQueryParams(taxOfficeRequestBuilder.params());
 
         WebClient.RequestBodySpec bodySpec = taxOfficeConnector.getClient().post().uri("/queryInvoiceDigest");
-        WebClient.RequestHeadersSpec<?> headersSpec = bodySpec.bodyValue(taxOfficeXmlConverter.toXml(queryRequest));
+        WebClient.RequestHeadersSpec<?> headersSpec = bodySpec.bodyValue(taxOfficeXmlConverter.toXml(request));
         Mono<String> responseMono = headersSpec.retrieve().bodyToMono(String.class);
         return taxOfficeXmlConverter.fromXml(responseMono.block(), QueryInvoiceDigestResponse.class);
     }
 
+    public Optional<InvoiceData> queryInboundInvoiceData(String invoiceNumber, String supplierTaxNumber) throws Exception {
+        InvoiceNumberQueryType query = new InvoiceNumberQueryType();
+        query.setInvoiceDirection(InvoiceDirectionType.INBOUND);
+        query.setInvoiceNumber(invoiceNumber);
+        query.setSupplierTaxNumber(supplierTaxNumber);
+
+        QueryInvoiceDataRequest queryRequest = taxOfficeRequestBuilder.requestInvoiceData();
+        queryRequest.setInvoiceNumberQuery(query);
+
+        WebClient.RequestBodySpec bodySpec = taxOfficeConnector.getClient().post().uri("/queryInvoiceData");
+        WebClient.RequestHeadersSpec<?> headersSpec = bodySpec.bodyValue(taxOfficeXmlConverter.toXml(queryRequest));
+        Mono<String> responseMono = headersSpec.retrieve().bodyToMono(String.class);
+        Optional<QueryInvoiceDataResponse> response = taxOfficeXmlConverter.fromXml(responseMono.block(), QueryInvoiceDataResponse.class);
+//        invoiceData: A számla adatai BASE64 kódolásban
+//        compressedContentIndicator: Jelöli, ha az invoiceData tartalmát a BASE64 dekódolást követően még ki kell tömöríteni az olvasáshoz
+//        Base64 base64 = new Base64();
+//        String decodedString = new String(base64.decode(invoiceBytes));
+        byte[] invoiceBytes = response
+                .map(QueryInvoiceDataResponse::getInvoiceDataResult)
+                .map(InvoiceDataResultType::getInvoiceData)
+                .orElseThrow(NullPointerException::new);
+
+        boolean isDataCompressed = response
+                .map(QueryInvoiceDataResponse::getInvoiceDataResult)
+                .map(InvoiceDataResultType::isCompressedContentIndicator)
+                .orElse(false);
+
+        String invoiceData;
+        if (isDataCompressed) {
+            invoiceData = decompress(invoiceBytes);
+        } else {
+            invoiceData = new String(invoiceBytes);
+        }
+        return taxOfficeXmlConverter.fromXml(invoiceData, InvoiceData.class);
+    }
+
 }
index 6da2b395ea0a6552d4005e55747d962da1fb3501..120870fdc9229454d474718cafc71993a0a0f6c2 100644 (file)
@@ -132,18 +132,18 @@ public class TaxOfficeRequestBuilder {
         Az így és sorrendben konkatenált string nagybetűs SHA3-512 hash eredménye lesz a requestSignature
         értéke.
     */
-    private void calculateSimpleRequestSignature(QueryInvoiceDigestRequest request) {
-        ZonedDateTime zdt = request.getHeader().getTimestamp().toGregorianCalendar().toZonedDateTime();
+    private void calculateSimpleRequestSignature(BasicHeaderType header, UserHeaderType user) {
+        ZonedDateTime zdt = header.getTimestamp().toGregorianCalendar().toZonedDateTime();
         ZonedDateTime zdtUTC = zdt.withZoneSameInstant(ZoneOffset.UTC);
 
-        String rsSignature = request.getHeader().getRequestId() +
+        String rsSignature = header.getRequestId() +
                 DATE_TIME_FORMATTER.format(zdtUTC) +
                 signKey;
 
         CryptoType reqCryptoType = new CryptoType();
         reqCryptoType.setCryptoType("SHA3-512");
         reqCryptoType.setValue(sha3_512Hex(rsSignature).toUpperCase());
-        request.getUser().setRequestSignature(reqCryptoType);
+        user.setRequestSignature(reqCryptoType);
     }
 
     /* Java 9 fölött már támogatott 3rd party nélkül is*/
@@ -158,8 +158,16 @@ public class TaxOfficeRequestBuilder {
         result.setHeader(header());
         result.setUser(user());
         result.setSoftware(software());
-        calculateSimpleRequestSignature(result);
+        calculateSimpleRequestSignature(result.getHeader(), result.getUser());
         return result;
     }
 
+    public QueryInvoiceDataRequest requestInvoiceData() throws DatatypeConfigurationException {
+        QueryInvoiceDataRequest result = new QueryInvoiceDataRequest();
+        result.setHeader(header());
+        result.setUser(user());
+        result.setSoftware(software());
+        calculateSimpleRequestSignature(result.getHeader(), result.getUser());
+        return result;
+    }
 }
index 3a52917ad2226a54af3767dd304e95f4e2fc01c4..ee5ed39b24ec1fadec3db217ef40f8f7c0e0720b 100644 (file)
@@ -10,7 +10,6 @@ import org.camunda.bpm.engine.delegate.JavaDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.nio.file.Paths;
 import java.util.Optional;
 
 @Log4j2
@@ -18,20 +17,30 @@ import java.util.Optional;
 public class DownloadInvoiceData implements JavaDelegate {
 
     @Autowired
-    PartnerRepository partnerRepository;
+    private PartnerRepository partnerRepository;
 
     @Autowired
-    IncomingInvoiceFetcherService incomingInvoiceFetcherService;
+    private IncomingInvoiceFetcherService incomingInvoiceFetcherService;
+
+//    @Override
+//    public void execute(DelegateExecution delegateExecution) throws Exception {
+//        String invoiceId = (String) delegateExecution.getVariable("invoice");
+//        log.info("Processing invoice {}", invoiceId);
+//        IncomingInvoice invoice = incomingInvoiceFetcherService.getInvoiceFromPath(Paths.get(invoiceId));
+//        Optional<Partner> partnerEntity = partnerRepository.findByVatNr(invoice.getPartner().getVatNr());
+//        partnerEntity.ifPresent(invoice::setPartner);
+//        delegateExecution.setVariableLocal("invoiceEntity", invoice);
+//        log.info("Invoice {} processed", invoiceId);
+//    }
 
     @Override
     public void execute(DelegateExecution delegateExecution) throws Exception {
-        String invoiceId = (String) delegateExecution.getVariable("invoice");
-        log.info("Processing invoice {}", invoiceId);
-        IncomingInvoice invoice = incomingInvoiceFetcherService.getInvoiceFromPath(Paths.get(invoiceId));
+        String invoiceXml = (String) delegateExecution.getVariable("invoice");
+        log.info("Processing invoice {}", invoiceXml);
+        IncomingInvoice invoice = incomingInvoiceFetcherService.getInvoiceDataOnline(invoiceXml);
         Optional<Partner> partnerEntity = partnerRepository.findByVatNr(invoice.getPartner().getVatNr());
         partnerEntity.ifPresent(invoice::setPartner);
         delegateExecution.setVariableLocal("invoiceEntity", invoice);
-        log.info("Invoice {} processed", invoiceId);
+        log.info("Invoice {} processed", invoiceXml);
     }
-
 }
index 3f9803300938662fff3ff0d496d9fcb329d16d62..84b8ce6af23a9533ce446e652010ed9584d95307 100644 (file)
@@ -1,31 +1,37 @@
 package hu.user.lis.workflow.invoice;
 
 import hu.user.lis.workflow.invoice.service.IncomingInvoiceFetcherService;
-import hu.user.lis.workflow.properties.WorkflowProperties;
 import lombok.extern.log4j.Log4j2;
 import org.camunda.bpm.engine.delegate.DelegateExecution;
 import org.camunda.bpm.engine.delegate.JavaDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.List;
 
 @Log4j2
 @Component
 public class ListNewInvoices implements JavaDelegate {
-    @Autowired
-    WorkflowProperties workflowProperties;
+//    @Autowired
+//    private WorkflowProperties workflowProperties;
 
     @Autowired
-    IncomingInvoiceFetcherService incomingInvoiceFetcherService;
+    private IncomingInvoiceFetcherService incomingInvoiceFetcherService;
+
+//    @Override
+//    public void execute(DelegateExecution delegateExecution) throws Exception {
+//        log.info("Executing");
+//        Path inputPath = Paths.get(workflowProperties.getImportInvoice().getInputPath());
+//        List<String> invoices = incomingInvoiceFetcherService.getNewInvoicesFromPath(inputPath);
+//
+//        delegateExecution.setVariableLocal("invoices", invoices);
+//    }
 
     @Override
     public void execute(DelegateExecution delegateExecution) throws Exception {
         log.info("Executing");
-        Path inputPath = Paths.get(workflowProperties.getImportInvoice().getInputPath());
-        List<String> invoices = incomingInvoiceFetcherService.getNewInvoicesFromPath(inputPath);
+        List<String> invoices = incomingInvoiceFetcherService.getNewInvoicesOnline();
         delegateExecution.setVariableLocal("invoices", invoices);
     }
+
 }
index 5be2e00535c973784e953c908cb999f5f252c7ed..20aa9c4e2aa97a6cdf40eaab0b21a74531386af5 100644 (file)
@@ -1,11 +1,19 @@
 package hu.user.lis.workflow.invoice.service;
 
+import hu.gov.nav.schemas.ntca._1_0.common.BasicResultType;
+import hu.gov.nav.schemas.ntca._1_0.common.FunctionCodeType;
+import hu.gov.nav.schemas.osa._3_0.api.InvoiceDigestResultType;
+import hu.gov.nav.schemas.osa._3_0.api.InvoiceDigestType;
+import hu.gov.nav.schemas.osa._3_0.api.QueryInvoiceDigestResponse;
+import hu.gov.nav.schemas.osa._3_0.data.InvoiceData;
 import hu.user.lis.db.Currency;
 import hu.user.lis.db.IncomingInvoice;
 import hu.user.lis.db.Partner;
 import hu.user.lis.db.repository.PartnerRepository;
 import hu.user.lis.service.data.DataGeneratorService;
 import hu.user.lis.service.nav.TaxOfficeDataConverter;
+import hu.user.lis.service.nav.TaxOfficeInvoiceService;
+import hu.user.lis.service.nav.TaxOfficeXmlConverter;
 import org.apache.commons.lang3.RandomUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -15,9 +23,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -35,6 +41,12 @@ public class IncomingInvoiceFetcherService {
     @Autowired
     private TaxOfficeDataConverter taxOfficeDataConverter;
 
+    @Autowired
+    private TaxOfficeInvoiceService taxOfficeInvoiceService;
+
+    @Autowired
+    private TaxOfficeXmlConverter taxOfficeXmlConverter;
+
     public List<String> getNewInvoices() {
         return Arrays.asList("Invoice-test-1", "Invoice-test-2", "Invoice-test-3");
     }
@@ -89,4 +101,54 @@ public class IncomingInvoiceFetcherService {
         List<Partner> partners = partnerRepository.findAll();
         return partners.get(RandomUtils.nextInt(0, partners.size()));
     }
+
+    public IncomingInvoice getInvoiceDataOnline(String invoiceDigestInfo) throws Exception {
+        String[] tokens = invoiceDigestInfo.split(",");
+        String invoiceNumber = tokens[0];
+        String supplierTaxNumber = tokens[1];
+        InvoiceData invoiceData = taxOfficeInvoiceService.queryInboundInvoiceData(invoiceNumber, supplierTaxNumber).orElseThrow(NullPointerException::new);
+        String invoiceXml = taxOfficeXmlConverter.toXml(invoiceData);
+        IncomingInvoice result = taxOfficeDataConverter.getIncomingInvoice(invoiceXml);
+        Partner partner = taxOfficeDataConverter.getPartner(invoiceXml);
+        result.setPartner(partner);
+        return result;
+    }
+
+    public List<String> getNewInvoicesOnline() throws Exception {
+        List<String> result = new ArrayList<>();
+        int availablePages = 1;
+        int currentPage = 1;
+        while (currentPage <= availablePages) {
+            Optional<QueryInvoiceDigestResponse> response = taxOfficeInvoiceService.queryInboundInvoices(currentPage);
+            if (!response.isPresent()) {
+                break;
+            }
+
+            Optional<FunctionCodeType> functionCode = response
+                    .map(QueryInvoiceDigestResponse::getResult)
+                    .map(BasicResultType::getFuncCode);
+
+            if (functionCode.isPresent() && FunctionCodeType.OK.equals(functionCode.get())) {
+                if (currentPage == 1) {
+                    availablePages = response
+                            .map(QueryInvoiceDigestResponse::getInvoiceDigestResult)
+                            .map(InvoiceDigestResultType::getAvailablePage).orElse(0);
+                }
+
+                Optional<List<InvoiceDigestType>> invoiceList = response
+                        .map(QueryInvoiceDigestResponse::getInvoiceDigestResult)
+                        .map(InvoiceDigestResultType::getInvoiceDigest);
+
+                if (invoiceList.isPresent()) {
+                    for (InvoiceDigestType invoiceDigest : invoiceList.get()) {
+                        String invoiceNumber = invoiceDigest.getInvoiceNumber();
+                        String supplierTaxNumber = invoiceDigest.getSupplierTaxNumber();
+                        result.add(String.format("%s,%s", invoiceNumber, supplierTaxNumber));
+                    }
+                }
+            }
+            currentPage++;
+        }
+        return result;
+    }
 }