From b00c71c107abcc3057cd775e497b7f8bd336ffb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miko=C5=82ajczyk?= Date: Sun, 10 Dec 2023 21:14:11 +0100 Subject: [PATCH] fix bulk.yaml for compatibility; allow to download a PDF based on source invoice file and status file which is much faster than waiting for interactive session --- .../invoice_collection_parse_issuer.go | 24 +++++++---- internal/sei/api/pdf/download_pdf.go | 40 ++++++++++++++++--- internal/sei/api/status/structs.go | 12 ++++++ .../api_session_interactive_login.go | 4 +- przykladowe-pliki-wejsciowe/bulk.yaml | 4 +- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/internal/invoice/invoice_collection_parse_issuer.go b/internal/invoice/invoice_collection_parse_issuer.go index 27d7044..5cf179d 100644 --- a/internal/invoice/invoice_collection_parse_issuer.go +++ b/internal/invoice/invoice_collection_parse_issuer.go @@ -6,19 +6,29 @@ import ( "os" ) -type xmlInvoice struct { - XMLName xml.Name `xml:"Faktura"` - Issuer string `xml:"Podmiot1>DaneIdentyfikacyjne>NIP"` +type XMLInvoice struct { + XMLName xml.Name `xml:"Faktura"` + Issuer string `xml:"Podmiot1>DaneIdentyfikacyjne>NIP"` + InvoiceNumber string `xml:"Fa>P_2"` } func parseInvoiceIssuer(sourceFile string) (string, error) { - var invoice xmlInvoice + invoice, err := ParseInvoice(sourceFile) + if err != nil { + return "", fmt.Errorf("cannot parse issuer: %v", err) + } + return invoice.Issuer, nil +} + +func ParseInvoice(sourceFile string) (*XMLInvoice, error) { + var invoice XMLInvoice xmlContents, err := os.ReadFile(sourceFile) if err != nil { - return "", fmt.Errorf("unable to read invoice file: %v", err) + return nil, fmt.Errorf("unable to read invoice file: %v", err) } if err = xml.Unmarshal(xmlContents, &invoice); err != nil { - return "", fmt.Errorf("unable to parse xml invoice: %v", err) + return nil, fmt.Errorf("unable to parse xml invoice: %v", err) } - return invoice.Issuer, nil + + return &invoice, nil } diff --git a/internal/sei/api/pdf/download_pdf.go b/internal/sei/api/pdf/download_pdf.go index 027fe97..e0025b6 100644 --- a/internal/sei/api/pdf/download_pdf.go +++ b/internal/sei/api/pdf/download_pdf.go @@ -4,17 +4,47 @@ import ( "bytes" "fmt" "io" + invoicePkg "ksef/internal/invoice" "ksef/internal/sei/api/client" "ksef/internal/sei/api/status" "ksef/internal/sei/api/upload/interactive" "net/http" + "os" "path" ) const downloadInvoiceXML = "online/Invoice/Get/%s" const downloadInvoicePDF = "https://%s/web/api/invoice/get-invoice-pdf-file?ksefReferenceNumber=%s" -func DownloadPDF(apiClient *client.APIClient, statusInfo *status.StatusInfo, invoiceNo string, outputPath string) error { +func DownloadPDF(apiClient *client.APIClient, statusInfo *status.StatusInfo, invoice string, outputPath string) error { + // let's check if the specified `invoice` is actually a source XML file. + invoiceStruct, err := invoicePkg.ParseInvoice(invoice) + if err != nil { + // nope. let's continue with the API route + return downloadPDFFromAPI(apiClient, statusInfo, invoice, outputPath) + } + // yes, it is! let's download the PDF based on that. + seiRefNo, err := statusInfo.GetSEIRefNo(invoiceStruct.InvoiceNumber) + if err != nil { + return fmt.Errorf("unable to find the invoice in status file. was it uploaded?") + } + sourceInvoiceBytes, err := os.ReadFile(invoice) + if err != nil { + return fmt.Errorf("unable to read the source file: %v", err) + } + + httpSession := client.NewRequestFactory(apiClient) + invoiceXMLReader := bytes.NewReader(sourceInvoiceBytes) + + return httpSession.DownloadPDFFromSourceXML( + fmt.Sprintf(downloadInvoicePDF, apiClient.Environment.Host, seiRefNo), + seiRefNo+".xml", + invoiceXMLReader, + path.Join(outputPath, seiRefNo+".pdf"), + ) +} + +func downloadPDFFromAPI(apiClient *client.APIClient, statusInfo *status.StatusInfo, invoice string, outputPath string) error { // we have to initialize the interactive session session := interactive.InteractiveSessionInit(apiClient) if err := session.Login(statusInfo.Issuer); err != nil { @@ -27,7 +57,7 @@ func DownloadPDF(apiClient *client.APIClient, statusInfo *status.StatusInfo, inv httpSession := session.HTTPSession() - invoiceXMLRequest, err := httpSession.Request("GET", fmt.Sprintf(downloadInvoiceXML, invoiceNo), nil) + invoiceXMLRequest, err := httpSession.Request("GET", fmt.Sprintf(downloadInvoiceXML, invoice), nil) if err != nil { return fmt.Errorf("unable to download invoice in XML Format: %v", err) } @@ -46,9 +76,9 @@ func DownloadPDF(apiClient *client.APIClient, statusInfo *status.StatusInfo, inv invoiceXMLReader := bytes.NewReader(invoiceXML.Bytes()) return httpSession.DownloadPDFFromSourceXML( - fmt.Sprintf(downloadInvoicePDF, apiClient.Environment.Host, invoiceNo), - invoiceNo+".xml", + fmt.Sprintf(downloadInvoicePDF, apiClient.Environment.Host, invoice), + invoice+".xml", invoiceXMLReader, - path.Join(outputPath, invoiceNo+".pdf"), + path.Join(outputPath, invoice+".pdf"), ) } diff --git a/internal/sei/api/status/structs.go b/internal/sei/api/status/structs.go index 0a6caef..c4e6d75 100644 --- a/internal/sei/api/status/structs.go +++ b/internal/sei/api/status/structs.go @@ -1,5 +1,7 @@ package status +import "errors" + type KsefInvoiceIdType struct { InvoiceNumber string `xml:"NumerFaktury" json:"invoiceNumber" yaml:"invoiceNumber"` KSeFInvoiceReferenceNo string `xml:"NumerKSeFDokumentu" json:"ksefDocumentId" yaml:"ksefDocumentId"` @@ -13,3 +15,13 @@ type StatusInfo struct { Issuer string `json:"issuer" yaml:"issuer"` InvoiceIds []KsefInvoiceIdType `json:"invoiceIds,omitempty" yaml:"invoiceIds,omitempty"` } + +func (s *StatusInfo) GetSEIRefNo(invoiceNo string) (string, error) { + for _, invoice := range s.InvoiceIds { + if invoice.InvoiceNumber == invoiceNo { + return invoice.KSeFInvoiceReferenceNo, nil + } + } + + return "", errors.New("invoice number could not be found") +} diff --git a/internal/sei/api/upload/interactive/api_session_interactive_login.go b/internal/sei/api/upload/interactive/api_session_interactive_login.go index 7efe5cb..24cee7e 100644 --- a/internal/sei/api/upload/interactive/api_session_interactive_login.go +++ b/internal/sei/api/upload/interactive/api_session_interactive_login.go @@ -106,8 +106,8 @@ func (i *InteractiveSession) Login(issuer string) error { } i.session.SetHeader("SessionToken", initTokenResponse.Token.Value) - fmt.Printf("set token %s\n", initTokenResponse.Token.Value) - fmt.Printf("set ref no: %s\n", initTokenResponse.ReferenceNumber) + // fmt.Printf("set token %s\n", initTokenResponse.Token.Value) + // fmt.Printf("set ref no: %s\n", initTokenResponse.ReferenceNumber) i.referenceNo = initTokenResponse.ReferenceNumber return nil diff --git a/przykladowe-pliki-wejsciowe/bulk.yaml b/przykladowe-pliki-wejsciowe/bulk.yaml index 6ba5448..c695a9e 100644 --- a/przykladowe-pliki-wejsciowe/bulk.yaml +++ b/przykladowe-pliki-wejsciowe/bulk.yaml @@ -8,8 +8,8 @@ common: SystemInfo: WSI Pegasus # the following values are default and can be omitted KodFormularza: FA - KodFormularza#KodSystemowy: FA (2) - KodFormularza#WersjaSchemy: 1-0E + KodFormularza#kodSystemowy: FA (2) + KodFormularza#wersjaSchemy: 1-0E WariantFormularza: 2 Podmiot1: DaneIdentyfikacyjne: