add all methods for cancellations, add methods for getting fbo shipment details
This commit is contained in:
18
ENDPOINTS.md
18
ENDPOINTS.md
@@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
## FBO
|
## FBO
|
||||||
- [x] Shipments list
|
- [x] Shipments list
|
||||||
- [ ] Shipment details
|
- [x] Shipment details
|
||||||
|
|
||||||
## FBS and rFBS products labeling
|
## FBS and rFBS products labeling
|
||||||
- [x] Validate labeling codes
|
- [x] Validate labeling codes
|
||||||
@@ -132,10 +132,10 @@
|
|||||||
- [x] Get information about FBS returns
|
- [x] Get information about FBS returns
|
||||||
|
|
||||||
## Cancellations
|
## Cancellations
|
||||||
- [ ] Get information about a rFBS cancellation request
|
- [x] Get information about a rFBS cancellation request
|
||||||
- [ ] Get a list of rFBS cancellation requests
|
- [x] Get a list of rFBS cancellation requests
|
||||||
- [ ] Approve a rFBS cancellation request
|
- [x] Approve a rFBS cancellation request
|
||||||
- [ ] Reject a rFBS cancellation request
|
- [x] Reject a rFBS cancellation request
|
||||||
|
|
||||||
## Chats with customers
|
## Chats with customers
|
||||||
- [ ] Chats list
|
- [ ] Chats list
|
||||||
@@ -160,9 +160,9 @@
|
|||||||
- [x] Returns report
|
- [x] Returns report
|
||||||
- [x] Shipment report
|
- [x] Shipment report
|
||||||
- [x] Financial report
|
- [x] Financial report
|
||||||
- [ ] Issue a report on discounted products
|
- [x] Issue a report on discounted products
|
||||||
- [ ] Report on discounted products
|
- [x] Report on discounted products
|
||||||
- [ ] List of reports on discounted products
|
- [x] List of reports on discounted products
|
||||||
|
|
||||||
## Analytics
|
## Analytics
|
||||||
- [x] Analytics data
|
- [x] Analytics data
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
|
|
||||||
## Finance
|
## Finance
|
||||||
- [x] Report on sold products
|
- [x] Report on sold products
|
||||||
- [ ] Transactions list (version 3)
|
- [x] Transactions list (version 3)
|
||||||
- [x] Total transactions sum
|
- [x] Total transactions sum
|
||||||
|
|
||||||
## Seller rating
|
## Seller rating
|
||||||
|
|||||||
207
ozon/cancellations.go
Normal file
207
ozon/cancellations.go
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
package ozon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cancellations struct {
|
||||||
|
client *core.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetCancellationInfoParams struct {
|
||||||
|
// Cancellation request identifier
|
||||||
|
CancellationId int64 `json:"cancellation_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetCancellationInfoResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result CancellationInfo `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CancellationInfo struct {
|
||||||
|
// Cancellation request identifier
|
||||||
|
CancellationId int64 `json:"cancellation_id"`
|
||||||
|
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Cancellation reason
|
||||||
|
CancellationReason struct {
|
||||||
|
// Cancellation reason identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
|
// Cancellation reason name
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"cancellation_reason"`
|
||||||
|
|
||||||
|
// Cancellation request creation date
|
||||||
|
CancelledAt time.Time `json:"cancelled_at"`
|
||||||
|
|
||||||
|
// Comment to cancellation submitted by cancellation initiator
|
||||||
|
CancellationReasonMessage string `json:"cancellation_reason_message"`
|
||||||
|
|
||||||
|
// Delivery service integration type
|
||||||
|
TPLIntegrationType string `json:"tpl_integration_type"`
|
||||||
|
|
||||||
|
// Cancellation request status
|
||||||
|
State struct {
|
||||||
|
// Status identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
|
// Status name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Request status
|
||||||
|
State string `json:"state"`
|
||||||
|
} `json:"state"`
|
||||||
|
|
||||||
|
// Cancellation initiator
|
||||||
|
CancellationInitiator string `json:"cancellation_initiator"`
|
||||||
|
|
||||||
|
// Order creation date
|
||||||
|
OrderDate time.Time `json:"order_date"`
|
||||||
|
|
||||||
|
// Comment submitted on the cancellation request approval or rejection
|
||||||
|
ApproveComment string `json:"approve_comment"`
|
||||||
|
|
||||||
|
// Cancellation request approval or rejection date
|
||||||
|
ApproveDate time.Time `json:"approve_date"`
|
||||||
|
|
||||||
|
// Date after which the request will be automatically approved
|
||||||
|
AutoApproveDate time.Time `json:"auto_approve_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method for getting information about a rFBS cancellation request
|
||||||
|
func (c Cancellations) GetInfo(params *GetCancellationInfoParams) (*GetCancellationInfoResponse, error) {
|
||||||
|
url := "/v1/delivery-method/list"
|
||||||
|
|
||||||
|
resp := &GetCancellationInfoResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListCancellationsParams struct {
|
||||||
|
// Filters
|
||||||
|
Filter ListCancellationsFilter `json:"filter"`
|
||||||
|
|
||||||
|
// Number of cancellation requests in the response
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
|
||||||
|
// Number of elements that will be skipped in the response.
|
||||||
|
// For example, if offset=10, the response will start with the 11th element found
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
|
||||||
|
// Additional information
|
||||||
|
With ListCancellationWith `json:"with"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListCancellationsFilter struct {
|
||||||
|
// Filter by cancellation initiator
|
||||||
|
CancellationInitiator []string `json:"cancellation_initiator"`
|
||||||
|
|
||||||
|
// Filter by shipment number.
|
||||||
|
//
|
||||||
|
// Optional parameter. You can pass several values here
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Filter by cancellation request status
|
||||||
|
State string `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListCancellationWith struct {
|
||||||
|
// Indication that the counter of requests in different statuses should be displayed in the response
|
||||||
|
Counters bool `json:"counters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListCancellationsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Cancellation requests list
|
||||||
|
Result []CancellationInfo `json:"result"`
|
||||||
|
|
||||||
|
// The total number of requests by the specified filters
|
||||||
|
Total int32 `json:"total"`
|
||||||
|
|
||||||
|
// Counter of requests in different statuses
|
||||||
|
Counters struct {
|
||||||
|
// Number of requests for approval
|
||||||
|
OnApproval int64 `json:"on_approval"`
|
||||||
|
|
||||||
|
// Number of approved requests
|
||||||
|
Approved int64 `json:"approved"`
|
||||||
|
|
||||||
|
// Number of rejected requests
|
||||||
|
Rejected int64 `json:"rejected"`
|
||||||
|
} `json:"counters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method for getting a list of rFBS cancellation requests
|
||||||
|
func (c Cancellations) List(params *ListCancellationsParams) (*ListCancellationsResponse, error) {
|
||||||
|
url := "/v1/conditional-cancellation/list"
|
||||||
|
|
||||||
|
resp := &ListCancellationsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApproveRejectCancellationsParams struct {
|
||||||
|
// Cancellation request identifier
|
||||||
|
CancellationId int64 `json:"cancellation_id"`
|
||||||
|
|
||||||
|
// Comment
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApproveRejectCancellationsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// The method allows to approve an rFBS cancellation request in the ON_APPROVAL status.
|
||||||
|
// The order will be canceled and the money will be returned to the customer
|
||||||
|
func (c Cancellations) Approve(params *ApproveRejectCancellationsParams) (*ApproveRejectCancellationsResponse, error) {
|
||||||
|
url := "/v1/conditional-cancellation/approve"
|
||||||
|
|
||||||
|
resp := &ApproveRejectCancellationsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The method allows to reject an rFBS cancellation request in the ON_APPROVAL status. Explain your decision in the comment parameter.
|
||||||
|
//
|
||||||
|
// The order will remain in the same status and must be delivered to the customer
|
||||||
|
func (c Cancellations) Reject(params *ApproveRejectCancellationsParams) (*ApproveRejectCancellationsResponse, error) {
|
||||||
|
url := "/v1/conditional-cancellation/reject"
|
||||||
|
|
||||||
|
resp := &ApproveRejectCancellationsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
271
ozon/cancellations_test.go
Normal file
271
ozon/cancellations_test.go
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
package ozon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetCancellationInfo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetCancellationInfoParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetCancellationInfoParams{
|
||||||
|
CancellationId: 90066344,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": {
|
||||||
|
"cancellation_id": 90066344,
|
||||||
|
"posting_number": "47134289-0029-1",
|
||||||
|
"cancellation_reason": {
|
||||||
|
"id": 508,
|
||||||
|
"name": "Покупатель отменил заказ"
|
||||||
|
},
|
||||||
|
"cancelled_at": "2022-04-07T06:37:26.871105Z",
|
||||||
|
"cancellation_reason_message": "Изменение пункта выдачи заказа.",
|
||||||
|
"tpl_integration_type": "ThirdPartyTracking",
|
||||||
|
"state": {
|
||||||
|
"id": 2,
|
||||||
|
"name": "Подтверждена",
|
||||||
|
"state": "APPROVED"
|
||||||
|
},
|
||||||
|
"cancellation_initiator": "CLIENT",
|
||||||
|
"order_date": "2022-04-06T17:17:24.517Z",
|
||||||
|
"approve_comment": "",
|
||||||
|
"approve_date": "2022-04-07T07:52:45.971824Z",
|
||||||
|
"auto_approve_date": "2022-04-09T06:37:26.871105Z"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetCancellationInfoParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Cancellations().GetInfo(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
if resp.Result.CancellationId != test.params.CancellationId {
|
||||||
|
t.Errorf("Cancellation ids in request and response are not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListCancellations(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ListCancellationsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ListCancellationsParams{
|
||||||
|
Filter: ListCancellationsFilter{
|
||||||
|
CancellationInitiator: []string{"CLIENT"},
|
||||||
|
State: "ALL",
|
||||||
|
},
|
||||||
|
Limit: 2,
|
||||||
|
Offset: 0,
|
||||||
|
With: ListCancellationWith{
|
||||||
|
Counters: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"cancellation_id": 50186754,
|
||||||
|
"posting_number": "41267064-0032-1",
|
||||||
|
"cancellation_reason": {
|
||||||
|
"id": 508,
|
||||||
|
"name": "Покупатель отменил заказ"
|
||||||
|
},
|
||||||
|
"cancelled_at": "2021-09-03T07:17:12.116114Z",
|
||||||
|
"cancellation_reason_message": "",
|
||||||
|
"tpl_integration_type": "ThirdPartyTracking",
|
||||||
|
"state": {
|
||||||
|
"id": 2,
|
||||||
|
"name": "Подтверждена",
|
||||||
|
"state": "APPROVED"
|
||||||
|
},
|
||||||
|
"cancellation_initiator": "CLIENT",
|
||||||
|
"order_date": "2021-09-03T07:04:53.220Z",
|
||||||
|
"approve_comment": "",
|
||||||
|
"approve_date": "2021-09-03T09:13:12.614200Z",
|
||||||
|
"auto_approve_date": "2021-09-06T07:17:12.116114Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cancellation_id": 51956491,
|
||||||
|
"posting_number": "14094410-0018-1",
|
||||||
|
"cancellation_reason": {
|
||||||
|
"id": 507,
|
||||||
|
"name": "Покупатель передумал"
|
||||||
|
},
|
||||||
|
"cancelled_at": "2021-09-13T15:03:25.155827Z",
|
||||||
|
"cancellation_reason_message": "",
|
||||||
|
"tpl_integration_type": "ThirdPartyTracking",
|
||||||
|
"state": {
|
||||||
|
"id": 5,
|
||||||
|
"name": "Автоматически отменена",
|
||||||
|
"state": "REJECTED"
|
||||||
|
},
|
||||||
|
"cancellation_initiator": "CLIENT",
|
||||||
|
"order_date": "2021-09-13T07:48:50.143Z",
|
||||||
|
"approve_comment": "",
|
||||||
|
"approve_date": null,
|
||||||
|
"auto_approve_date": "2021-09-16T15:03:25.155827Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 19,
|
||||||
|
"counters": {
|
||||||
|
"on_approval": 0,
|
||||||
|
"approved": 14,
|
||||||
|
"rejected": 5
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ListCancellationsParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Cancellations().List(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApproveCancellations(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ApproveRejectCancellationsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ApproveRejectCancellationsParams{
|
||||||
|
CancellationId: 74393917,
|
||||||
|
},
|
||||||
|
`{}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ApproveRejectCancellationsParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Cancellations().Approve(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectCancellations(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ApproveRejectCancellationsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ApproveRejectCancellationsParams{
|
||||||
|
CancellationId: 74393917,
|
||||||
|
},
|
||||||
|
`{}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ApproveRejectCancellationsParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Cancellations().Reject(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
179
ozon/fbo.go
179
ozon/fbo.go
@@ -99,19 +99,7 @@ type GetFBOShipmentsListResponse struct {
|
|||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
||||||
// Financial data
|
// Financial data
|
||||||
FinancialData struct {
|
FinancialData FBOFinancialData `json:"financial_data"`
|
||||||
// Identifier of the cluster, where the shipment is sent from
|
|
||||||
ClusterFrom string `json:"cluster_from"`
|
|
||||||
|
|
||||||
// Identifier of the cluster, where the shipment is delivered to
|
|
||||||
ClusterTo string `json:"cluster_to"`
|
|
||||||
|
|
||||||
// Services
|
|
||||||
PostingServices MarketplaceServices `json:"posting_services"`
|
|
||||||
|
|
||||||
// Products list
|
|
||||||
Products []FinancialDataProduct `json:"products"`
|
|
||||||
} `json:"financial_data"`
|
|
||||||
|
|
||||||
// Date and time of shipment processing start
|
// Date and time of shipment processing start
|
||||||
InProccessAt time.Time `json:"in_process_at"`
|
InProccessAt time.Time `json:"in_process_at"`
|
||||||
@@ -126,36 +114,52 @@ type GetFBOShipmentsListResponse struct {
|
|||||||
PostingNumber string `json:"posting_number"`
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
// Number of products in the shipment
|
// Number of products in the shipment
|
||||||
Products []struct {
|
Products []FBOPostingProduct `json:"products"`
|
||||||
// Activation codes for services and digital products
|
|
||||||
DigitalCodes []string `json:"digital_codes"`
|
|
||||||
|
|
||||||
// Currency of your prices. It matches the currency set in the personal account settings
|
|
||||||
CurrencyCode string `json:"currency_code"`
|
|
||||||
|
|
||||||
// Product name
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Product identifier in the seller's system
|
|
||||||
OfferId string `json:"offer_id"`
|
|
||||||
|
|
||||||
// Product price
|
|
||||||
Price string `json:"price"`
|
|
||||||
|
|
||||||
// Quantity of products in the shipment
|
|
||||||
Quantity int64 `json:"quantity"`
|
|
||||||
|
|
||||||
// Product identifier in the Ozon system, SKU
|
|
||||||
SKU int64 `json:"sku"`
|
|
||||||
} `json:"products"`
|
|
||||||
|
|
||||||
// Shipment status
|
// Shipment status
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
} `json:"result"`
|
} `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FBOPostingProduct struct {
|
||||||
|
// Activation codes for services and digital products
|
||||||
|
DigitalCodes []string `json:"digital_codes"`
|
||||||
|
|
||||||
|
// Currency of your prices. It matches the currency set in the personal account settings
|
||||||
|
CurrencyCode string `json:"currency_code"`
|
||||||
|
|
||||||
|
// Product name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Product identifier in the seller's system
|
||||||
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
|
// Product price
|
||||||
|
Price string `json:"price"`
|
||||||
|
|
||||||
|
// Quantity of products in the shipment
|
||||||
|
Quantity int64 `json:"quantity"`
|
||||||
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FBOFinancialData struct {
|
||||||
|
// Identifier of the cluster, where the shipment is sent from
|
||||||
|
ClusterFrom string `json:"cluster_from"`
|
||||||
|
|
||||||
|
// Identifier of the cluster, where the shipment is delivered to
|
||||||
|
ClusterTo string `json:"cluster_to"`
|
||||||
|
|
||||||
|
// Services
|
||||||
|
PostingServices MarketplaceServices `json:"posting_services"`
|
||||||
|
|
||||||
|
// Products list
|
||||||
|
Products []FinancialDataProduct `json:"products"`
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a list of shipments for a specified period of time. You can additionally filter the shipments by their status
|
// Returns a list of shipments for a specified period of time. You can additionally filter the shipments by their status
|
||||||
func (c FBO) GetFBOShipmentsList(params *GetFBOShipmentsListParams) (*GetFBOShipmentsListResponse, error) {
|
func (c FBO) GetShipmentsList(params *GetFBOShipmentsListParams) (*GetFBOShipmentsListResponse, error) {
|
||||||
url := "/v2/posting/fbo/list"
|
url := "/v2/posting/fbo/list"
|
||||||
|
|
||||||
resp := &GetFBOShipmentsListResponse{}
|
resp := &GetFBOShipmentsListResponse{}
|
||||||
@@ -168,3 +172,106 @@ func (c FBO) GetFBOShipmentsList(params *GetFBOShipmentsListParams) (*GetFBOShip
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetShipmentDetailsParams struct{
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// true if the address transliteration from Cyrillic to Latin is enabled
|
||||||
|
Translit bool `json:"translit"`
|
||||||
|
|
||||||
|
// Additional fields to add to the response
|
||||||
|
With GetShipmentDetailsWith `json:"with"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetShipmentDetailsWith struct{
|
||||||
|
// Specify true to add analytics data to the response
|
||||||
|
AnalyticsData bool `json:"analytics_data"`
|
||||||
|
|
||||||
|
// Specify true to add financial data to the response
|
||||||
|
FinancialData bool `json:"financial_data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetShipmentDetailsResponse struct{
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result struct{
|
||||||
|
// Additional data
|
||||||
|
AdditionalData []struct{
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
} `json:"additional_data"`
|
||||||
|
|
||||||
|
// Analytical data
|
||||||
|
AnalyticsData struct{
|
||||||
|
// Delivery city
|
||||||
|
City string `json:"Delivery city"`
|
||||||
|
|
||||||
|
// Delivery method
|
||||||
|
DeliveryType string `json:"delivery_type"`
|
||||||
|
|
||||||
|
// Indication that the recipient is a legal person:
|
||||||
|
// - true — a legal person
|
||||||
|
// - false — a natural person
|
||||||
|
IsLegal bool `json:"is_legal"`
|
||||||
|
|
||||||
|
// Premium subscription
|
||||||
|
IsPremium bool `json:"is_premium"`
|
||||||
|
|
||||||
|
// Payment method
|
||||||
|
PaymentTypeGroupName string `json:"payment_type_group_name"`
|
||||||
|
|
||||||
|
// Delivery region
|
||||||
|
Region string `json:"region"`
|
||||||
|
|
||||||
|
// Warehouse identifier
|
||||||
|
WarehouseId int64 `json:"warehouse_id"`
|
||||||
|
|
||||||
|
// Name of the warehouse from which the order is shipped
|
||||||
|
WarehouseName string `json:"warehouse_name"`
|
||||||
|
} `json:"analytics_data"`
|
||||||
|
|
||||||
|
// Shipment cancellation reason identifier
|
||||||
|
CancelReasonId int64 `json:"cancel_reason_id"`
|
||||||
|
|
||||||
|
// Date and time of shipment creation
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
||||||
|
// Financial data
|
||||||
|
FinancialData FBOFinancialData `json:"financial_data"`
|
||||||
|
|
||||||
|
// Date and time of shipment processing start
|
||||||
|
InProcessAt time.Time `json:"in_process_at"`
|
||||||
|
|
||||||
|
// Identifier of the order to which the shipment belongs
|
||||||
|
OrderId int64 `json:"order_id"`
|
||||||
|
|
||||||
|
// Number of the order to which the shipment belongs
|
||||||
|
OrderNumber string `json:"order_number"`
|
||||||
|
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Number of products in the shipment
|
||||||
|
Products []FBOPostingProduct `json:"products"`
|
||||||
|
|
||||||
|
// Shipment status
|
||||||
|
Status string `json:"status"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns information about the shipment by its identifier
|
||||||
|
func (c FBO) GetShipmentDetails(params *GetShipmentDetailsParams) (*GetShipmentDetailsResponse, error) {
|
||||||
|
url := "/v2/posting/fbo/get"
|
||||||
|
|
||||||
|
resp := &GetShipmentDetailsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|||||||
136
ozon/fbo_test.go
136
ozon/fbo_test.go
@@ -133,7 +133,7 @@ func TestGetFBOShipmentsList(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
resp, err := c.FBO().GetFBOShipmentsList(test.params)
|
resp, err := c.FBO().GetShipmentsList(test.params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -143,3 +143,137 @@ func TestGetFBOShipmentsList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetShipmentDetails(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetShipmentDetailsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetShipmentDetailsParams{
|
||||||
|
PostingNumber: "50520644-0012-7",
|
||||||
|
Translit: true,
|
||||||
|
With: GetShipmentDetailsWith{
|
||||||
|
AnalyticsData: true,
|
||||||
|
FinancialData: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": {
|
||||||
|
"order_id": 354679434,
|
||||||
|
"order_number": "50520644-0012",
|
||||||
|
"posting_number": "50520644-0012-7",
|
||||||
|
"status": "delivered",
|
||||||
|
"cancel_reason_id": 0,
|
||||||
|
"created_at": "2021-09-01T00:34:56.563Z",
|
||||||
|
"in_process_at": "2021-09-01T00:34:56.103Z",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"sku": 254665483,
|
||||||
|
"name": "Мочалка натуральная из люфы с деревянной ручкой",
|
||||||
|
"quantity": 1,
|
||||||
|
"offer_id": "PS1033",
|
||||||
|
"price": "137.00",
|
||||||
|
"digital_codes": [],
|
||||||
|
"currency_code": "RUB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"analytics_data": {
|
||||||
|
"region": "МОСКВА",
|
||||||
|
"city": "Москва",
|
||||||
|
"delivery_type": "Courier",
|
||||||
|
"is_premium": false,
|
||||||
|
"payment_type_group_name": "Карты оплаты",
|
||||||
|
"warehouse_id": 15431806189000,
|
||||||
|
"warehouse_name": "ХОРУГВИНО_РФЦ",
|
||||||
|
"is_legal": false
|
||||||
|
},
|
||||||
|
"financial_data": {
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"commission_amount": 13.7,
|
||||||
|
"commission_percent": 10,
|
||||||
|
"payout": 123.3,
|
||||||
|
"product_id": 254665483,
|
||||||
|
"currency_code": "RUB",
|
||||||
|
"old_price": 198,
|
||||||
|
"price": 137,
|
||||||
|
"total_discount_value": 61,
|
||||||
|
"total_discount_percent": 30.81,
|
||||||
|
"actions": [
|
||||||
|
"Системная виртуальная скидка селлера"
|
||||||
|
],
|
||||||
|
"picking": null,
|
||||||
|
"quantity": 0,
|
||||||
|
"client_price": "",
|
||||||
|
"item_services": {
|
||||||
|
"marketplace_service_item_fulfillment": -31.5,
|
||||||
|
"marketplace_service_item_pickup": 0,
|
||||||
|
"marketplace_service_item_dropoff_pvz": 0,
|
||||||
|
"marketplace_service_item_dropoff_sc": 0,
|
||||||
|
"marketplace_service_item_dropoff_ff": 0,
|
||||||
|
"marketplace_service_item_direct_flow_trans": -5,
|
||||||
|
"marketplace_service_item_return_flow_trans": 0,
|
||||||
|
"marketplace_service_item_deliv_to_customer": -20,
|
||||||
|
"marketplace_service_item_return_not_deliv_to_customer": 0,
|
||||||
|
"marketplace_service_item_return_part_goods_customer": 0,
|
||||||
|
"marketplace_service_item_return_after_deliv_to_customer": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"posting_services": {
|
||||||
|
"marketplace_service_item_fulfillment": 0,
|
||||||
|
"marketplace_service_item_pickup": 0,
|
||||||
|
"marketplace_service_item_dropoff_pvz": 0,
|
||||||
|
"marketplace_service_item_dropoff_sc": 0,
|
||||||
|
"marketplace_service_item_dropoff_ff": 0,
|
||||||
|
"marketplace_service_item_direct_flow_trans": 0,
|
||||||
|
"marketplace_service_item_return_flow_trans": 0,
|
||||||
|
"marketplace_service_item_deliv_to_customer": 0,
|
||||||
|
"marketplace_service_item_return_not_deliv_to_customer": 0,
|
||||||
|
"marketplace_service_item_return_part_goods_customer": 0,
|
||||||
|
"marketplace_service_item_return_after_deliv_to_customer": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additional_data": []
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetShipmentDetailsParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.FBO().GetShipmentDetails(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
if resp.Result.PostingNumber != test.params.PostingNumber {
|
||||||
|
t.Errorf("Posting numbers in request and response are not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
139
ozon/finance.go
139
ozon/finance.go
@@ -246,3 +246,142 @@ func (c Finance) GetTotalTransactionsSum(params *GetTotalTransactionsSumParams)
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ListTransactionsParams struct{
|
||||||
|
// Filter
|
||||||
|
Filter ListTransactionsFilter `json:"filter"`
|
||||||
|
|
||||||
|
// Number of the page returned in the request
|
||||||
|
Page int64 `json:"page"`
|
||||||
|
|
||||||
|
// Number of items on the page
|
||||||
|
PageSize int64 `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListTransactionsFilter struct{
|
||||||
|
// Filter by date
|
||||||
|
Date ListTransactionsFilterDate `json:"date"`
|
||||||
|
|
||||||
|
// Operation type
|
||||||
|
OperationType string `json:"operation_type"`
|
||||||
|
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Transaction type
|
||||||
|
TransactionType string `json:"transaction_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListTransactionsFilterDate struct{
|
||||||
|
// Period start.
|
||||||
|
//
|
||||||
|
// Format: YYYY-MM-DDTHH:mm:ss.sssZ.
|
||||||
|
// Example: 2019-11-25T10:43:06.51
|
||||||
|
From time.Time `json:"from"`
|
||||||
|
|
||||||
|
// Period end.
|
||||||
|
//
|
||||||
|
// Format: YYYY-MM-DDTHH:mm:ss.sssZ.
|
||||||
|
// Example: 2019-11-25T10:43:06.51
|
||||||
|
To time.Time `json:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListTransactionsResponse struct{
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result struct{
|
||||||
|
// Transactions infromation
|
||||||
|
Operations []struct{
|
||||||
|
// Cost of the products with seller's discounts applied
|
||||||
|
AccrualsForSale float64 `json:"accruals_for_sale"`
|
||||||
|
|
||||||
|
// Total transaction sum
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
|
||||||
|
// Delivery cost for charges by rates that were in effect until February 1, 2021, and for charges for bulky products
|
||||||
|
DeliveryCharge float64 `json:"delivery_charge"`
|
||||||
|
|
||||||
|
// Product information
|
||||||
|
Items []struct{
|
||||||
|
// Product name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku"`
|
||||||
|
} `json:"items"`
|
||||||
|
|
||||||
|
// Operation date
|
||||||
|
OperationDate string `json:"operation_date"`
|
||||||
|
|
||||||
|
// Operation identifier
|
||||||
|
OperationId int64 `json:"operation_id"`
|
||||||
|
|
||||||
|
// Operation type
|
||||||
|
OperationType string `json:"operation_type"`
|
||||||
|
|
||||||
|
// Operation type name
|
||||||
|
OperationTypeName string `json:"operation_type_name"`
|
||||||
|
|
||||||
|
// Shipment information
|
||||||
|
Posting struct{
|
||||||
|
// Delivery scheme:
|
||||||
|
// - FBO — delivery to Ozon warehouse
|
||||||
|
// - FBS — delivery from seller's warehouse
|
||||||
|
// - RFBS — delivery service of seller's choice
|
||||||
|
// - Crossborder — delivery from abroad
|
||||||
|
DeliverySchema string `json:"delivery_schema"`
|
||||||
|
|
||||||
|
// Date the product was accepted for processing
|
||||||
|
OrderDate string `json:"order_date"`
|
||||||
|
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Warehouse identifier
|
||||||
|
WarehouseId int64 `json:"warehouse_id"`
|
||||||
|
} `json:"posting"`
|
||||||
|
|
||||||
|
// Returns and cancellation cost for charges by rates that were in effect until February 1, 2021, and for charges for bulky products
|
||||||
|
ReturnDeliveryCharge float64 `json:"return_delivery_charge"`
|
||||||
|
|
||||||
|
// Sales commission or sales commission refund
|
||||||
|
SaleCommission float64 `json:"sale_commission"`
|
||||||
|
|
||||||
|
// Additional services
|
||||||
|
Services []struct{
|
||||||
|
// Service name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Price
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
} `json:"services"`
|
||||||
|
|
||||||
|
// Transaction type
|
||||||
|
Type string `json:"type"`
|
||||||
|
} `json:"operations"`
|
||||||
|
|
||||||
|
// Number of pages
|
||||||
|
PageCount int64 `json:"page_count"`
|
||||||
|
|
||||||
|
// Number of products
|
||||||
|
RowCount int64 `json:"row_count"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns detailed information on all accruals. The maximum period for which you can get information in one request is 1 month.
|
||||||
|
//
|
||||||
|
// If you don't specify the posting_number in request, the response contains all shipments for the specified period or shipments of a certain type
|
||||||
|
func (c Finance) ListTransactions(params *ListTransactionsParams) (*ListTransactionsResponse, error) {
|
||||||
|
url := "/v3/finance/transaction/list"
|
||||||
|
|
||||||
|
resp := &ListTransactionsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -174,3 +174,91 @@ func TestGetTotalTransactionsSum(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListTransactions(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ListTransactionsParams
|
||||||
|
response string
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ListTransactionsParams{
|
||||||
|
Filter: ListTransactionsFilter{
|
||||||
|
Date: ListTransactionsFilterDate{
|
||||||
|
From: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-01T00:00:00.000Z"),
|
||||||
|
To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-02T00:00:00.000Z"),
|
||||||
|
},
|
||||||
|
TransactionType: "ALL",
|
||||||
|
},
|
||||||
|
Page: 1,
|
||||||
|
PageSize: 1000,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": {
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"operation_id": 11401182187840,
|
||||||
|
"operation_type": "MarketplaceMarketingActionCostOperation",
|
||||||
|
"operation_date": "2021-11-01 00:00:00",
|
||||||
|
"operation_type_name": "Услуги продвижения товаров",
|
||||||
|
"delivery_charge": 0,
|
||||||
|
"return_delivery_charge": 0,
|
||||||
|
"accruals_for_sale": 0,
|
||||||
|
"sale_commission": 0,
|
||||||
|
"amount": -6.46,
|
||||||
|
"type": "services",
|
||||||
|
"posting": {
|
||||||
|
"delivery_schema": "",
|
||||||
|
"order_date": "",
|
||||||
|
"posting_number": "",
|
||||||
|
"warehouse_id": 0
|
||||||
|
},
|
||||||
|
"items": [],
|
||||||
|
"services": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"page_count": 1,
|
||||||
|
"row_count": 355
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ListTransactionsParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
"Client-Id and Api-Key headers are required",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Finance().ListTransactions(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
if resp.Message != test.errorMessage {
|
||||||
|
t.Errorf("got wrong error message: got: %s, expected: %s", resp.Message, test.errorMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
71
ozon/ozon.go
71
ozon/ozon.go
@@ -13,16 +13,17 @@ const (
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
client *core.Client
|
client *core.Client
|
||||||
|
|
||||||
analytics *Analytics
|
analytics *Analytics
|
||||||
fbo *FBO
|
fbo *FBO
|
||||||
fbs *FBS
|
fbs *FBS
|
||||||
finance *Finance
|
finance *Finance
|
||||||
products *Products
|
products *Products
|
||||||
promotions *Promotions
|
promotions *Promotions
|
||||||
rating *Rating
|
rating *Rating
|
||||||
warehouses *Warehouses
|
warehouses *Warehouses
|
||||||
returns *Returns
|
returns *Returns
|
||||||
reports *Reports
|
reports *Reports
|
||||||
|
cancellations *Cancellations
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) Analytics() *Analytics {
|
func (c Client) Analytics() *Analytics {
|
||||||
@@ -65,6 +66,10 @@ func (c Client) Reports() *Reports {
|
|||||||
return c.reports
|
return c.reports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Client) Cancellations() *Cancellations {
|
||||||
|
return c.cancellations
|
||||||
|
}
|
||||||
|
|
||||||
func NewClient(clientId, apiKey string) *Client {
|
func NewClient(clientId, apiKey string) *Client {
|
||||||
coreClient := core.NewClient(DefaultAPIBaseUrl, map[string]string{
|
coreClient := core.NewClient(DefaultAPIBaseUrl, map[string]string{
|
||||||
"Client-Id": clientId,
|
"Client-Id": clientId,
|
||||||
@@ -72,17 +77,18 @@ func NewClient(clientId, apiKey string) *Client {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
client: coreClient,
|
client: coreClient,
|
||||||
analytics: &Analytics{client: coreClient},
|
analytics: &Analytics{client: coreClient},
|
||||||
fbo: &FBO{client: coreClient},
|
fbo: &FBO{client: coreClient},
|
||||||
fbs: &FBS{client: coreClient},
|
fbs: &FBS{client: coreClient},
|
||||||
finance: &Finance{client: coreClient},
|
finance: &Finance{client: coreClient},
|
||||||
products: &Products{client: coreClient},
|
products: &Products{client: coreClient},
|
||||||
promotions: &Promotions{client: coreClient},
|
promotions: &Promotions{client: coreClient},
|
||||||
rating: &Rating{client: coreClient},
|
rating: &Rating{client: coreClient},
|
||||||
warehouses: &Warehouses{client: coreClient},
|
warehouses: &Warehouses{client: coreClient},
|
||||||
returns: &Returns{client: coreClient},
|
returns: &Returns{client: coreClient},
|
||||||
reports: &Reports{client: coreClient},
|
reports: &Reports{client: coreClient},
|
||||||
|
cancellations: &Cancellations{client: coreClient},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,16 +96,17 @@ func NewMockClient(handler http.HandlerFunc) *Client {
|
|||||||
coreClient := core.NewMockClient(handler)
|
coreClient := core.NewMockClient(handler)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
client: coreClient,
|
client: coreClient,
|
||||||
analytics: &Analytics{client: coreClient},
|
analytics: &Analytics{client: coreClient},
|
||||||
fbo: &FBO{client: coreClient},
|
fbo: &FBO{client: coreClient},
|
||||||
fbs: &FBS{client: coreClient},
|
fbs: &FBS{client: coreClient},
|
||||||
finance: &Finance{client: coreClient},
|
finance: &Finance{client: coreClient},
|
||||||
products: &Products{client: coreClient},
|
products: &Products{client: coreClient},
|
||||||
promotions: &Promotions{client: coreClient},
|
promotions: &Promotions{client: coreClient},
|
||||||
rating: &Rating{client: coreClient},
|
rating: &Rating{client: coreClient},
|
||||||
warehouses: &Warehouses{client: coreClient},
|
warehouses: &Warehouses{client: coreClient},
|
||||||
returns: &Returns{client: coreClient},
|
returns: &Returns{client: coreClient},
|
||||||
reports: &Reports{client: coreClient},
|
reports: &Reports{client: coreClient},
|
||||||
|
cancellations: &Cancellations{client: coreClient},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -472,3 +472,86 @@ func (c Reports) GetShipment(params *GetShipmentReportParams) (*GetShipmentRepor
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IssueOnDiscountedProductsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Unique report identifier
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a report on discounted products in Ozon warehouses.
|
||||||
|
// For example, Ozon can discount a product due to damage when delivering.
|
||||||
|
//
|
||||||
|
// Returns report identifier. To get the report, send the identifier in the request body of a method `/v1/report/discounted/info`
|
||||||
|
func (c Reports) IssueOnDiscountedProducts() (*IssueOnDiscountedProductsResponse, error) {
|
||||||
|
url := "/v1/report/discounted/create"
|
||||||
|
|
||||||
|
resp := &IssueOnDiscountedProductsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReportOnDiscountedProductsParams struct {
|
||||||
|
// Unique report identifier
|
||||||
|
Code string `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReportOnDiscountedProductsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Report information
|
||||||
|
Report struct {
|
||||||
|
// Report creation date
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
||||||
|
// Link to report file
|
||||||
|
File string `json:"file"`
|
||||||
|
|
||||||
|
// Report status:
|
||||||
|
// - success — created
|
||||||
|
// - pending — waiting to be processed
|
||||||
|
// - processing — processed
|
||||||
|
// - failed — generation error
|
||||||
|
Status string `json:"status"`
|
||||||
|
|
||||||
|
// Report generation error code
|
||||||
|
Error string `json:"error"`
|
||||||
|
} `json:"report"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// By report identifier, returns information about the report generated earlier
|
||||||
|
func (c Reports) ReportOnDiscountedProducts(params *ReportOnDiscountedProductsParams) (*ReportOnDiscountedProductsResponse, error) {
|
||||||
|
url := "/v1/report/discounted/info"
|
||||||
|
|
||||||
|
resp := &ReportOnDiscountedProductsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// By report identifier, returns information about the report generated earlier
|
||||||
|
func (c Reports) ListReportsOnDiscountedProducts() (*ReportOnDiscountedProductsResponse, error) {
|
||||||
|
url := "/v1/report/discounted/list"
|
||||||
|
|
||||||
|
resp := &ReportOnDiscountedProductsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(http.MethodPost, url, nil, resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -495,3 +495,149 @@ func TestGetShipmentReport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssueOnDiscountedProducts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
`{
|
||||||
|
"code": "d55f4517-8347-4e24-9d93-d6e736c1c07c"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Reports().IssueOnDiscountedProducts()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
if resp.Code == "" {
|
||||||
|
t.Errorf("Code cannot be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReportOnDiscountedProducts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ReportOnDiscountedProductsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ReportOnDiscountedProductsParams{
|
||||||
|
Code: "d55f4517-8347-4e24-9d93-d6e736c1c07c",
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"report": {
|
||||||
|
"created_at": "2022-10-04T10:07:08.146Z",
|
||||||
|
"error": "string",
|
||||||
|
"file": "string",
|
||||||
|
"status": "string"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ReportOnDiscountedProductsParams{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Reports().ReportOnDiscountedProducts(test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListReportsOnDiscountedProducts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
`{
|
||||||
|
"reports": [
|
||||||
|
{
|
||||||
|
"created_at": "2022-10-04T10:07:08.146Z",
|
||||||
|
"error": "string",
|
||||||
|
"file": "string",
|
||||||
|
"status": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
resp, err := c.Reports().ListReportsOnDiscountedProducts()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user