Skip to content

Commit

Permalink
Add error handling when sending invoice
Browse files Browse the repository at this point in the history
Today (2024-06-10) after sending the invoice
we received the following error:

```xml
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <ns2:rispostaSdIRiceviFile
      xmlns:ns2="http://www.fatturapa.gov.it/sdi/ws/trasmissione/v1.0/types">
      <IdentificativoSdI>0</IdentificativoSdI>
      <DataOraRicezione>2024-06-10T16:00:00.003+02:00</DataOraRicezione>
      <Errore>EI02</Errore>
    </ns2:rispostaSdIRiceviFile>
  </soapenv:Body>
</soapenv:Envelope>
```

Code "EI02" (SERVIZIO NON DISPONIBILE) means service unavailable.
SdI (Sistema di Interscambio) was not working correctly
but `SendInvoice` function returned error as a nil.

This change fixes that.

Now CLI returns error about service unavailable.

```console
$ go run ./cmd/gobl.fatturapa transmit --ca-cert ./ca-all.pem --cert ./SDI-B85905495.pem --key ./key_client.key --env test invoice.xml
...
sending error: service unavailable
exit status 1
```
  • Loading branch information
torrocus committed Jun 10, 2024
1 parent 08491e6 commit 853e4fb
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 1 deletion.
37 changes: 36 additions & 1 deletion sdi/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func SendInvoice(ctx context.Context, invOpts InvoiceOpts, c *Client, cfg Config
return nil, err
}

return response, nil
return response, getErrorMessageFromResponse(*response)
}

// SoapRequestToSendInvoice prepares the request content for SOAP to send an invoice
Expand All @@ -63,3 +63,38 @@ func SoapRequestToSendInvoice(fileName string, fileBody []byte) string {
`</soapenv:Body>` +
`</soapenv:Envelope>`
}

const (
// EmptyFileError represents error for empty invoice file
EmptyFileError = "EI01" // FILE VUOTO

// ServiceUnavailableError represents error when service is unavailable
ServiceUnavailableError = "EI02" // SERVIZIO NON DISPONIBILE

// InactiveUserError represents error for inactive user
InactiveUserError = "EI03" // UTENTE NON ABILITATO

// IncorrectFileTypeError represents error for incorrect file type
IncorrectFileTypeError = "EI04" // TIPO FILE NON CORRETTO
)

func getErrorMessageFromResponse(response SendInvoiceResponse) error {
respErr := response.Body.Response.Error
if respErr == nil {
return nil
}

errors := map[string]string{
EmptyFileError: "empty file",
ServiceUnavailableError: "service unavailable",
InactiveUserError: "inactive user",
IncorrectFileTypeError: "incorrect file type",
}

errCode := errors[respErr.ErrorCode]
if errCode == "" {
return fmt.Errorf("unknown error code: %v", respErr)
}

return fmt.Errorf(errCode)
}
60 changes: 60 additions & 0 deletions sdi/invoice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,66 @@ import (
)

func TestSendInvoice(t *testing.T) {
t.Run("should return error for empty invoice file", func(t *testing.T) {
ctx := context.Background()
cfg := sdi.DevelopmentSdIConfig

xop := `
--MIMEBoundary_000000000000000000000000000000000000000000000000
Content-Type: application/xop+xml; charset=utf-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <[email protected]>
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns2:rispostaSdIRiceviFile
xmlns:ns2="http://www.fatturapa.gov.it/sdi/ws/trasmissione/v1.0/types">
<IdentificativoSdI>0</IdentificativoSdI>
<DataOraRicezione>2024-06-10T17:00:00.400+02:00</DataOraRicezione>
<Errore>EI01</Errore>
</ns2:rispostaSdIRiceviFile>
</soapenv:Body>
</soapenv:Envelope>
--MIMEBoundary_000000000000000000000000000000000000000000000000--
`

expectedResponse := sdi.ReceiveFileResponse{
SdiIdentifier: "0",
ReceiptTime: "2024-06-10T17:00:00.400+02:00",
Error: &sdi.ErrorReceipt{ErrorCode: "EI01"},
}

invOpts := sdi.InvoiceOpts{
FileName: "FILENAME.xml",
FileBody: []byte(""),
}

client := resty.New()

// block all HTTP requests
httpmock.ActivateNonDefault(client.GetClient())

header := http.Header{}
header.Set("Content-Type", `multipart/related; boundary="MIMEBoundary_000000000000000000000000000000000000000000000000"; type="application/xop+xml"; start="<[email protected]>"; start-info="text/xml"`)
header.Set("X-Powered-By", "Servlet/3.0")
responder := httpmock.NewStringResponder(200, xop).HeaderAdd(header)
httpmock.RegisterResponder("POST", cfg.SOAPReceiveFileEndpoint(), responder)

c := sdi.NewClient(
sdi.WithClient(client),
sdi.WithDebugMode(true),
)

response, err := sdi.SendInvoice(ctx, invOpts, c, cfg)
require.Error(t, err)
require.EqualError(t, err, "empty file")

assert.Equal(t, expectedResponse, response.Body.Response)

httpmock.DeactivateAndReset()
})

t.Run("should return receive file response", func(t *testing.T) {
ctx := context.Background()
cfg := sdi.DevelopmentSdIConfig
Expand Down

0 comments on commit 853e4fb

Please sign in to comment.