Update November 6, 2024 (#115)
This commit is contained in:
74
ozon/clusters.go
Normal file
74
ozon/clusters.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
)
|
||||
|
||||
type Clusters struct {
|
||||
client *core.Client
|
||||
}
|
||||
|
||||
type ListClustersParams struct {
|
||||
// Clusters identifiers
|
||||
ClusterIds []string `json:"cluster_ids"`
|
||||
|
||||
// Cluster type
|
||||
ClusterType string `json:"cluster_type"`
|
||||
}
|
||||
|
||||
type ListClustersResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Cluster details
|
||||
Clusters []Cluster `json:"clusters"`
|
||||
}
|
||||
|
||||
type Cluster struct {
|
||||
// Cluster identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Cluster warehouse details
|
||||
LogisticClusters []LogisticCluster `json:"logistic_clusters"`
|
||||
|
||||
// Cluster name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Cluster type
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LogisticCluster struct {
|
||||
// Warehouse status
|
||||
IsArchived bool `json:"is_archived"`
|
||||
|
||||
// Warehouses
|
||||
Warehouses []LogisticClusterWarehouse `json:"warehouses"`
|
||||
}
|
||||
|
||||
type LogisticClusterWarehouse struct {
|
||||
// Warehouse name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Warehouse type
|
||||
Type string `json:"type"`
|
||||
|
||||
// Warehouse identifier
|
||||
Id int64 `json:"warehouse_id"`
|
||||
}
|
||||
|
||||
func (c Clusters) List(ctx context.Context, params *ListClustersParams) (*ListClustersResponse, error) {
|
||||
url := "/v1/cluster/list"
|
||||
|
||||
resp := &ListClustersResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
78
ozon/clusters_test.go
Normal file
78
ozon/clusters_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListClusters(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListClustersParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListClustersParams{
|
||||
ClusterIds: []string{"string"},
|
||||
ClusterType: "CLUSTER_TYPE_UNKNOWN",
|
||||
},
|
||||
`{
|
||||
"clusters": [
|
||||
{
|
||||
"id": 0,
|
||||
"logistic_clusters": [
|
||||
{
|
||||
"is_archived": true,
|
||||
"warehouses": [
|
||||
{
|
||||
"name": "string",
|
||||
"type": "FULL_FILLMENT",
|
||||
"warehouse_id": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"type": "CLUSTER_TYPE_UNKNOWN"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListClustersParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Clusters().List(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListClustersResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
278
ozon/fbo.go
278
ozon/fbo.go
@@ -872,3 +872,281 @@ func (c FBO) GetSupplyContent(ctx context.Context, params *GetSupplyContentParam
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type CreateSupplyDraftParams struct {
|
||||
// Clusters identifiers
|
||||
ClusterIds []string `json:"cluster_ids"`
|
||||
|
||||
// Shipping point identifier: pick-up point or sorting center. Only for the type = CREATE_TYPE_CROSSDOCK supply type.
|
||||
DropoffWarehouseId int64 `json:"drop_off_point_warehouse_id"`
|
||||
|
||||
// Products
|
||||
Items []CreateSupplyDraftItem `json:"items"`
|
||||
|
||||
// Supply type
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type CreateSupplyDraftItem struct {
|
||||
// Product quantity
|
||||
Quantity int32 `json:"quantity"`
|
||||
|
||||
// Product identifier
|
||||
SKU int64 `json:"sku"`
|
||||
}
|
||||
|
||||
type CreateSupplyDraftResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Identifier of the supply request draft
|
||||
OperationId string `json:"operation_id"`
|
||||
}
|
||||
|
||||
// Create a direct or cross-docking supply request draft and specify the products to supply.
|
||||
//
|
||||
// You can leave feedback on this method in the comments section to the discussion in the Ozon for dev community
|
||||
func (c FBO) CreateSupplyDraft(ctx context.Context, params *CreateSupplyDraftParams) (*CreateSupplyDraftResponse, error) {
|
||||
url := "/v1/draft/create"
|
||||
|
||||
resp := &CreateSupplyDraftResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodGet, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetSupplyDraftInfoParams struct {
|
||||
// Identifier of the supply request draft
|
||||
OperationId string `json:"operation_id"`
|
||||
}
|
||||
|
||||
type GetSupplyDraftInfoResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Clusters
|
||||
Clusters []SupplyDraftCluster `json:"clusters"`
|
||||
|
||||
// Identifier of the supply request draft
|
||||
DraftId int64 `json:"draft_id"`
|
||||
|
||||
// Errors
|
||||
Errors []GetSupplyDraftInfoError `json:"errors"`
|
||||
|
||||
// Creation status of the supply request draft
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type SupplyDraftCluster struct {
|
||||
// Cluster identifier
|
||||
Id int64 `json:"cluster_id"`
|
||||
|
||||
// Cluster name
|
||||
Name string `json:"cluster_name"`
|
||||
|
||||
// Warehouses
|
||||
Warehouses []SupplyDraftWarehouse `json:"warehouses"`
|
||||
}
|
||||
|
||||
type SupplyDraftWarehouse struct {
|
||||
// Warehouse address
|
||||
Address string `json:"address"`
|
||||
|
||||
// Product list bundle
|
||||
BundleIds []SupplyDraftWarehouseBundle `json:"bundle_ids"`
|
||||
|
||||
// Warehouse name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Bundle of products that don't come in a shipment
|
||||
RestrictedBundleId string `json:"restricted_bundle_id"`
|
||||
|
||||
// Warehouse availability
|
||||
Status SupplyDraftWarehouseStatus `json:"status"`
|
||||
|
||||
// Supply warehouses
|
||||
SupplyWarehouse SupplyWarehouse `json:"supply_warehouse"`
|
||||
|
||||
// Warehouse rank in the cluster
|
||||
TotalRank int32 `json:"total_rank"`
|
||||
|
||||
// Warehouse rating
|
||||
TotalScore float64 `json:"total_score"`
|
||||
|
||||
// Estimated delivery time
|
||||
//
|
||||
// Nullable
|
||||
TravelTimeDays *int64 `json:"travel_time_days"`
|
||||
|
||||
// Warehouse identifier
|
||||
Id int64 `json:"warehouse_id"`
|
||||
}
|
||||
|
||||
type SupplyDraftWarehouseBundle struct {
|
||||
// Bundle identifier
|
||||
Id string `json:"bundle_id"`
|
||||
|
||||
// Indicates that the UTD is to be passed
|
||||
IsDocless bool `json:"is_docless"`
|
||||
}
|
||||
|
||||
type SupplyDraftWarehouseStatus struct {
|
||||
// Reason why the warehouse isn't available
|
||||
InvalidReason string `json:"invalid_reason"`
|
||||
|
||||
// Warehouse availability
|
||||
IsAvailable bool `json:"is_available"`
|
||||
|
||||
// Warehouse status
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
type GetSupplyDraftInfoError struct {
|
||||
// Possible errors
|
||||
Message string `json:"error_message"`
|
||||
|
||||
// Validation errors
|
||||
ItemsValidation []GetSupplyDraftInfoValidationError `json:"items_validation"`
|
||||
|
||||
// Unknown clusters identifiers
|
||||
UnknownClusterIds []string `json:"unknown_cluster_ids"`
|
||||
}
|
||||
|
||||
type GetSupplyDraftInfoValidationError struct {
|
||||
// Error reasons
|
||||
Reasons []string `json:"reasons"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
}
|
||||
|
||||
func (c FBO) GetSupplyDraftInfo(ctx context.Context, params *GetSupplyDraftInfoParams) (*GetSupplyDraftInfoResponse, error) {
|
||||
url := "/v1/draft/create/info"
|
||||
|
||||
resp := &GetSupplyDraftInfoResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodGet, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type CreateSupplyFromDraftParams struct {
|
||||
// Identifier of the supply request draft
|
||||
DraftId int64 `json:"draft_id"`
|
||||
|
||||
// Supply time slot
|
||||
Timeslot CreateSupplyFromDraftTimeslot `json:"timeslot"`
|
||||
|
||||
// Shipping warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
}
|
||||
|
||||
type CreateSupplyFromDraftTimeslot struct {
|
||||
// Supply time slot start date
|
||||
FromInTimezone time.Time `json:"from_in_timezone"`
|
||||
|
||||
// Supply time slot end date
|
||||
ToInTimezone time.Time `json:"to_in_timezone"`
|
||||
}
|
||||
|
||||
type CreateSupplyFromDraftResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Supply request identifier
|
||||
OperationId string `json:"operation_id"`
|
||||
}
|
||||
|
||||
func (c FBO) CreateSupplyFromDraft(ctx context.Context, params *CreateSupplyFromDraftParams) (*CreateSupplyFromDraftResponse, error) {
|
||||
url := "/v1/draft/supply/create"
|
||||
|
||||
resp := &CreateSupplyFromDraftResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodGet, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetDraftTimeslotsParams struct {
|
||||
// Start date of the available supply time slots period
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
|
||||
// End date of the available supply time slots period
|
||||
//
|
||||
// The maximum period is 28 days from the current date
|
||||
DateTo time.Time `json:"date_to"`
|
||||
|
||||
// Identifier of the supply request draft
|
||||
DraftId int64 `json:"draft_id"`
|
||||
|
||||
// The warehouses identifiers for which supply time slots are required
|
||||
WarehouseIds []string `json:"warehouse_ids"`
|
||||
}
|
||||
|
||||
type GetDraftTimeslotsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Warehouses supply time slots
|
||||
DropoffWarehouseTimeslots []DraftTimeslot `json:"drop_off_warehouse_timeslots"`
|
||||
|
||||
// Start date of the necessary period
|
||||
RequestedDateFrom time.Time `json:"requested_date_from"`
|
||||
|
||||
// End date of the necessary period
|
||||
RequestedDateTo time.Time `json:"requested_date_to"`
|
||||
}
|
||||
|
||||
type DraftTimeslot struct {
|
||||
// Current time in the warehouse time zone
|
||||
CurrentTimeInTimezone time.Time `json:"current_time_in_timezone"`
|
||||
|
||||
// Supply time slots by dates
|
||||
Days []DraftTimeslotDay `json:"days"`
|
||||
|
||||
// Warehouse identifier
|
||||
DropoffWarehouseId int64 `json:"drop_off_warehouse_id"`
|
||||
|
||||
// Warehouse time zone
|
||||
WarehouseTimezone string `json:"warehouse_timezone"`
|
||||
}
|
||||
|
||||
type DraftTimeslotDay struct {
|
||||
// Supply time slots date
|
||||
DateInTimezone time.Time `json:"date_in_timezone"`
|
||||
|
||||
// Supply time slots details
|
||||
Timeslots []DraftTimeslotDayTimeslot `json:"timeslots"`
|
||||
}
|
||||
|
||||
type DraftTimeslotDayTimeslot struct {
|
||||
// Supply time slot start date
|
||||
FromInTimezone time.Time `json:"from_in_timezone"`
|
||||
|
||||
// Supply time slot end date
|
||||
ToInTimezone time.Time `json:"to_in_timezone"`
|
||||
}
|
||||
|
||||
// Available supply time slots at final shipping warehouses
|
||||
func (c FBO) GetDraftTimeslots(ctx context.Context, params *GetDraftTimeslotsParams) (*GetDraftTimeslotsResponse, error) {
|
||||
url := "/v1/draft/timeslot/info"
|
||||
|
||||
resp := &GetDraftTimeslotsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodGet, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
285
ozon/fbo_test.go
285
ozon/fbo_test.go
@@ -989,3 +989,288 @@ func TestGetSupplyContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSupplyDraft(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *CreateSupplyDraftParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&CreateSupplyDraftParams{
|
||||
ClusterIds: []string{"string"},
|
||||
DropoffWarehouseId: 0,
|
||||
Items: []CreateSupplyDraftItem{
|
||||
{
|
||||
Quantity: 1,
|
||||
SKU: 11,
|
||||
},
|
||||
},
|
||||
Type: "CREATE_TYPE_CROSSDOCK",
|
||||
},
|
||||
`{
|
||||
"operation_id": "string"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&CreateSupplyDraftParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBO().CreateSupplyDraft(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &CreateSupplyDraftResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSupplyDraftInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetSupplyDraftInfoParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetSupplyDraftInfoParams{
|
||||
OperationId: "string",
|
||||
},
|
||||
`{
|
||||
"clusters": [
|
||||
{
|
||||
"cluster_id": 0,
|
||||
"cluster_name": "string",
|
||||
"warehouses": [
|
||||
{
|
||||
"address": "string",
|
||||
"bundle_ids": [
|
||||
{
|
||||
"bundle_id": "string",
|
||||
"is_docless": true
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"restricted_bundle_id": "string",
|
||||
"status": {
|
||||
"invalid_reason": "WAREHOUSE_SCORING_INVALID_REASON_UNSPECIFIED",
|
||||
"is_available": true,
|
||||
"state": "WAREHOUSE_SCORING_STATUS_FULL_AVAILABLE"
|
||||
},
|
||||
"supply_warehouse": {
|
||||
"address": "string",
|
||||
"name": "string",
|
||||
"warehouse_id": 0
|
||||
},
|
||||
"total_rank": 0,
|
||||
"total_score": 0,
|
||||
"travel_time_days": 0,
|
||||
"warehouse_id": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"draft_id": 0,
|
||||
"errors": [
|
||||
{
|
||||
"error_message": "string",
|
||||
"items_validation": [
|
||||
{
|
||||
"reasons": [
|
||||
"string"
|
||||
],
|
||||
"sku": 0
|
||||
}
|
||||
],
|
||||
"unknown_cluster_ids": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "CALCULATION_STATUS_FAILED"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetSupplyDraftInfoParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBO().GetSupplyDraftInfo(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetSupplyDraftInfoResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSupplyFromDraft(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *CreateSupplyFromDraftParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&CreateSupplyFromDraftParams{
|
||||
DraftId: 11,
|
||||
Timeslot: CreateSupplyFromDraftTimeslot{
|
||||
FromInTimezone: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
ToInTimezone: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
},
|
||||
WarehouseId: 45,
|
||||
},
|
||||
`{
|
||||
"operation_id": "string"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&CreateSupplyFromDraftParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBO().CreateSupplyFromDraft(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &CreateSupplyFromDraftResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDraftTimeslots(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetDraftTimeslotsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetDraftTimeslotsParams{
|
||||
DraftId: 123,
|
||||
DateFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
DateTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
WarehouseIds: []string{"ddd456"},
|
||||
},
|
||||
`{
|
||||
"drop_off_warehouse_timeslots": [
|
||||
{
|
||||
"current_time_in_timezone": "2019-08-24T14:15:22Z",
|
||||
"days": [
|
||||
{
|
||||
"date_in_timezone": "2019-08-24T14:15:22Z",
|
||||
"timeslots": [
|
||||
{
|
||||
"from_in_timezone": "2019-08-24T14:15:22Z",
|
||||
"to_in_timezone": "2019-08-24T14:15:22Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"drop_off_warehouse_id": 0,
|
||||
"warehouse_timezone": "string"
|
||||
}
|
||||
],
|
||||
"requested_date_from": "2019-08-24T14:15:22Z",
|
||||
"requested_date_to": "2019-08-24T14:15:22Z"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetDraftTimeslotsParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBO().GetDraftTimeslots(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetDraftTimeslotsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
ozon/fbs.go
10
ozon/fbs.go
@@ -53,6 +53,11 @@ type ListUnprocessedShipmentsFilter struct {
|
||||
// Delivery method identifier
|
||||
DeliveryMethodId []int64 `json:"delivery_method_id"`
|
||||
|
||||
// Specify true to get only MOQ shipments.
|
||||
//
|
||||
// The default value is false, the response contains all shipments
|
||||
IsQuantum bool `json:"is_quantum"`
|
||||
|
||||
// Filter for shipments delivered from partner warehouse (FBP). You can pass one of the following values:
|
||||
//
|
||||
// Default value is all.
|
||||
@@ -544,6 +549,11 @@ type GetFBSShipmentsListFilter struct {
|
||||
// Order identifier
|
||||
OrderId int64 `json:"order_id"`
|
||||
|
||||
// Specify true to get only MOQ shipments.
|
||||
//
|
||||
// The default value is false, the response contains all shipments
|
||||
IsQuantum bool `json:"is_quantum"`
|
||||
|
||||
// Delivery service identifier
|
||||
ProviderId []int64 `json:"provider_id"`
|
||||
|
||||
|
||||
@@ -404,3 +404,57 @@ func (c Finance) ListTransactions(ctx context.Context, params *ListTransactionsP
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetReportParams struct {
|
||||
// Time period in the YYYY-MM format
|
||||
Date string `json:"date"`
|
||||
|
||||
// Response language
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
type ReportResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result ReportResult `json:"result"`
|
||||
}
|
||||
|
||||
type ReportResult struct {
|
||||
// Unique report identifier
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// Use the method to get mutual settlements report.
|
||||
// Matches the Finance → Documents → Analytical reports → Mutual
|
||||
// settlements report section in your personal account.
|
||||
func (c Finance) MutualSettlements(ctx context.Context, params *GetReportParams) (*ReportResponse, error) {
|
||||
url := "/v1/finance/mutual-settlement"
|
||||
|
||||
resp := &ReportResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Use the method to get sales to legal entities report.
|
||||
// Matches the Finance → Documents → Legal
|
||||
// entities sales register section in your personal account.
|
||||
func (c Finance) SalesToLegalEntities(ctx context.Context, params *GetReportParams) (*ReportResponse, error) {
|
||||
url := "/v1/finance/mutual-settlement"
|
||||
|
||||
resp := &ReportResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -271,3 +271,127 @@ func TestListTransactions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutualSettlements(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetReportParams
|
||||
response string
|
||||
errorMessage string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetReportParams{
|
||||
Date: "2024-08",
|
||||
Language: "DEFAULT",
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"code": "string"
|
||||
}
|
||||
}`,
|
||||
"",
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetReportParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Finance().MutualSettlements(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ReportResponse{})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSalesToLegalEntities(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetReportParams
|
||||
response string
|
||||
errorMessage string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetReportParams{
|
||||
Date: "2024-08",
|
||||
Language: "DEFAULT",
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"code": "string"
|
||||
}
|
||||
}`,
|
||||
"",
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetReportParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Finance().SalesToLegalEntities(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ReportResponse{})
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
ozon/ozon.go
14
ozon/ozon.go
@@ -42,6 +42,8 @@ type Client struct {
|
||||
strategies *Strategies
|
||||
barcodes *Barcodes
|
||||
passes *Passes
|
||||
clusters *Clusters
|
||||
quants *Quants
|
||||
}
|
||||
|
||||
func (c Client) Analytics() *Analytics {
|
||||
@@ -124,6 +126,14 @@ func (c Client) Passes() *Passes {
|
||||
return c.passes
|
||||
}
|
||||
|
||||
func (c Client) Clusters() *Clusters {
|
||||
return c.clusters
|
||||
}
|
||||
|
||||
func (c Client) Quants() *Quants {
|
||||
return c.quants
|
||||
}
|
||||
|
||||
type ClientOption func(c *ClientOptions)
|
||||
|
||||
func WithHttpClient(httpClient core.HttpClient) ClientOption {
|
||||
@@ -188,6 +198,8 @@ func NewClient(opts ...ClientOption) *Client {
|
||||
strategies: &Strategies{client: coreClient},
|
||||
barcodes: &Barcodes{client: coreClient},
|
||||
passes: &Passes{client: coreClient},
|
||||
clusters: &Clusters{client: coreClient},
|
||||
quants: &Quants{client: coreClient},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,5 +228,7 @@ func NewMockClient(handler http.HandlerFunc) *Client {
|
||||
strategies: &Strategies{client: coreClient},
|
||||
barcodes: &Barcodes{client: coreClient},
|
||||
passes: &Passes{client: coreClient},
|
||||
clusters: &Clusters{client: coreClient},
|
||||
quants: &Quants{client: coreClient},
|
||||
}
|
||||
}
|
||||
|
||||
206
ozon/products.go
206
ozon/products.go
@@ -525,6 +525,14 @@ type UpdateQuantityStockProductsStock struct {
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Use parameter if the regular and economy products have the same article code—offer_id = quant_id. To update quantity of the:
|
||||
//
|
||||
// - regular product, pass the 1 value;
|
||||
// - economy product, pass the size of its MOQ.
|
||||
//
|
||||
// If the regular and economy products have different article codes, don't specify the parameter.
|
||||
QuantSize int64 `json:"quant_size"`
|
||||
|
||||
// Quantity
|
||||
Stock int64 `json:"stock"`
|
||||
|
||||
@@ -549,6 +557,12 @@ type UpdateQuantityStockProductsResult struct {
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Shows the quantity of which product type you are updating:
|
||||
//
|
||||
// - 1, if you are updating the stock of a regular product
|
||||
// - MOQ size, if you are updating the stock of economy product
|
||||
QuantSize int64 `json:"quant_size"`
|
||||
|
||||
// If the request was completed successfully and the stocks are updated — true
|
||||
Updated bool `json:"updated"`
|
||||
|
||||
@@ -686,6 +700,14 @@ type UpdatePricesPrice struct {
|
||||
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Use parameter if the regular and economy products have the same article code—offer_id = quant_id. To update price of the:
|
||||
//
|
||||
// - regular product, pass the 1 value;
|
||||
// - economy product, pass the size of its MOQ.
|
||||
//
|
||||
// If the regular and economy products have different article codes, don't specify the parameter.
|
||||
QuantSize int64 `json:"quant_size"`
|
||||
}
|
||||
|
||||
type UpdatePricesResponse struct {
|
||||
@@ -964,6 +986,10 @@ type GetListOfProductsResultItem struct {
|
||||
ProductId int64 `json:"product_id"`
|
||||
}
|
||||
|
||||
// When using the filter by offer_id or product_id identifier, other parameters are not required.
|
||||
// Only one identifiers group can be used at a time, not more than 1000 products.
|
||||
//
|
||||
// If you do not use identifiers for display, specify limit and last_id in subsequent requests.
|
||||
func (c Products) GetListOfProducts(ctx context.Context, params *GetListOfProductsParams) (*GetListOfProductsResponse, error) {
|
||||
url := "/v2/product/list"
|
||||
|
||||
@@ -2348,3 +2374,183 @@ func (c Products) GetRelatedSKUs(ctx context.Context, params *GetRelatedSKUsPara
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetEconomyInfoParams struct {
|
||||
// List of MOQs with products
|
||||
QuantCode []string `json:"quant_code"`
|
||||
}
|
||||
|
||||
type GetEconomyInfoResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Economy products
|
||||
Items []EconomyInfoItem `json:"items"`
|
||||
}
|
||||
|
||||
type EconomyInfoItem struct {
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// MOQ information
|
||||
QuantInfo EconomyInfoItemQuants `json:"quant_info"`
|
||||
}
|
||||
|
||||
type EconomyInfoItemQuants struct {
|
||||
Quants []EconomyInfoItemQuant `json:"quants"`
|
||||
}
|
||||
|
||||
type EconomyInfoItemQuant struct {
|
||||
// Barcodes information
|
||||
BarcodesExtended []EconomyInfoItemQuantBarcode `json:"barcodes_extended"`
|
||||
|
||||
// Dimensions
|
||||
Dimensions DimensionsMM `json:"dimensions"`
|
||||
|
||||
// Marketing prices
|
||||
MarketingPrice EconomyInfoItemQuantMarketingPrice `json:"marketing_price"`
|
||||
|
||||
// Minimum price specified by the seller
|
||||
MinPrice string `json:"min_price"`
|
||||
|
||||
// The strikethrough price specified by the seller
|
||||
OldPrice string `json:"old_price"`
|
||||
|
||||
// The selling price specified by the seller
|
||||
Price string `json:"price"`
|
||||
|
||||
// Economy product identifier
|
||||
QuantCode string `json:"quant_code"`
|
||||
|
||||
// MOQ size
|
||||
QuantSize int64 `json:"quant_sice"`
|
||||
|
||||
// Product delivery type
|
||||
ShipmentType string `json:"shipment_type"`
|
||||
|
||||
// Product SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Statuses descriptions
|
||||
Statuses EconomyInfoItemQuantStatus `json:"statuses"`
|
||||
}
|
||||
|
||||
type EconomyInfoItemQuantBarcode struct {
|
||||
// Barcode
|
||||
Barcode string `json:"barcode"`
|
||||
|
||||
// Error when receiving the barcode
|
||||
Error string `json:"error"`
|
||||
|
||||
// Barcode status
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type DimensionsMM struct {
|
||||
// Depth, mm
|
||||
Depth int64 `json:"depth"`
|
||||
|
||||
// Height, mm
|
||||
Height int64 `json:"height"`
|
||||
|
||||
// Weight, g
|
||||
Weight int64 `json:"weight"`
|
||||
|
||||
// Width, mm
|
||||
Width int64 `json:"width"`
|
||||
}
|
||||
|
||||
type EconomyInfoItemQuantMarketingPrice struct {
|
||||
// Selling price
|
||||
Price string `json:"price"`
|
||||
|
||||
// Price specified by the seller
|
||||
SellerPrice string `json:"seller_price"`
|
||||
}
|
||||
|
||||
type EconomyInfoItemQuantStatus struct {
|
||||
// Status description
|
||||
StateDescription string `json:"state_description"`
|
||||
|
||||
// Status name
|
||||
StateName string `json:"state_name"`
|
||||
|
||||
// System name of the status
|
||||
StateSysName string `json:"state_sys_name"`
|
||||
|
||||
// Tooltip with current product status details
|
||||
StateTooltip string `json:"state_tooltip"`
|
||||
}
|
||||
|
||||
func (c Products) EconomyInfo(ctx context.Context, params *GetEconomyInfoParams) (*GetEconomyInfoResponse, error) {
|
||||
url := "/v1/product/quant/info"
|
||||
|
||||
resp := &GetEconomyInfoResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ListEconomyProductsParams struct {
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Maximum number of values in the response
|
||||
Limit int64 `json:"limit"`
|
||||
|
||||
// Filter by product visibility
|
||||
Visibility string `json:"visibility"`
|
||||
}
|
||||
|
||||
type ListEconomyProductsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Economy products
|
||||
Products []EconomyProduct `json:"products"`
|
||||
|
||||
// Leftover stock in all warehouses
|
||||
TotalItems int32 `json:"total_items"`
|
||||
}
|
||||
|
||||
type EconomyProduct struct {
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Product MOQs list
|
||||
Quants []EconomyProductQuant `json:"quants"`
|
||||
}
|
||||
|
||||
type EconomyProductQuant struct {
|
||||
// MOQ identifier
|
||||
QuantCode string `json:"quant_code"`
|
||||
|
||||
// MOQ size
|
||||
QuantSize int64 `json:"quant_size"`
|
||||
}
|
||||
|
||||
func (c Products) ListEconomy(ctx context.Context, params *ListEconomyProductsParams) (*ListEconomyProductsResponse, error) {
|
||||
url := "/v1/product/quant/list"
|
||||
|
||||
resp := &ListEconomyProductsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -2686,3 +2686,159 @@ func TestGetRelatedSKUs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEconomyInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetEconomyInfoParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetEconomyInfoParams{
|
||||
QuantCode: []string{"321", "322"},
|
||||
},
|
||||
`{
|
||||
"items": [
|
||||
{
|
||||
"offer_id": "string",
|
||||
"product_id": 0,
|
||||
"quant_info": {
|
||||
"quants": [
|
||||
{
|
||||
"barcodes_extended": [
|
||||
{
|
||||
"barcode": "string",
|
||||
"error": "string",
|
||||
"status": "string"
|
||||
}
|
||||
],
|
||||
"dimensions": {
|
||||
"depth": 0,
|
||||
"height": 0,
|
||||
"weight": 0,
|
||||
"width": 0
|
||||
},
|
||||
"marketing_price": {
|
||||
"price": "string",
|
||||
"seller_price": "string"
|
||||
},
|
||||
"min_price": "string",
|
||||
"old_price": "string",
|
||||
"price": "string",
|
||||
"quant_code": "string",
|
||||
"quant_sice": 0,
|
||||
"shipment_type": "string",
|
||||
"sku": 0,
|
||||
"statuses": {
|
||||
"state_description": "string",
|
||||
"state_name": "string",
|
||||
"state_sys_name": "string",
|
||||
"state_tooltip": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetEconomyInfoParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Products().EconomyInfo(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetEconomyInfoResponse{})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListEconomy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListEconomyProductsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListEconomyProductsParams{
|
||||
Cursor: "string",
|
||||
Limit: 10,
|
||||
Visibility: "ALL",
|
||||
},
|
||||
`{
|
||||
"cursor": "string",
|
||||
"products": [
|
||||
{
|
||||
"offer_id": "string",
|
||||
"product_id": 0,
|
||||
"quants": [
|
||||
{
|
||||
"quant_code": "string",
|
||||
"quant_size": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_items": 0
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListEconomyProductsParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Products().ListEconomy(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListEconomyProductsResponse{})
|
||||
}
|
||||
}
|
||||
|
||||
356
ozon/quants.go
Normal file
356
ozon/quants.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
)
|
||||
|
||||
type Quants struct {
|
||||
client *core.Client
|
||||
}
|
||||
|
||||
type ListQuantsParams struct {
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Filter
|
||||
Filter ListQuantsFilter `json:"filter"`
|
||||
|
||||
// Maximum number of values in the response
|
||||
Limit int32 `json:"limit"`
|
||||
|
||||
// Parameter by which products will be sorted
|
||||
Sort string `json:"sort"`
|
||||
|
||||
// Sorting direction
|
||||
SortDir string `json:"sort_dir"`
|
||||
}
|
||||
|
||||
type ListQuantsFilter struct {
|
||||
// MOQ creation period
|
||||
CreatedAt *ListQuantsFilterTime `json:"created_at"`
|
||||
|
||||
// Time for MOQ assembly
|
||||
Cutoff *ListQuantsFilterTime `json:"cutoff"`
|
||||
|
||||
// Destination point identifier
|
||||
DestinationPlaceId int64 `json:"destination_place_id"`
|
||||
|
||||
// MOQ inventory identifiers
|
||||
InvQuantIds []string `json:"inv_quants_ids"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product name
|
||||
SKUName string `json:"sku_name"`
|
||||
|
||||
// MOQ statuses
|
||||
Statuses []string `json:"statuses"`
|
||||
|
||||
// Warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
}
|
||||
|
||||
type ListQuantsFilterTime struct {
|
||||
// Start date
|
||||
From string `json:"from"`
|
||||
|
||||
// End date
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
type ListQuantsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
Result ListQuantsResult `json:"result"`
|
||||
}
|
||||
|
||||
type ListQuantsResult struct {
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Indication that the response returned only a part of characteristic values
|
||||
HasNext bool `json:"has_next"`
|
||||
|
||||
// MOQs list
|
||||
Quants []Quant `json:"quants"`
|
||||
}
|
||||
|
||||
type Quant struct {
|
||||
// List of available actions with MOQ
|
||||
AvailableActions []string `json:"available_actions"`
|
||||
|
||||
// Date until which the leftover stock amount must be specified
|
||||
AwaitingStockDueDate string `json:"awaiting_stock_due_date"`
|
||||
|
||||
// MOQ cancellation reason
|
||||
CancelReason `json:"cancel_reason"`
|
||||
|
||||
// Seller company identifier
|
||||
CompanyId int64 `json:"company_id"`
|
||||
|
||||
// MOQ creation date
|
||||
CreatedAt string `json:"created_at"`
|
||||
|
||||
// Current number of shipments in the MOQ
|
||||
CurrentPostingsCount int64 `json:"current_postings_count"`
|
||||
|
||||
// Time until which the MOQ must be assembled
|
||||
Cutoff string `json:"cutoff"`
|
||||
|
||||
// Delivery method name
|
||||
DeliveryMethod string `json:"delivery_method_name"`
|
||||
|
||||
// Destination point identifier
|
||||
DestinationPlaceId int64 `json:"destination_place_id"`
|
||||
|
||||
// Destination point name
|
||||
DestinationPlaceName string `json:"destination_place_name"`
|
||||
|
||||
// MOQ filling percentage
|
||||
FillingPercent float32 `json:"filling_percent"`
|
||||
|
||||
// Date when the shipments start to get canceled if the MOQ is not reserved
|
||||
FirstPostingCancellationDate string `json:"first_posting_cancellation_date"`
|
||||
|
||||
// MOQ identifier in Ozon system
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// MOQ inventory identifier
|
||||
InvQuantId int64 `json:"inv_quant_id"`
|
||||
|
||||
// Date of the last MOQ status change
|
||||
LastStatusChangeAt string `json:"last_status_change_at"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Total cost of products in the MOQ
|
||||
ProductsPrice float32 `json:"products_price"`
|
||||
|
||||
// Start date of MOQ filling
|
||||
QuantumStartDate string `json:"quantum_start_date"`
|
||||
|
||||
// Product SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Product name
|
||||
SKUName string `json:"sku_name"`
|
||||
|
||||
// MOQ statuses
|
||||
Status string `json:"status"`
|
||||
|
||||
// Required number of products in the MOQ
|
||||
TargetPostingsCount int64 `json:"target_postings_count"`
|
||||
|
||||
// Delivery service name
|
||||
TPLProviderName string `json:"tpl_provider_name"`
|
||||
|
||||
// MOQ type: box or pallet
|
||||
Type string `json:"type"`
|
||||
|
||||
// Seller warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
|
||||
// Seller warehouse name
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
type CancelReason struct {
|
||||
// Identifier of MOQ cancellation reason
|
||||
Id int64 `json:"cancel_reason_id"`
|
||||
|
||||
// Cancellation reason name
|
||||
Name string `json:"cancel_reason_name"`
|
||||
|
||||
// Cancellation initiator
|
||||
Responsible string `json:"responsible"`
|
||||
}
|
||||
|
||||
// You can leave feedback on this method in the comments section to the discussion in the Ozon for dev community.
|
||||
func (q Quants) List(ctx context.Context, params *ListQuantsParams) (*ListQuantsResponse, error) {
|
||||
url := "/v1/quant/list"
|
||||
|
||||
resp := &ListQuantsResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetQuantParams struct {
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"inv_quant_id"`
|
||||
}
|
||||
|
||||
type GetQuantResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// MOQ information
|
||||
Result []GetQuantResult `json:"result"`
|
||||
}
|
||||
|
||||
type GetQuantResult struct {
|
||||
// Available actions
|
||||
AvailableActions []string `json:"available_actions"`
|
||||
|
||||
// Date until which the leftover stock amount must be specified
|
||||
AwaitingStockDueDate time.Time `json:"awaiting_stock_due_date"`
|
||||
|
||||
// Shipment cancellation reason
|
||||
CancelReason CancelReason `json:"cancel_reason"`
|
||||
|
||||
// Current number of shipments in the MOQ
|
||||
CurrentPostingsCount int64 `json:"current_postings_count"`
|
||||
|
||||
// Time until which the MOQ must be assembled
|
||||
Cutoff time.Time `json:"cutoff"`
|
||||
|
||||
// Delivery method name
|
||||
DeliveryMethodName string `json:"delivery_method_name"`
|
||||
|
||||
// Destination point identifier
|
||||
DestinationPlaceId int64 `json:"destination_place_id"`
|
||||
|
||||
// Destination point name
|
||||
DestinationPlaceName string `json:"destination_place_name"`
|
||||
|
||||
// MOQ filling percentage
|
||||
FillingPercent float32 `json:"filling_percent"`
|
||||
|
||||
// Date when the shipments start to get canceled if the MOQ isn't reserved
|
||||
FirstPostingCancellationDate time.Time `json:"first_posting_cancellation_date"`
|
||||
|
||||
// MOQ identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"inv_quant_id"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Shipments
|
||||
Postings []GetQuantResultPosting `json:"postings"`
|
||||
|
||||
// Link to product photo
|
||||
ProductPictureURL string `json:"product_picture_url"`
|
||||
|
||||
// Total price of products in the MOQ
|
||||
ProductsPrice float32 `json:"products_price"`
|
||||
|
||||
// Start date of MOQ filling
|
||||
QuantumStartDate time.Time `json:"quantum_start_date"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Product name
|
||||
SKUName string `json:"sku_name"`
|
||||
|
||||
// MOQ statuses
|
||||
Status string `json:"status"`
|
||||
|
||||
// Required number of products in the MOQ
|
||||
TargetPostingsCount int64 `json:"target_postings_count"`
|
||||
|
||||
// Delivery service name
|
||||
TPLProviderName string `json:"tpl_provider_name"`
|
||||
|
||||
// MOQ type: box or pallet
|
||||
Type string `json:"type"`
|
||||
|
||||
// Warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
|
||||
// Warehouse name
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
type GetQuantResultPosting struct {
|
||||
// Shipment cancellation reason
|
||||
CancelReason CancelReason `json:"cancel_reason"`
|
||||
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Total price of products in the MOQ
|
||||
ProductsPrice float32 `json:"products_price"`
|
||||
|
||||
// Status text
|
||||
StatusAlias string `json:"status_alias"`
|
||||
|
||||
// Status identifier
|
||||
StatusId int64 `json:"status_id"`
|
||||
}
|
||||
|
||||
func (q Quants) Get(ctx context.Context, params *GetQuantParams) (*GetQuantResponse, error) {
|
||||
url := "/v1/quant/get"
|
||||
|
||||
resp := &GetQuantResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ShipQuantParams struct {
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"quant_id"`
|
||||
}
|
||||
|
||||
type ShipQuantResponse struct {
|
||||
core.CommonResponse
|
||||
}
|
||||
|
||||
func (q Quants) Ship(ctx context.Context, params *ShipQuantParams) (*ShipQuantResponse, error) {
|
||||
url := "/v1/quant/ship"
|
||||
|
||||
resp := &ShipQuantResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type StatusQuantParams struct {
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"inv_quant_id"`
|
||||
}
|
||||
|
||||
type StatusQuantResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// MOQ statuses
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (q Quants) Status(ctx context.Context, params *StatusQuantParams) (*StatusQuantResponse, error) {
|
||||
url := "/v1/quant/ship"
|
||||
|
||||
resp := &StatusQuantResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
303
ozon/quants_test.go
Normal file
303
ozon/quants_test.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListQuants(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListQuantsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListQuantsParams{
|
||||
Cursor: "string",
|
||||
Filter: ListQuantsFilter{
|
||||
InvQuantIds: []string{"string"},
|
||||
DestinationPlaceId: 123,
|
||||
OfferId: "string",
|
||||
SKUName: "string",
|
||||
Statuses: []string{"unknown"},
|
||||
WarehouseId: 456,
|
||||
},
|
||||
Limit: 10,
|
||||
Sort: "string",
|
||||
SortDir: "string",
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"cursor": "string",
|
||||
"has_next": true,
|
||||
"quants": [
|
||||
{
|
||||
"available_actions": [
|
||||
"string"
|
||||
],
|
||||
"awaiting_stock_due_date": "2019-08-24T14:15:22Z",
|
||||
"cancel_reason": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason_name": "string",
|
||||
"responsible": "string"
|
||||
},
|
||||
"company_id": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"current_postings_count": 0,
|
||||
"cutoff": "2019-08-24T14:15:22Z",
|
||||
"delivery_method_name": "string",
|
||||
"destination_place_id": 0,
|
||||
"destination_place_name": "string",
|
||||
"filling_percent": 0,
|
||||
"first_posting_cancellation_date": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"inv_quant_id": 0,
|
||||
"last_status_change_at": "2019-08-24T14:15:22Z",
|
||||
"offer_id": "string",
|
||||
"products_price": 0,
|
||||
"quantum_start_date": "2019-08-24T14:15:22Z",
|
||||
"sku": 0,
|
||||
"sku_name": "string",
|
||||
"status": "unknown",
|
||||
"target_postings_count": 0,
|
||||
"tpl_provider_name": "string",
|
||||
"type": "string",
|
||||
"warehouse_id": 0,
|
||||
"warehouse_name": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListQuantsParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().List(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListQuantsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetQuant(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetQuantParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetQuantParams{
|
||||
QuantId: 456,
|
||||
},
|
||||
`{
|
||||
"result": [
|
||||
{
|
||||
"available_actions": [
|
||||
"string"
|
||||
],
|
||||
"awaiting_stock_due_date": "2019-08-24T14:15:22Z",
|
||||
"cancel_reason": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason_name": "string",
|
||||
"responsible": "string"
|
||||
},
|
||||
"current_postings_count": 0,
|
||||
"cutoff": "2019-08-24T14:15:22Z",
|
||||
"delivery_method_name": "string",
|
||||
"destination_place_id": 0,
|
||||
"destination_place_name": "string",
|
||||
"filling_percent": 0,
|
||||
"first_posting_cancellation_date": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"inv_quant_id": 0,
|
||||
"offer_id": "string",
|
||||
"postings": [
|
||||
{
|
||||
"cancel_reason": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason_name": "string",
|
||||
"responsible": "string"
|
||||
},
|
||||
"posting_number": "string",
|
||||
"products_price": 0,
|
||||
"status_alias": "string",
|
||||
"status_id": 0
|
||||
}
|
||||
],
|
||||
"product_picture_url": "string",
|
||||
"products_price": 0,
|
||||
"quantum_start_date": "2019-08-24T14:15:22Z",
|
||||
"sku": 0,
|
||||
"sku_name": "string",
|
||||
"status": "unknown",
|
||||
"target_postings_count": 0,
|
||||
"tpl_provider_name": "string",
|
||||
"type": "string",
|
||||
"warehouse_id": 0,
|
||||
"warehouse_name": "string"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetQuantParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().Get(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetQuantResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShipQuant(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ShipQuantParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ShipQuantParams{
|
||||
QuantId: 456,
|
||||
},
|
||||
`{}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ShipQuantParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().Ship(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ShipQuantResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusQuant(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *StatusQuantParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&StatusQuantParams{
|
||||
QuantId: 456,
|
||||
},
|
||||
`{
|
||||
"status": "unknown"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&StatusQuantParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().Status(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &StatusQuantResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,9 @@ type GetListOfWarehousesResult struct {
|
||||
// Indication that the warehouse accepts bulky products
|
||||
IsKGT bool `json:"is_kgt"`
|
||||
|
||||
// true if the warehouse handles economy products
|
||||
IsEconomy bool `json:"is_economy"`
|
||||
|
||||
// Indication that warehouse schedule can be changed
|
||||
IsTimetableEditable bool `json:"is_timetable_editable"`
|
||||
|
||||
@@ -189,3 +192,58 @@ func (c Warehouses) GetListOfDeliveryMethods(ctx context.Context, params *GetLis
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ListForShippingParams struct {
|
||||
// Supply type
|
||||
FilterBySupplyType []string `json:"filter_by_supply_type"`
|
||||
|
||||
// Search by warehouse name. To search for pick-up points, specify the full name
|
||||
Search string `json:"search"`
|
||||
}
|
||||
|
||||
type ListForShippingResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Warehouse search result
|
||||
Search []ListForShippingSearch `json:"search"`
|
||||
}
|
||||
|
||||
type ListForShippingSearch struct {
|
||||
// Warehouse address
|
||||
Address string `json:"address"`
|
||||
|
||||
// Warehouse coordinates
|
||||
Coordinates Coordinates `json:"coordinates"`
|
||||
|
||||
// Warehouse name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Identifier of the warehouse, pick-up point, or sorting center
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
|
||||
// Type of warehouse, pick-up point, or sorting center
|
||||
WarehouseType string `json:"warehouse_type"`
|
||||
}
|
||||
|
||||
type Coordinates struct {
|
||||
// Latitude
|
||||
Latitude float64 `json:"latitude"`
|
||||
|
||||
// Longitude
|
||||
Longitude float64 `json:"longitude"`
|
||||
}
|
||||
|
||||
// Get a list of warehouses, sorting centers and pick-up points available for cross-docking, and direct supplies.
|
||||
func (c Warehouses) ListForShipping(ctx context.Context, params *ListForShippingParams) (*ListForShippingResponse, error) {
|
||||
url := "/v1/warehouse/fbo/list"
|
||||
|
||||
resp := &ListForShippingResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -179,3 +179,65 @@ func TestGetListOfDeliveryMethods(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListForShipping(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListForShippingParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListForShippingParams{
|
||||
FilterBySupplyType: []string{"CREATE_TYPE_UNKNOWN"},
|
||||
Search: "string",
|
||||
},
|
||||
`{
|
||||
"search": [
|
||||
{
|
||||
"address": "string",
|
||||
"coordinates": {
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
},
|
||||
"name": "string",
|
||||
"warehouse_id": 0,
|
||||
"warehouse_type": "WAREHOUSE_TYPE_UNKNOWN"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListForShippingParams{},
|
||||
`{
|
||||
"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))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Warehouses().ListForShipping(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListForShippingResponse{})
|
||||
|
||||
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