diff --git a/api/myDATA.openapi.yaml b/api/myDATA.openapi.yaml index f0f6759..762a712 100644 --- a/api/myDATA.openapi.yaml +++ b/api/myDATA.openapi.yaml @@ -47,6 +47,24 @@ paths: - '{aade-user-id}' type: string default: '{aade-user-id}' + - name: nextPartitionKey + in: query + description: Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + required: false + schema: + enum: + - '{nextPartitionKey}' + type: string + default: '{nextPartitionKey}' + - name: nextRowKey + in: query + description: Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + required: false + schema: + enum: + - '{nextRowKey}' + type: string + default: '{nextRowKey}' responses: '200': description: @@ -98,6 +116,24 @@ paths: enum: - '{aade-user-id}' type: string + - name: nextPartitionKey + in: query + description: Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + required: false + schema: + enum: + - '{nextPartitionKey}' + type: string + default: '{nextPartitionKey}' + - name: nextRowKey + in: query + description: Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + required: false + schema: + enum: + - '{nextRowKey}' + type: string + default: '{nextRowKey}' responses: '200': description: diff --git a/internal/api/client.go b/internal/api/client.go index 0358f9c..e6ff548 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -40,6 +40,12 @@ type GetRequestdocsParams struct { // Μοναδικός Αριθμός Καταχώρησης Mark *GetRequestdocsParamsMark `json:"mark,omitempty"` + // Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + NextPartitionKey *GetRequestdocsParamsNextPartitionKey `json:"nextPartitionKey,omitempty"` + + // Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + NextRowKey *GetRequestdocsParamsNextRowKey `json:"nextRowKey,omitempty"` + // aade-user-id AadeUserId GetRequestdocsParamsAadeUserId `json:"aade-user-id"` } @@ -47,6 +53,12 @@ type GetRequestdocsParams struct { // GetRequestdocsParamsMark defines parameters for GetRequestdocs. type GetRequestdocsParamsMark string +// GetRequestdocsParamsNextPartitionKey defines parameters for GetRequestdocs. +type GetRequestdocsParamsNextPartitionKey string + +// GetRequestdocsParamsNextRowKey defines parameters for GetRequestdocs. +type GetRequestdocsParamsNextRowKey string + // GetRequestdocsParamsAadeUserId defines parameters for GetRequestdocs. type GetRequestdocsParamsAadeUserId string @@ -55,6 +67,12 @@ type GetRequesttransmitteddocsParams struct { // Μοναδικός Αριθμός Καταχώρησης Mark GetRequesttransmitteddocsParamsMark `json:"mark"` + // Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + NextPartitionKey *GetRequesttransmitteddocsParamsNextPartitionKey `json:"nextPartitionKey,omitempty"` + + // Παράμετρος για την τμηματική λήψη των αποτελεσμάτων + NextRowKey *GetRequesttransmitteddocsParamsNextRowKey `json:"nextRowKey,omitempty"` + // User Id AadeUserId GetRequesttransmitteddocsParamsAadeUserId `json:"aade-user-id"` } @@ -62,6 +80,12 @@ type GetRequesttransmitteddocsParams struct { // GetRequesttransmitteddocsParamsMark defines parameters for GetRequesttransmitteddocs. type GetRequesttransmitteddocsParamsMark string +// GetRequesttransmitteddocsParamsNextPartitionKey defines parameters for GetRequesttransmitteddocs. +type GetRequesttransmitteddocsParamsNextPartitionKey string + +// GetRequesttransmitteddocsParamsNextRowKey defines parameters for GetRequesttransmitteddocs. +type GetRequesttransmitteddocsParamsNextRowKey string + // GetRequesttransmitteddocsParamsAadeUserId defines parameters for GetRequesttransmitteddocs. type GetRequesttransmitteddocsParamsAadeUserId string @@ -345,6 +369,38 @@ func NewGetRequestdocsRequest(server string, params *GetRequestdocsParams) (*htt } + if params.NextPartitionKey != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "nextPartitionKey", runtime.ParamLocationQuery, *params.NextPartitionKey); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.NextRowKey != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "nextRowKey", runtime.ParamLocationQuery, *params.NextRowKey); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + queryURL.RawQuery = queryValues.Encode() req, err := http.NewRequest("GET", queryURL.String(), nil) @@ -397,6 +453,38 @@ func NewGetRequesttransmitteddocsRequest(server string, params *GetRequesttransm } } + if params.NextPartitionKey != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "nextPartitionKey", runtime.ParamLocationQuery, *params.NextPartitionKey); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.NextRowKey != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "nextRowKey", runtime.ParamLocationQuery, *params.NextRowKey); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + queryURL.RawQuery = queryValues.Encode() req, err := http.NewRequest("GET", queryURL.String(), nil) diff --git a/pkg/models/expense_classification.go b/pkg/models/expense_classification.go index ed3b4e6..18c425e 100644 --- a/pkg/models/expense_classification.go +++ b/pkg/models/expense_classification.go @@ -1,5 +1,10 @@ package models +import ( + "encoding/xml" + "fmt" +) + type ExpensesClassificationsDoc struct { ExpensesInvoiceClassification []ExpensesInvoiceClassification `xml:"ExpensesInvoiceClassification"` } @@ -16,3 +21,21 @@ type InvoicesExpensesClassificationDetails struct { LineNumber uint `xml:"lineNumber"` ExpensesClassificationDetailData []ExpensesClassificationType `xml:"ExpensesClassificationDetailData"` } + +// MarshalXML transforms an ExpensesClassificationType to expensesClassificationType and serializes it in order to +// include the `ecls` namespace for every field. +func (classification ExpensesClassificationType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error { + type expensesClassificationType struct { + ClassificationType string `xml:"ecls:classificationType"` + ClassificationCategory string `xml:"ecls:classificationCategory"` + Amount float64 `xml:"ecls:amount"` + ID *byte `xml:"ecls:id"` + } + + err := enc.EncodeElement(expensesClassificationType(classification), start) + if err != nil { + return fmt.Errorf("xml marshal expense classification: %w", err) + } + + return nil +} diff --git a/pkg/models/expense_classification_test.go b/pkg/models/expense_classification_test.go new file mode 100644 index 0000000..b244c41 --- /dev/null +++ b/pkg/models/expense_classification_test.go @@ -0,0 +1,28 @@ +package models + +import ( + "encoding/xml" + "testing" + + "github.com/ppapapetrou76/go-testing/assert" +) + +func TestExpensesClassificationType_MarshalXML(t *testing.T) { + t.Run("should have `ecls` namespace", func(t *testing.T) { + id := byte(1) + classification := ExpensesClassificationType{ + ClassificationType: "type", + ClassificationCategory: "category", + Amount: 12, + ID: &id, + } + + ft := assert.NewFluentT(t) + + got, err := xml.Marshal(classification) + ft.AssertThat(err).IsNil() + + want := []byte(`typecategory121`) + ft.AssertThat(got).IsEqualTo(want) + }) +} diff --git a/pkg/models/income_classification.go b/pkg/models/income_classification.go index 68c0206..6942684 100644 --- a/pkg/models/income_classification.go +++ b/pkg/models/income_classification.go @@ -1,5 +1,10 @@ package models +import ( + "encoding/xml" + "fmt" +) + type IncomeClassificationsDoc struct { IncomeInvoiceClassification []IncomeInvoiceClassification `xml:"incomeInvoiceClassification"` } @@ -16,3 +21,21 @@ type InvoicesIncomeClassificationDetails struct { LineNumber uint `xml:"lineNumber"` IncomeClassificationDetailData []IncomeClassificationType `xml:"incomeClassificationDetailData"` } + +// MarshalXML transforms an IncomeClassificationType to incomeClassificationType and serializes it in order to include +// the `icls` namespace for every field. +func (classification IncomeClassificationType) MarshalXML(enc *xml.Encoder, start xml.StartElement) error { + type incomeClassificationType struct { + ClassificationType string `xml:"icls:classificationType"` + ClassificationCategory string `xml:"icls:classificationCategory"` + Amount float64 `xml:"icls:amount"` + ID *byte `xml:"icls:id"` + } + + err := enc.EncodeElement(incomeClassificationType(classification), start) + if err != nil { + return fmt.Errorf("xml marshal income classification: %w", err) + } + + return nil +} diff --git a/pkg/models/income_classification_test.go b/pkg/models/income_classification_test.go new file mode 100644 index 0000000..530ea43 --- /dev/null +++ b/pkg/models/income_classification_test.go @@ -0,0 +1,28 @@ +package models + +import ( + "encoding/xml" + "testing" + + "github.com/ppapapetrou76/go-testing/assert" +) + +func TestIncomeClassificationType_MarshalXML(t *testing.T) { + t.Run("should have `icls` namespace", func(t *testing.T) { + id := byte(1) + classification := IncomeClassificationType{ + ClassificationType: "type", + ClassificationCategory: "category", + Amount: 12, + ID: &id, + } + + ft := assert.NewFluentT(t) + + got, err := xml.Marshal(classification) + ft.AssertThat(err).IsNil() + + want := []byte(`typecategory121`) + ft.AssertThat(got).IsEqualTo(want) + }) +} diff --git a/pkg/models/requested_doc.go b/pkg/models/requested_doc.go index 3231b42..41b09a0 100644 --- a/pkg/models/requested_doc.go +++ b/pkg/models/requested_doc.go @@ -9,8 +9,10 @@ type RequestedDoc struct { } type InvoicesDoc struct { - Xmlns string `xml:"xmlns,attr"` - Invoices []Invoice `xml:"invoice"` + Xmlns string `xml:"xmlns,attr"` + XmlnsICLS string `xml:"xmlns:icls,attr"` + XmlnsECLS string `xml:"xmlns:ecls,attr"` + Invoices []Invoice `xml:"invoice"` } type ContinuationToken struct { @@ -61,42 +63,42 @@ type InvoiceHeader struct { } type IncomeClassificationType struct { - ClassificationType string `xml:"classificationType"` - ClassificationCategory string `xml:"classificationCategory"` - Amount string `xml:"amount"` - ID *byte `xml:"id"` + ClassificationType string `xml:"classificationType"` + ClassificationCategory string `xml:"classificationCategory"` + Amount float64 `xml:"amount"` + ID *byte `xml:"id"` } type ExpensesClassificationType struct { - ClassificationType string `xml:"classificationType"` - ClassificationCategory string `xml:"classificationCategory"` - Amount string `xml:"amount"` - ID *byte `xml:"id"` + ClassificationType string `xml:"classificationType"` + ClassificationCategory string `xml:"classificationCategory"` + Amount float64 `xml:"amount"` + ID *byte `xml:"id"` } type InvoiceDetails struct { - LineNumber uint `xml:"lineNumber"` - RecType *uint `xml:"recType"` - Quantity *float64 `xml:"quantity"` - MeasurementUnit *uint `xml:"measurementUnit"` - InvoiceDetailType *uint `xml:"invoiceDetailType"` - NetValue float64 `xml:"netValue"` - VatCategory uint `xml:"vatCategory"` - VatAmount float64 `xml:"vatAmount"` - VatExemptionCategory *uint `xml:"vatExemptionCategory"` - Dienergia *ShipType `xml:"dienergia"` - DiscountOption *bool `xml:"discountOption"` - WithheldAmount *float64 `xml:"withheldAmount"` - WithheldPercentCategory *uint `xml:"withheldPercentCategory"` - StampDutyAmount *float64 `xml:"stampDutyAmount"` - StampDutyPercentCategory *uint `xml:"stampDutyPercentCategory"` - FeesAmount *float64 `xml:"feesAmount"` - FeesPercentCategory *uint `xml:"feesPercentCategory"` - OtherTaxesPercentCategory *uint `xml:"otherTaxesPercentCategory"` - OtherTaxesAmount *float64 `xml:"otherTaxesAmount"` - DeductionsAmount *float64 `xml:"deductionsAmount"` - IncomeClassification *IncomeClassificationType `xml:"incomeClassification"` - ExpensesClassification *ExpensesClassificationType `xml:"expensesClassification"` + LineNumber uint `xml:"lineNumber"` + RecType *uint `xml:"recType"` + Quantity *float64 `xml:"quantity"` + MeasurementUnit *uint `xml:"measurementUnit"` + InvoiceDetailType *uint `xml:"invoiceDetailType"` + NetValue float64 `xml:"netValue"` + VatCategory uint `xml:"vatCategory"` + VatAmount float64 `xml:"vatAmount"` + VatExemptionCategory *uint `xml:"vatExemptionCategory"` + Dienergia *ShipType `xml:"dienergia"` + DiscountOption *bool `xml:"discountOption"` + WithheldAmount *float64 `xml:"withheldAmount"` + WithheldPercentCategory *uint `xml:"withheldPercentCategory"` + StampDutyAmount *float64 `xml:"stampDutyAmount"` + StampDutyPercentCategory *uint `xml:"stampDutyPercentCategory"` + FeesAmount *float64 `xml:"feesAmount"` + FeesPercentCategory *uint `xml:"feesPercentCategory"` + OtherTaxesPercentCategory *uint `xml:"otherTaxesPercentCategory"` + OtherTaxesAmount *float64 `xml:"otherTaxesAmount"` + DeductionsAmount *float64 `xml:"deductionsAmount"` + IncomeClassification []*IncomeClassificationType `xml:"incomeClassification"` + ExpensesClassification []*ExpensesClassificationType `xml:"expensesClassification"` } type ShipType struct { @@ -107,16 +109,16 @@ type ShipType struct { } type InvoiceSummary struct { - TotalNetValue float64 `xml:"totalNetValue"` - TotalVatAmount float64 `xml:"totalVatAmount"` - TotalWithheldAmount float64 `xml:"totalWithheldAmount"` - TotalFeesAmount float64 `xml:"totalFeesAmount"` - TotalStampDutyAmount float64 `xml:"totalStampDutyAmount"` - TotalOtherTaxesAmount float64 `xml:"totalOtherTaxesAmount"` - TotalDeductionsAmount float64 `xml:"totalDeductionsAmount"` - TotalGrossValue float64 `xml:"totalGrossValue"` - IncomeClassification *IncomeClassificationType `xml:"incomeClassification"` - ExpensesClassification *ExpensesClassificationType `xml:"expensesClassification"` + TotalNetValue float64 `xml:"totalNetValue"` + TotalVatAmount float64 `xml:"totalVatAmount"` + TotalWithheldAmount float64 `xml:"totalWithheldAmount"` + TotalFeesAmount float64 `xml:"totalFeesAmount"` + TotalStampDutyAmount float64 `xml:"totalStampDutyAmount"` + TotalOtherTaxesAmount float64 `xml:"totalOtherTaxesAmount"` + TotalDeductionsAmount float64 `xml:"totalDeductionsAmount"` + TotalGrossValue float64 `xml:"totalGrossValue"` + IncomeClassification []*IncomeClassificationType `xml:"incomeClassification"` + ExpensesClassification []*ExpensesClassificationType `xml:"expensesClassification"` } type PaymentMethodDetails struct { diff --git a/pkg/mydata/client.go b/pkg/mydata/client.go index d125d73..02231c5 100644 --- a/pkg/mydata/client.go +++ b/pkg/mydata/client.go @@ -19,6 +19,8 @@ const ( defaultTimeout = time.Second * 30 defaultHost = "https://mydata-dev.azure-api.net/" xmlns = "http://www.aade.gr/myDATA/invoice/v1.0" + xmlnsICLS = "https://www.aade.gr/myDATA/incomeClassificaton/v1.0" + xmlnsECLS = "https://www.aade.gr/myDATA/expensesClassificaton/v1.0" ) // Client describes the myDATA Client. @@ -90,13 +92,25 @@ func NewClient(config *Config) (*Client, error) { // RequestTransmittedDocs returns the invoices,cancellations etc., issued by the entity associated to the // authenticated user. // If the api returns an error, or it is not accessible then the method returns an error with descriptive message. -func (c Client) RequestTransmittedDocs() (*models.RequestedDoc, error) { +func (c Client) RequestTransmittedDocs(mark string, nextPartitionKey, nextRowKey *string) (*models.RequestedDoc, error) { const errPrefix = "getting transmitted docs" ctx, cancel := context.WithTimeout(context.Background(), c.timeout) defer cancel() - resp, err := c.myDataClient.GetRequesttransmitteddocs(ctx, &api.GetRequesttransmitteddocsParams{Mark: ""}) + params := api.GetRequesttransmitteddocsParams{ + Mark: api.GetRequesttransmitteddocsParamsMark(mark), + } + if nextPartitionKey != nil { + partitionKey := api.GetRequesttransmitteddocsParamsNextPartitionKey(*nextPartitionKey) + params.NextPartitionKey = &partitionKey + } + if nextRowKey != nil { + rowKey := api.GetRequesttransmitteddocsParamsNextRowKey(*nextRowKey) + params.NextRowKey = &rowKey + } + + resp, err := c.myDataClient.GetRequesttransmitteddocs(ctx, ¶ms) if err != nil { return nil, fmt.Errorf("%s: api client failed: %w", errPrefix, err) } @@ -112,14 +126,25 @@ func (c Client) RequestTransmittedDocs() (*models.RequestedDoc, error) { // RequestDocs returns the invoices,cancellations etc., issued by third-party entities and are related to the entity // associated to the authenticated user. // If the api returns an error, or it is not accessible then the method returns an error with descriptive message. -func (c Client) RequestDocs() (*models.RequestedDoc, error) { +func (c Client) RequestDocs(mark string, nextPartitionKey, nextRowKey *string) (*models.RequestedDoc, error) { const errPrefix = "getting docs" ctx, cancel := context.WithTimeout(context.Background(), c.timeout) defer cancel() - mark := api.GetRequestdocsParamsMark("") + requestDocsMark := api.GetRequestdocsParamsMark(mark) + params := api.GetRequestdocsParams{ + Mark: &requestDocsMark, + } + if nextPartitionKey != nil { + partitionKey := api.GetRequestdocsParamsNextPartitionKey(*nextPartitionKey) + params.NextPartitionKey = &partitionKey + } + if nextRowKey != nil { + rowKey := api.GetRequestdocsParamsNextRowKey(*nextRowKey) + params.NextRowKey = &rowKey + } - resp, err := c.myDataClient.GetRequestdocs(ctx, &api.GetRequestdocsParams{Mark: &mark}) + resp, err := c.myDataClient.GetRequestdocs(ctx, ¶ms) if err != nil { return nil, fmt.Errorf("%s: api client failed: %w", errPrefix, err) } @@ -162,6 +187,8 @@ func (c Client) CancelDoc(mark uint64) ([]*models.Response, error) { func (c Client) SendDocs(invoiceDoc *models.InvoicesDoc) ([]*models.Response, error) { const errPrefix = "sending doc" invoiceDoc.Xmlns = xmlns + invoiceDoc.XmlnsICLS = xmlnsICLS + invoiceDoc.XmlnsECLS = xmlnsECLS ctx, cancel := context.WithTimeout(context.Background(), c.timeout) defer cancel() diff --git a/pkg/mydata/client_test.go b/pkg/mydata/client_test.go index ca12485..4f68cd4 100644 --- a/pkg/mydata/client_test.go +++ b/pkg/mydata/client_test.go @@ -288,14 +288,16 @@ func TestClient_SendDocs(t *testing.T) { }) } -func runRequestTest(t *testing.T, client *Client, f func() (*models.RequestedDoc, error), path, errPrefix string) { +func runRequestTest(t *testing.T, client *Client, f func(mark string, nextPartitionKey, nextRowKey *string) (*models.RequestedDoc, error), path, errPrefix string) { t.Helper() ft := assert.NewFluentT(t) + var mark string + var nextPartitionKey, nextRowKey *string = nil, nil t.Run("should fail if api call fails", func(t *testing.T) { client.myDataClient.Client = httpmock.NewMockClient( httpmock.NewMockResponse(&http.Response{}, errors.New("host not found"))) - docs, err := f() + docs, err := f(mark, nextPartitionKey, nextRowKey) ft.AssertThat(docs).IsNil() assert.ThatError(t, err).HasExactMessage( fmt.Sprintf("%s: api client failed: Get \"https://mydata-dev.azure-api.net/%s?mark=\": host not found", errPrefix, path)) @@ -308,7 +310,7 @@ func runRequestTest(t *testing.T, client *Client, f func() (*models.RequestedDoc Body: io.NopCloser(bytes.NewBuffer([]byte("bad request"))), }, nil)) - docs, err := f() + docs, err := f(mark, nextPartitionKey, nextRowKey) ft.AssertThat(docs).IsNil() assert.ThatError(t, err).HasExactMessage( fmt.Sprintf("%s: unexpected status code (400): bad request", errPrefix)) @@ -321,7 +323,7 @@ func runRequestTest(t *testing.T, client *Client, f func() (*models.RequestedDoc Body: io.NopCloser(bytes.NewBuffer([]byte("{}"))), }, nil)) - docs, err := f() + docs, err := f(mark, nextPartitionKey, nextRowKey) ft.AssertThat(docs).IsNil() assert.ThatError(t, err).HasExactMessage( fmt.Sprintf("%s: xml parser failed EOF", errPrefix)) @@ -334,11 +336,41 @@ func runRequestTest(t *testing.T, client *Client, f func() (*models.RequestedDoc Body: io.NopCloser(bytes.NewBuffer(response)), }, nil)) - docs, err := f() + docs, err := f(mark, nextPartitionKey, nextRowKey) ft.AssertThat(err).IsNil() ft.AssertThatStruct(docs.InvoicesDoc.Invoices[0]).IsEqualTo(expectedInvoice()) ft.AssertThatSlice(docs.InvoicesDoc.Invoices).HasSize(1).Contains(expectedInvoice()) }) + + t.Run("should include next partition and row key in the URL", func(t *testing.T) { + client.myDataClient.Client = httpmock.NewMockClient( + httpmock.NewMockResponse(&http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBuffer(response)), + }, nil)) + + want := map[string][]string{ + "mark": {"123"}, + "nextPartitionKey": {"1"}, + "nextRowKey": {"2"}, + } + + checkParameters := func(_ context.Context, req *http.Request) error { + values := req.URL.Query() + for k, got := range values { + ft.AssertThat(got).IsEqualTo(want[k]) + } + + return nil + } + client.myDataClient.RequestEditors = append(client.myDataClient.RequestEditors, checkParameters) + + mark = "123" + nextPartitionKeyValue := "1" + nextRowKeyValue := "2" + _, err := f(mark, &nextPartitionKeyValue, &nextRowKeyValue) + ft.AssertThat(err).IsNil() + }) } func expectedInvoice() models.Invoice { @@ -382,6 +414,20 @@ func expectedInvoice() models.Invoice { NetValue: 50.00, VatCategory: 1, VatAmount: 12, + IncomeClassification: []*models.IncomeClassificationType{ + { + ClassificationType: "E3_561_001", + ClassificationCategory: "category1_2", + Amount: 15, + ID: nil, + }, + { + ClassificationType: "E3_561_001", + ClassificationCategory: "category1_3", + Amount: 20, + ID: nil, + }, + }, }, }, InvoiceSummary: &models.InvoiceSummary{ diff --git a/pkg/mydata/testdata/request_response.xml b/pkg/mydata/testdata/request_response.xml index 1fdf1c6..7dcafeb 100644 --- a/pkg/mydata/testdata/request_response.xml +++ b/pkg/mydata/testdata/request_response.xml @@ -41,6 +41,16 @@ 1 12 LineItems + + E3_561_001 + category1_2 + 15 + + + E3_561_001 + category1_3 + 20 + 50