Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c609b7c7a | ||
|
|
7f71ed6545 | ||
|
|
2f4d207726 | ||
|
|
7b5f44ee44 | ||
|
|
bc5f0e52a5 | ||
|
|
8b8b3bc974 | ||
|
|
45b0dffe39 | ||
|
|
c7697863db | ||
|
|
8a585d086a | ||
|
|
d1fd698368 | ||
|
|
6b8b22180a | ||
|
|
f4a09903c7 | ||
|
|
23ca98fedd | ||
|
|
eae6f54e71 | ||
|
|
8a6cd20b95 | ||
|
|
1706575a34 | ||
|
|
3430ead143 | ||
|
|
2164eff0a6 | ||
|
|
b6af642636 | ||
|
|
67898a4738 | ||
|
|
8c07540d28 |
@@ -219,3 +219,124 @@ func (c Analytics) GetStocksOnWarehouses(ctx context.Context, params *GetStocksO
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetProductTurnoverParams struct {
|
||||||
|
// Number of values in the response
|
||||||
|
Limit int64 `json:"limit"`
|
||||||
|
|
||||||
|
// Number of elements to skip in the response.
|
||||||
|
//
|
||||||
|
// For example, if offset = 10, the response starts with the 11th element found
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
|
||||||
|
// Product identifiers in the Ozon system, SKU
|
||||||
|
SKU []string `json:"sku"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductTurnoverResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Products
|
||||||
|
Items []ProductTurnoverItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductTurnoverItem struct {
|
||||||
|
// Average daily number of product items sold over the last 60 days
|
||||||
|
Ads float64 `json:"ads"`
|
||||||
|
|
||||||
|
// Product stock, pcs
|
||||||
|
CurrentStock int64 `json:"current_stock"`
|
||||||
|
|
||||||
|
// Number of days the stock will last based on your average daily sales
|
||||||
|
IDC float64 `json:"idc"`
|
||||||
|
|
||||||
|
// Product stock level
|
||||||
|
IDCGrade string `json:"idc_grade"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the method to get the product turnover rate and the number of days the current stock will last.
|
||||||
|
//
|
||||||
|
// If you request a list of products by sku, the limit and offset parameters are optional.
|
||||||
|
func (c Analytics) GetProductTurnover(ctx context.Context, params *GetProductTurnoverParams) (*GetProductTurnoverResponse, error) {
|
||||||
|
url := "/v1/analytics/turnover/stocks"
|
||||||
|
|
||||||
|
resp := &GetProductTurnoverResponse{}
|
||||||
|
|
||||||
|
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 GetStockManagementParams struct {
|
||||||
|
// GetStockManagementFilter
|
||||||
|
Filter GetStockManagementFilter `json:"filter"`
|
||||||
|
|
||||||
|
// Number of values in the response
|
||||||
|
Limit int32 `json:"limit,omitempty"`
|
||||||
|
|
||||||
|
// Number of elements to skip in the response
|
||||||
|
Offset int32 `json:"offset,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetStockManagementFilter struct {
|
||||||
|
// Product identifiers in the Ozon system, SKU
|
||||||
|
SKUs []string `json:"skus"`
|
||||||
|
|
||||||
|
// The type of item in stock
|
||||||
|
StockTypes string `json:"stock_types"`
|
||||||
|
|
||||||
|
// Warehouse identifiers
|
||||||
|
WarehouseIds []string `json:"warehouse_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetStockManagementResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Products
|
||||||
|
Items []StockItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StockItem struct {
|
||||||
|
// Stock of defective products, pcs
|
||||||
|
DefectCount int64 `json:"defect_stock_count"`
|
||||||
|
|
||||||
|
// Stock of near-expired products, pcs
|
||||||
|
ExpiringCount int64 `json:"expiring_stock_count"`
|
||||||
|
|
||||||
|
// Product name
|
||||||
|
ProductName string `json:"name"`
|
||||||
|
|
||||||
|
// Product identifier in the seller's system
|
||||||
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
|
// Stock of valid products
|
||||||
|
ValidCount int64 `json:"valid_stock_count"`
|
||||||
|
|
||||||
|
// Stock of products that waiting for documents
|
||||||
|
WaitingDocsCount int64 `json:"waitingdocs_stock_count"`
|
||||||
|
|
||||||
|
// Warehouse name
|
||||||
|
WarehouseName string `json:"warehouse_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the method to find out how many product items are left in stock
|
||||||
|
func (c Analytics) Stock(ctx context.Context, params *GetStockManagementParams) (*GetStockManagementResponse, error) {
|
||||||
|
url := "/v1/analytics/manage/stocks"
|
||||||
|
|
||||||
|
resp := &GetStockManagementResponse{}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -145,3 +145,135 @@ func TestGetStocksOnWarehouses(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetProductTurnover(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetProductTurnoverParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetProductTurnoverParams{
|
||||||
|
Limit: 1,
|
||||||
|
SKU: []string{"string"},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"ads": 0,
|
||||||
|
"current_stock": 0,
|
||||||
|
"idc": 0,
|
||||||
|
"idc_grade": "GRADES_NONE"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetProductTurnoverParams{},
|
||||||
|
`{
|
||||||
|
"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.Analytics().GetProductTurnover(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetProductTurnoverResponse{})
|
||||||
|
|
||||||
|
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 len(resp.Items) > int(test.params.Limit) {
|
||||||
|
t.Errorf("Length of items is bigger than limit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetStock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetStockManagementParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetStockManagementParams{
|
||||||
|
Limit: 1,
|
||||||
|
Offset: 0,
|
||||||
|
Filter: GetStockManagementFilter{
|
||||||
|
StockTypes: "STOCK_TYPE_VALID",
|
||||||
|
SKUs: []string{
|
||||||
|
"string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"defect_stock_count": 0,
|
||||||
|
"expiring_stock_count": 0,
|
||||||
|
"name": "string",
|
||||||
|
"offer_id": "string",
|
||||||
|
"sku": 0,
|
||||||
|
"valid_stock_count": 0,
|
||||||
|
"waitingdocs_stock_count": 0,
|
||||||
|
"warehouse_name": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetStockManagementParams{},
|
||||||
|
`{
|
||||||
|
"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.Analytics().Stock(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetStockManagementResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
ozon/common.go
120
ozon/common.go
@@ -333,7 +333,7 @@ const (
|
|||||||
ServiceProcessingIdentifiedSurplus DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedSurplus"
|
ServiceProcessingIdentifiedSurplus DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedSurplus"
|
||||||
ServiceProcessingIdentifiedDiscrepancies DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedDiscrepancies"
|
ServiceProcessingIdentifiedDiscrepancies DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedDiscrepancies"
|
||||||
ServiceItemInternetSiteAdvertising DetailsServiceItemName = "MarketplaceServiceItemInternetSiteAdvertising"
|
ServiceItemInternetSiteAdvertising DetailsServiceItemName = "MarketplaceServiceItemInternetSiteAdvertising"
|
||||||
ServiceItemPremiumSubscribtion DetailsServiceItemName = "MarketplaceServiceItemPremiumSubscribtion"
|
ServiceItemPremiumSubscribtion DetailsServiceItemName = "MarketplaceServiceItemSubscribtionPremium"
|
||||||
AgencyFeeAggregator3PLGlobalItem DetailsServiceItemName = "MarketplaceAgencyFeeAggregator3PLGlobalItem"
|
AgencyFeeAggregator3PLGlobalItem DetailsServiceItemName = "MarketplaceAgencyFeeAggregator3PLGlobalItem"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -453,6 +453,8 @@ const (
|
|||||||
ReadyForShipment GetFBSReturnsFilterStatus = "ready_for_shipment"
|
ReadyForShipment GetFBSReturnsFilterStatus = "ready_for_shipment"
|
||||||
Disposing GetFBSReturnsFilterStatus = "disposing"
|
Disposing GetFBSReturnsFilterStatus = "disposing"
|
||||||
Disposed GetFBSReturnsFilterStatus = "disposed"
|
Disposed GetFBSReturnsFilterStatus = "disposed"
|
||||||
|
ArrivedForResale GetFBSReturnsFilterStatus = "arrived_for_resale"
|
||||||
|
MovingToResale GetFBSReturnsFilterStatus = "moving_to_resale"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GetFBOReturnsFilterStatus string
|
type GetFBOReturnsFilterStatus string
|
||||||
@@ -559,6 +561,12 @@ const (
|
|||||||
|
|
||||||
// financial report
|
// financial report
|
||||||
ReportTypeSellerFinance ReportType = "SELLER_FINANCE"
|
ReportTypeSellerFinance ReportType = "SELLER_FINANCE"
|
||||||
|
|
||||||
|
// report on sales to legal entities
|
||||||
|
ReportTypeDocB2BSales ReportType = "DOCUMENT_B2B_SALES"
|
||||||
|
|
||||||
|
// settlement report
|
||||||
|
ReportTypeMutualSettlement ReportType = "MUTUAL_SETTLEMENT"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReportInfoStatus string
|
type ReportInfoStatus string
|
||||||
@@ -722,6 +730,8 @@ const (
|
|||||||
// picking up products for removal by the seller
|
// picking up products for removal by the seller
|
||||||
TransactionServiceReturnFromStock TransactionOperationService = "MarketplaceServiceItemReturnFromStock"
|
TransactionServiceReturnFromStock TransactionOperationService = "MarketplaceServiceItemReturnFromStock"
|
||||||
|
|
||||||
|
TransactionServiceStarsMembership TransactionOperationService = "ItemAgentServiceStarsMembership"
|
||||||
|
|
||||||
// forwarding trade
|
// forwarding trade
|
||||||
TransactionItemAdForSupplierLogisticSeller TransactionOperationService = "ItemAdvertisementForSupplierLogisticSeller"
|
TransactionItemAdForSupplierLogisticSeller TransactionOperationService = "ItemAdvertisementForSupplierLogisticSeller"
|
||||||
|
|
||||||
@@ -792,7 +802,7 @@ const (
|
|||||||
TransactionServiceRedistributionReturnsPVZ TransactionOperationService = "MarketplaceServiceItemRedistributionReturnsPVZ"
|
TransactionServiceRedistributionReturnsPVZ TransactionOperationService = "MarketplaceServiceItemRedistributionReturnsPVZ"
|
||||||
|
|
||||||
// Agregator 3PL Globalagency service tariffication
|
// Agregator 3PL Globalagency service tariffication
|
||||||
TransactionServiceAgencyFeeAggregator3PLGlobal TransactionOperationService = "OperationMarketplaceAgencyFeeAggregator3PLGlobal "
|
TransactionServiceAgencyFeeAggregator3PLGlobal TransactionOperationService = "OperationMarketplaceAgencyFeeAggregator3PLGlobal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PaymentTypeGroupName string
|
type PaymentTypeGroupName string
|
||||||
@@ -807,3 +817,109 @@ const (
|
|||||||
PaymentTypeGroupPaymentToCurrentAccount PaymentTypeGroupName = "payment to current account"
|
PaymentTypeGroupPaymentToCurrentAccount PaymentTypeGroupName = "payment to current account"
|
||||||
PaymentTypeGroupSberpay PaymentTypeGroupName = "Sberpay"
|
PaymentTypeGroupSberpay PaymentTypeGroupName = "Sberpay"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type VisualStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// dispute with the customer has been opened
|
||||||
|
VisualStatusDisputeOpened VisualStatus = "DisputeOpened"
|
||||||
|
|
||||||
|
// pending with the seller
|
||||||
|
VisualStatusOnSellerApproval VisualStatus = "OnSellerApproval"
|
||||||
|
|
||||||
|
// at the pick-up point
|
||||||
|
VisualStatusArrivedAtReturnPlace VisualStatus = "ArrivedAtReturnPlace"
|
||||||
|
|
||||||
|
// pending clarification by the seller
|
||||||
|
VisualStatusOnSellerClarification VisualStatus = "OnSellerClarification"
|
||||||
|
|
||||||
|
// pending clarification by the seller after partial compensation
|
||||||
|
VisualStatusOnSellerClarificationPartial VisualStatus = "OnSellerClarificationAfterPartialCompensation"
|
||||||
|
|
||||||
|
// partial compensation offered
|
||||||
|
VisualStatusOfferedPartial VisualStatus = "OfferedPartialCompensation"
|
||||||
|
|
||||||
|
// refund approved
|
||||||
|
VisualStatusReturnMoneyApproved VisualStatus = "ReturnMoneyApproved"
|
||||||
|
|
||||||
|
// partial compensation provided
|
||||||
|
VisualStatusPartialReturned VisualStatus = "PartialCompensationReturned"
|
||||||
|
|
||||||
|
// refund rejected, dispute isn't opened
|
||||||
|
VisualStatusCancelledDisputeNotOpen VisualStatus = "CancelledDisputeNotOpen"
|
||||||
|
|
||||||
|
// request rejected
|
||||||
|
VisualStatusRejected VisualStatus = "Rejected"
|
||||||
|
|
||||||
|
// request rejected by Ozon
|
||||||
|
VisualStatusCrmRejected VisualStatus = "CrmRejected"
|
||||||
|
|
||||||
|
// request canceled
|
||||||
|
VisualStatusCancelled VisualStatus = "Cancelled"
|
||||||
|
|
||||||
|
// request approved by the seller
|
||||||
|
VisualStatusApproved VisualStatus = "Approved"
|
||||||
|
|
||||||
|
// request approved by Ozon
|
||||||
|
VisualStatusApprovedByOzon VisualStatus = "ApprovedByOzon"
|
||||||
|
|
||||||
|
// seller received the return
|
||||||
|
VisualStatusReceivedBySeller VisualStatus = "ReceivedBySeller"
|
||||||
|
|
||||||
|
// return is on its way to the seller
|
||||||
|
VisualStatusMovingToSeller VisualStatus = "MovingToSeller"
|
||||||
|
|
||||||
|
// seller received the refund
|
||||||
|
VisualStatusReturnCompensated VisualStatus = "ReturnCompensated"
|
||||||
|
|
||||||
|
// courier is taking the return to the seller
|
||||||
|
VisualStatusReturningByCourier VisualStatus = "ReturningByCourier"
|
||||||
|
|
||||||
|
// on disposal
|
||||||
|
VisualStatusUtilizing VisualStatus = "Utilizing"
|
||||||
|
|
||||||
|
// disposed of
|
||||||
|
VisualStatusUtilized VisualStatus = "Utilized"
|
||||||
|
|
||||||
|
// customer received full refund
|
||||||
|
VisualStatusMoneyReturned VisualStatus = "MoneyReturned"
|
||||||
|
|
||||||
|
// partial refund has been approved
|
||||||
|
VisualStatusPartialInProcess VisualStatus = "PartialCompensationInProcess"
|
||||||
|
|
||||||
|
// seller opened a dispute
|
||||||
|
VisualStatusDisputeYouOpened VisualStatus = "DisputeYouOpened"
|
||||||
|
|
||||||
|
// compensation rejected
|
||||||
|
VisualStatusCompensationRejected VisualStatus = "CompensationRejected"
|
||||||
|
|
||||||
|
// support request sent
|
||||||
|
VisualStatusDisputeOpening VisualStatus = "DisputeOpening"
|
||||||
|
|
||||||
|
// awaiting your decision on compensation
|
||||||
|
VisualStatusCompensationOffered VisualStatus = "CompensationOffered"
|
||||||
|
|
||||||
|
// awaiting compensation
|
||||||
|
VisualStatusWaitingCompensation VisualStatus = "WaitingCompensation"
|
||||||
|
|
||||||
|
// an error occurred when sending the support request
|
||||||
|
VisualStatusSendingError VisualStatus = "SendingError"
|
||||||
|
|
||||||
|
// decision period has expired
|
||||||
|
VisualStatusCompensationRejectedBySla VisualStatus = "CompensationRejectedBySla"
|
||||||
|
|
||||||
|
// seller has refused compensation
|
||||||
|
VisualStatusCompensationRejectedBySeller VisualStatus = "CompensationRejectedBySeller"
|
||||||
|
|
||||||
|
// on the way to the Ozon warehouse
|
||||||
|
VisualStatusMovingToOzon VisualStatus = "MovingToOzon"
|
||||||
|
|
||||||
|
// arrived at the Ozon warehouse
|
||||||
|
VisualStatusReturnedToOzon VisualStatus = "ReturnedToOzon"
|
||||||
|
|
||||||
|
// quick refund
|
||||||
|
VisualStatusMoneyReturnedBySystem VisualStatus = "MoneyReturnedBySystem"
|
||||||
|
|
||||||
|
// awaiting shipping
|
||||||
|
VisualStatusWaitingShipment VisualStatus = "WaitingShipment"
|
||||||
|
)
|
||||||
|
|||||||
791
ozon/fbo.go
791
ozon/fbo.go
@@ -101,9 +101,6 @@ type GetFBOShipmentsListResultAdditionalData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetFBOShipmentsListResultAnalyticsData struct {
|
type GetFBOShipmentsListResultAnalyticsData struct {
|
||||||
// Delivery city
|
|
||||||
City string `json:"city"`
|
|
||||||
|
|
||||||
// Delivery method
|
// Delivery method
|
||||||
DeliveryType string `json:"delivery_type"`
|
DeliveryType string `json:"delivery_type"`
|
||||||
|
|
||||||
@@ -118,9 +115,6 @@ type GetFBOShipmentsListResultAnalyticsData struct {
|
|||||||
// Payment method
|
// Payment method
|
||||||
PaymentTypeGroupName PaymentTypeGroupName `json:"payment_type_group_name"`
|
PaymentTypeGroupName PaymentTypeGroupName `json:"payment_type_group_name"`
|
||||||
|
|
||||||
// Delivery region
|
|
||||||
Region string `json:"region"`
|
|
||||||
|
|
||||||
// Warehouse identifier
|
// Warehouse identifier
|
||||||
WarehouseId int64 `json:"warehouse_id"`
|
WarehouseId int64 `json:"warehouse_id"`
|
||||||
|
|
||||||
@@ -247,9 +241,6 @@ type GetShipmentDetailsResultAdditionalData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetShipmentDetailsResultAnalyticsData struct {
|
type GetShipmentDetailsResultAnalyticsData struct {
|
||||||
// Delivery city
|
|
||||||
City string `json:"city"`
|
|
||||||
|
|
||||||
// Delivery method
|
// Delivery method
|
||||||
DeliveryType string `json:"delivery_type"`
|
DeliveryType string `json:"delivery_type"`
|
||||||
|
|
||||||
@@ -264,9 +255,6 @@ type GetShipmentDetailsResultAnalyticsData struct {
|
|||||||
// Payment method
|
// Payment method
|
||||||
PaymentTypeGroupName string `json:"payment_type_group_name"`
|
PaymentTypeGroupName string `json:"payment_type_group_name"`
|
||||||
|
|
||||||
// Delivery region
|
|
||||||
Region string `json:"region"`
|
|
||||||
|
|
||||||
// Warehouse identifier
|
// Warehouse identifier
|
||||||
WarehouseId int64 `json:"warehouse_id"`
|
WarehouseId int64 `json:"warehouse_id"`
|
||||||
|
|
||||||
@@ -290,108 +278,38 @@ func (c FBO) GetShipmentDetails(ctx context.Context, params *GetShipmentDetailsP
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListSupplyRequestsParams struct {
|
type ListSupplyRequestsParams struct {
|
||||||
// Number of the page returned in the request
|
// Filter
|
||||||
Page int32 `json:"page"`
|
Filter *ListSupplyRequestsFilter `json:"filter"`
|
||||||
|
|
||||||
// Number of elements on the page
|
// Customizing the display of the requests list
|
||||||
PageSize int32 `json:"page_size"`
|
Paging *ListSupplyRequestsPaging `json:"paging"`
|
||||||
|
}
|
||||||
|
|
||||||
// Filter on status of a supply by request
|
type ListSupplyRequestsFilter struct {
|
||||||
States []SupplyRequestState `json:"states"`
|
States []string `json:"states"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListSupplyRequestsPaging struct {
|
||||||
|
// Supply number from which the list of requests will start
|
||||||
|
FromOrderId int64 `json:"from_supply_order_id"`
|
||||||
|
|
||||||
|
// Number of requests in the response
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListSupplyRequestsResponse struct {
|
type ListSupplyRequestsResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
// Indicates that the response contains not the entire array of supply requests:
|
// Supply request identifier you last requested
|
||||||
// - true — make a new request with a different page and page_size values to get information on the remaining requests;
|
LastSupplyOrderId int64 `json:"last_supply_order_id"`
|
||||||
// - false — the entire array of requests for the filter specified in the request was returned in the response
|
|
||||||
HasNext bool `json:"has_next"`
|
|
||||||
|
|
||||||
// Supply requests list
|
|
||||||
SupplyOrders []SupplyRequestCommonResponse `json:"supply_orders"`
|
|
||||||
|
|
||||||
// Total requests number
|
|
||||||
TotalSupplyOrdersCount int32 `json:"total_supply_orders_count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SupplyRequestCommonResponse struct {
|
|
||||||
// Supply request creation date
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
|
|
||||||
// Local time supply interval
|
|
||||||
LocalTimeslot SupplyRequestCommonResponseLocalTimeslot `json:"local_timeslot"`
|
|
||||||
|
|
||||||
// Date from which you want to bring the supply to the warehouse. Only for supplies via vDC
|
|
||||||
PreferredSupplyDateFrom string `json:"preferred_supply_date_from"`
|
|
||||||
|
|
||||||
// Date by which you want to bring the supply to the warehouse. Only for supplies via vDC
|
|
||||||
PreferredSupplyDateTo string `json:"preferred_supply_date_to"`
|
|
||||||
|
|
||||||
// Your own warehouse from which you'll take the products to the supply warehouse.
|
|
||||||
// Only for supplies via vDC
|
|
||||||
SellerWarehouse SupplyRequestSellerWarehouse `json:"seller_warehouse"`
|
|
||||||
|
|
||||||
// Status of a supply by request
|
|
||||||
State string `json:"state"`
|
|
||||||
|
|
||||||
// Supply request identifier
|
// Supply request identifier
|
||||||
SupplyOrderId int64 `json:"supply_order_id"`
|
SupplyOrderId []string `json:"supply_order_id"`
|
||||||
|
|
||||||
// Supply request number
|
|
||||||
SupplyOrderNumber string `json:"supply_order_number"`
|
|
||||||
|
|
||||||
// Supply warehouse
|
|
||||||
SupplyWarehouse SupplyRequestCommonResponseSupplyWarehouse `json:"supply_warehouse"`
|
|
||||||
|
|
||||||
// time_left_to_prepare_supply
|
|
||||||
TimeLeftToPrepareSupply int64 `json:"time_left_to_prepare_supply"`
|
|
||||||
|
|
||||||
// Time in seconds left to select the supply option. Only for supplies via vDC
|
|
||||||
TimeLeftToSelectSupplyVariant int64 `json:"time_left_to_select_supply_variant"`
|
|
||||||
|
|
||||||
// total_items_count
|
|
||||||
TotalItemsCount int32 `json:"total_items_count"`
|
|
||||||
|
|
||||||
// Total number of items in the request
|
|
||||||
TotalQuantity int32 `json:"total_quantity"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SupplyRequestSellerWarehouse struct {
|
// Requests with supply to a specific warehouse and through a virtual distribution center (vDC) are taken into account
|
||||||
// Warehouse address
|
|
||||||
Address string `json:"address"`
|
|
||||||
|
|
||||||
// Warehouse name
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Warehouse identifier
|
|
||||||
WarehouseId int64 `json:"warehouse_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SupplyRequestCommonResponseLocalTimeslot struct {
|
|
||||||
// Interval start
|
|
||||||
From string `json:"from"`
|
|
||||||
|
|
||||||
// Interval end
|
|
||||||
To string `json:"to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SupplyRequestCommonResponseSupplyWarehouse struct {
|
|
||||||
// Warehouse address
|
|
||||||
Address string `json:"address"`
|
|
||||||
|
|
||||||
// Warehouse name
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Warehouse identifier
|
|
||||||
WarehouseId int64 `json:"warehouse_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method for getting a list of supply requests to the Ozon warehouse.
|
|
||||||
// Requests with supply both to a specific warehouse and via a virtual
|
|
||||||
// distribution center (vDC) are taken into account
|
|
||||||
func (c FBO) ListSupplyRequests(ctx context.Context, params *ListSupplyRequestsParams) (*ListSupplyRequestsResponse, error) {
|
func (c FBO) ListSupplyRequests(ctx context.Context, params *ListSupplyRequestsParams) (*ListSupplyRequestsResponse, error) {
|
||||||
url := "/v1/supply-order/list"
|
url := "/v2/supply-order/list"
|
||||||
|
|
||||||
resp := &ListSupplyRequestsResponse{}
|
resp := &ListSupplyRequestsResponse{}
|
||||||
|
|
||||||
@@ -405,17 +323,111 @@ func (c FBO) ListSupplyRequests(ctx context.Context, params *ListSupplyRequestsP
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetSupplyRequestInfoParams struct {
|
type GetSupplyRequestInfoParams struct {
|
||||||
// Supply request identifier
|
// Supply request identifier in the Ozon system
|
||||||
SupplyOrderId int64 `json:"supply_order_id"`
|
OrderIds []string `json:"order_ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetSupplyRequestInfoResponse struct {
|
type GetSupplyRequestInfoResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
SupplyRequestCommonResponse
|
// Supply request details
|
||||||
|
Orders []SupplyOrder `json:"orders"`
|
||||||
|
|
||||||
// Driver and car information
|
// Warehouse details
|
||||||
VehicleInfo GetSupplyRequestInfoVehicle `json:"vehicle_info"`
|
Warehouses []SupplyWarehouse `json:"warehouses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyOrder struct {
|
||||||
|
// Date of supply request creation
|
||||||
|
CreationDate string `json:"creation_date"`
|
||||||
|
|
||||||
|
// Request source
|
||||||
|
CreationFlow string `json:"creation_flow"`
|
||||||
|
|
||||||
|
// Time remaining in seconds to fill in the supply details. Only for requests from the vDC
|
||||||
|
DataFillingDeadline time.Time `json:"data_filling_deadline_utc"`
|
||||||
|
|
||||||
|
// Supply warehouse identifier
|
||||||
|
DropoffWarehouseId int64 `json:"dropoff_warehouse_id"`
|
||||||
|
|
||||||
|
// Filter by supply status
|
||||||
|
State string `json:"state"`
|
||||||
|
|
||||||
|
// Supply request contents
|
||||||
|
Supplies []Supply `json:"supplies"`
|
||||||
|
|
||||||
|
// Supply request identifier
|
||||||
|
Id int64 `json:"supply_order_id"`
|
||||||
|
|
||||||
|
// Request number
|
||||||
|
OrderNumber string `json:"supply_order_number"`
|
||||||
|
|
||||||
|
// Supply time slot
|
||||||
|
Timeslot []SupplyTimeslot `json:"timeslot"`
|
||||||
|
|
||||||
|
// Driver and vehicle details
|
||||||
|
Vehicle []SupplyVehicle `json:"vehicle"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Supply struct {
|
||||||
|
// Supply contents identifier. Used in the /v1/supply-order/bundle method
|
||||||
|
BundleId string `json:"bundle_id"`
|
||||||
|
|
||||||
|
// Storage warehouse identifier
|
||||||
|
StorageWarehouseId int64 `json:"storage_warehouse_id"`
|
||||||
|
|
||||||
|
// Supply identifier
|
||||||
|
Id int64 `json:"supply_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyTimeslot struct {
|
||||||
|
// Reason why you can't select the supply time slot
|
||||||
|
Reasons []string `json:"can_not_set_reasons"`
|
||||||
|
|
||||||
|
// true, if you can select or edit the supply time slot
|
||||||
|
CanSet bool `json:"can_set"`
|
||||||
|
|
||||||
|
// true, if the characteristic is required
|
||||||
|
IsRequired bool `json:"is_required"`
|
||||||
|
|
||||||
|
Value SupplyTimeslotValue `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyVehicle struct {
|
||||||
|
// Reason why you can't select the supply time slot
|
||||||
|
Reasons []string `json:"can_not_set_reasons"`
|
||||||
|
|
||||||
|
// true, if you can select or edit the supply time slot
|
||||||
|
CanSet bool `json:"can_set"`
|
||||||
|
|
||||||
|
// true, if the characteristic is required
|
||||||
|
IsRequired bool `json:"is_required"`
|
||||||
|
|
||||||
|
Value []GetSupplyRequestInfoVehicle `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyTimeslotValue struct {
|
||||||
|
// Supply time slot in local time
|
||||||
|
Timeslot []SupplyTimeslotValueTimeslot `json:"timeslot"`
|
||||||
|
|
||||||
|
// Time zone
|
||||||
|
Timezone []SupplyTimeslotValueTimezone `json:"timezone_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyTimeslotValueTimeslot struct {
|
||||||
|
// Supply time slot start
|
||||||
|
From time.Time `json:"from"`
|
||||||
|
|
||||||
|
// Supply time slot end
|
||||||
|
To time.Time `json:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyTimeslotValueTimezone struct {
|
||||||
|
// Time zone name
|
||||||
|
Name string `json:"iana_name"`
|
||||||
|
|
||||||
|
// Time zone offset from UTC-0 in seconds
|
||||||
|
Offset string `json:"offset"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetSupplyRequestInfoVehicle struct {
|
type GetSupplyRequestInfoVehicle struct {
|
||||||
@@ -432,11 +444,22 @@ type GetSupplyRequestInfoVehicle struct {
|
|||||||
VehicleNumber string `json:"vehicle_number"`
|
VehicleNumber string `json:"vehicle_number"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SupplyWarehouse struct {
|
||||||
|
// Warehouse address
|
||||||
|
Address string `json:"address"`
|
||||||
|
|
||||||
|
// Warehouse name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Warehouse identifier
|
||||||
|
Id int64 `json:"warehouse_id"`
|
||||||
|
}
|
||||||
|
|
||||||
// Method for getting detailed information on a supply request.
|
// Method for getting detailed information on a supply request.
|
||||||
// Requests with supply both to a specific warehouse and via a
|
// Requests with supply both to a specific warehouse and via a
|
||||||
// virtual distribution center (vDC) are taken into account
|
// virtual distribution center (vDC) are taken into account
|
||||||
func (c FBO) GetSupplyRequestInfo(ctx context.Context, params *GetSupplyRequestInfoParams) (*GetSupplyRequestInfoResponse, error) {
|
func (c FBO) GetSupplyRequestInfo(ctx context.Context, params *GetSupplyRequestInfoParams) (*GetSupplyRequestInfoResponse, error) {
|
||||||
url := "/v1/supply-order/get"
|
url := "/v2/supply-order/get"
|
||||||
|
|
||||||
resp := &GetSupplyRequestInfoResponse{}
|
resp := &GetSupplyRequestInfoResponse{}
|
||||||
|
|
||||||
@@ -563,3 +586,555 @@ func (c FBO) GetWarehouseWorkload(ctx context.Context) (*GetWarehouseWorkloadRes
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetSupplyOrdersByStatusParams struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSupplyOrdersByStatusResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
Items []SupplyOrdersByStatus `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyOrdersByStatus struct {
|
||||||
|
// Number of supply requests in this status
|
||||||
|
Count int32 `json:"count"`
|
||||||
|
|
||||||
|
// Supply status
|
||||||
|
OrderState string `json:"order_state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of supply requests in a specific status.
|
||||||
|
func (c FBO) GetSupplyOrdersByStatus(ctx context.Context) (*GetSupplyOrdersByStatusResponse, error) {
|
||||||
|
url := "/v1/supply-order/status/counter"
|
||||||
|
|
||||||
|
resp := &GetSupplyOrdersByStatusResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(ctx, http.MethodGet, url, &GetSupplyOrdersByStatusParams{}, resp, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSupplyTimeslotsParams struct {
|
||||||
|
// Supply request identifier
|
||||||
|
SupplyOrderId int64 `json:"supply_order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSupplyTimeslotsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Supply time slot
|
||||||
|
Timeslots []SupplyTimeslotValueTimeslot `json:"timeslots"`
|
||||||
|
|
||||||
|
// Time zone
|
||||||
|
Timezones []SupplyTimeslotValueTimezone `json:"timezone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBO) GetSupplyTimeslots(ctx context.Context, params *GetSupplyTimeslotsParams) (*GetSupplyTimeslotsResponse, error) {
|
||||||
|
url := "/v1/supply-order/timeslot/get"
|
||||||
|
|
||||||
|
resp := &GetSupplyTimeslotsResponse{}
|
||||||
|
|
||||||
|
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 UpdateSupplyTimeslotParams struct {
|
||||||
|
// Supply request identifier
|
||||||
|
SupplyOrderId int64 `json:"supply_order_id"`
|
||||||
|
|
||||||
|
// Supply time slot details
|
||||||
|
Timeslot SupplyTimeslotValueTimeslot `json:"timeslot"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateSupplyTimeslotResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Possible errors
|
||||||
|
Errors []string `json:"errors"`
|
||||||
|
|
||||||
|
// Operation identifier
|
||||||
|
OperationId string `json:"operation_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBO) UpdateSupplyTimeslot(ctx context.Context, params *UpdateSupplyTimeslotParams) (*UpdateSupplyTimeslotResponse, error) {
|
||||||
|
url := "/v1/supply-order/timeslot/update"
|
||||||
|
|
||||||
|
resp := &UpdateSupplyTimeslotResponse{}
|
||||||
|
|
||||||
|
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 GetSupplyTimeslotStatusParams struct {
|
||||||
|
// Operation identifier
|
||||||
|
OperationId string `json:"operation_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSupplyTimeslotStatusResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Possible errors
|
||||||
|
Errors []string `json:"errors"`
|
||||||
|
|
||||||
|
// Data status
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBO) GetSupplyTimeslotStatus(ctx context.Context, params *GetSupplyTimeslotStatusParams) (*GetSupplyTimeslotStatusResponse, error) {
|
||||||
|
url := "/v1/supply-order/timeslot/status"
|
||||||
|
|
||||||
|
resp := &GetSupplyTimeslotStatusResponse{}
|
||||||
|
|
||||||
|
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 CreatePassParams struct {
|
||||||
|
// Supply request identifier
|
||||||
|
SupplyOrderId int64 `json:"supply_order_id"`
|
||||||
|
|
||||||
|
// Driver and car information
|
||||||
|
Vehicle GetSupplyRequestInfoVehicle `json:"vehicle"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreatePassResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Possible errors
|
||||||
|
Errors []string `json:"error_reasons"`
|
||||||
|
|
||||||
|
// Operation identifier
|
||||||
|
OperationId string `json:"operation_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBO) CreatePass(ctx context.Context, params *CreatePassParams) (*CreatePassResponse, error) {
|
||||||
|
url := "/v1/supply-order/pass/create"
|
||||||
|
|
||||||
|
resp := &CreatePassResponse{}
|
||||||
|
|
||||||
|
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 GetPassParams struct {
|
||||||
|
// Operation identifier
|
||||||
|
OperationId string `json:"operation_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetPassResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Possible errors
|
||||||
|
Errors []string `json:"errors"`
|
||||||
|
|
||||||
|
// Status of driver and vehicle data entry
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBO) GetPass(ctx context.Context, params *GetPassParams) (*GetPassResponse, error) {
|
||||||
|
url := "/v1/supply-order/pass/status"
|
||||||
|
|
||||||
|
resp := &GetPassResponse{}
|
||||||
|
|
||||||
|
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 GetSupplyContentParams struct {
|
||||||
|
// Identifiers of supply contents. Minimum is 1, maximum is 1000. You can get them using the /v2/supply-order/get method
|
||||||
|
BundleIds []string `json:"bundle_ids"`
|
||||||
|
|
||||||
|
// true, to sort in ascending order
|
||||||
|
IsAsc bool `json:"is_asc"`
|
||||||
|
|
||||||
|
// Identifier of the last value on the page
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
|
// Number of values on the page. Minimum is 1, maximum is 1000
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
|
||||||
|
// Search query, for example: by name, article code, or SKU
|
||||||
|
Query string `json:"query"`
|
||||||
|
|
||||||
|
// Sorting by parameters
|
||||||
|
SortField string `json:"sort_field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetSupplyContentResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// List of products in the supply request
|
||||||
|
Items []SupplyContentItem `json:"items"`
|
||||||
|
|
||||||
|
// Quantity of products in the request
|
||||||
|
TotalCount int32 `json:"total_count"`
|
||||||
|
|
||||||
|
// Indication that the response hasn't returned all products
|
||||||
|
HasNext bool `json:"has_next"`
|
||||||
|
|
||||||
|
// Identifier of the last value on the page
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupplyContentItem struct {
|
||||||
|
// Link to product image
|
||||||
|
IconPath string `json:"icon_path"`
|
||||||
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
|
// Product name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Product items quantity
|
||||||
|
Quantity int32 `json:"quantity"`
|
||||||
|
|
||||||
|
// Barcode
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
|
||||||
|
// Product identifier
|
||||||
|
ProductId int64 `json:"product_id"`
|
||||||
|
|
||||||
|
// Quantity of products in one package
|
||||||
|
Quant int32 `json:"quant"`
|
||||||
|
|
||||||
|
// true if the quantity of products in one package can be edited
|
||||||
|
IsQuantEditable bool `json:"is_quant_editable"`
|
||||||
|
|
||||||
|
// Volume of products in liters
|
||||||
|
VolumeInLiters float64 `json:"volume_in_litres"`
|
||||||
|
|
||||||
|
// Volume of all products in liters
|
||||||
|
TotalVolumeInLiters float64 `json:"total_volume_in_litres"`
|
||||||
|
|
||||||
|
// Product article code
|
||||||
|
ContractorItemCode string `json:"contractor_item_code"`
|
||||||
|
|
||||||
|
// Super product label
|
||||||
|
SFBOAttribute string `json:"sfbo_attribute"`
|
||||||
|
|
||||||
|
// Type of wrapper
|
||||||
|
ShipmentType string `json:"shipment_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBO) GetSupplyContent(ctx context.Context, params *GetSupplyContentParams) (*GetSupplyContentResponse, error) {
|
||||||
|
url := "/v1/supply-order/bundle"
|
||||||
|
|
||||||
|
resp := &GetSupplyContentResponse{}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
|||||||
817
ozon/fbo_test.go
817
ozon/fbo_test.go
@@ -58,8 +58,6 @@ func TestGetFBOShipmentsList(t *testing.T) {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"analytics_data": {
|
"analytics_data": {
|
||||||
"region": "РОСТОВСКАЯ ОБЛАСТЬ",
|
|
||||||
"city": "Ростов-на-Дону",
|
|
||||||
"delivery_type": "PVZ",
|
"delivery_type": "PVZ",
|
||||||
"is_premium": false,
|
"is_premium": false,
|
||||||
"payment_type_group_name": "Карты оплаты",
|
"payment_type_group_name": "Карты оплаты",
|
||||||
@@ -191,8 +189,6 @@ func TestGetShipmentDetails(t *testing.T) {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"analytics_data": {
|
"analytics_data": {
|
||||||
"region": "МОСКВА",
|
|
||||||
"city": "Москва",
|
|
||||||
"delivery_type": "Courier",
|
"delivery_type": "Courier",
|
||||||
"is_premium": false,
|
"is_premium": false,
|
||||||
"payment_type_group_name": "Карты оплаты",
|
"payment_type_group_name": "Карты оплаты",
|
||||||
@@ -301,36 +297,19 @@ func TestListSupplyRequests(t *testing.T) {
|
|||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
&ListSupplyRequestsParams{
|
&ListSupplyRequestsParams{
|
||||||
Page: 0,
|
Filter: &ListSupplyRequestsFilter{
|
||||||
PageSize: 0,
|
States: []string{"ORDER_STATE_DATA_FILLING"},
|
||||||
States: []SupplyRequestState{AcceptanceAtStorageWarehouse},
|
},
|
||||||
|
Paging: &ListSupplyRequestsPaging{
|
||||||
|
FromOrderId: 0,
|
||||||
|
Limit: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
"has_next": true,
|
"last_supply_order_id": 0,
|
||||||
"supply_orders": [
|
"supply_order_id": [
|
||||||
{
|
"string"
|
||||||
"created_at": "string",
|
]
|
||||||
"local_timeslot": {
|
|
||||||
"from": "string",
|
|
||||||
"to": "string"
|
|
||||||
},
|
|
||||||
"preferred_supply_date_from": "string",
|
|
||||||
"preferred_supply_date_to": "string",
|
|
||||||
"state": "string",
|
|
||||||
"supply_order_id": 0,
|
|
||||||
"supply_order_number": "string",
|
|
||||||
"supply_warehouse": {
|
|
||||||
"address": "string",
|
|
||||||
"name": "string",
|
|
||||||
"warehouse_id": 0
|
|
||||||
},
|
|
||||||
"time_left_to_prepare_supply": 0,
|
|
||||||
"time_left_to_select_supply_variant": 0,
|
|
||||||
"total_items_count": 0,
|
|
||||||
"total_quantity": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total_supply_orders_count": 0
|
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
@@ -377,39 +356,74 @@ func TestGetSupplyRequestInfo(t *testing.T) {
|
|||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
&GetSupplyRequestInfoParams{
|
&GetSupplyRequestInfoParams{
|
||||||
SupplyOrderId: 0,
|
OrderIds: []string{"string"},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
"created_at": "string",
|
"orders": [
|
||||||
"local_timeslot": {
|
{
|
||||||
"from": "string",
|
"creation_date": "string",
|
||||||
"to": "string"
|
"creation_flow": "string",
|
||||||
},
|
"data_filling_deadline_utc": "2019-08-24T14:15:22Z",
|
||||||
"preferred_supply_date_from": "string",
|
"dropoff_warehouse_id": 0,
|
||||||
"preferred_supply_date_to": "string",
|
"state": "ORDER_STATE_UNSPECIFIED",
|
||||||
"seller_warehouse": {
|
"supplies": [
|
||||||
"address": "string",
|
{
|
||||||
"name": "string",
|
"bundle_id": "string",
|
||||||
"warehouse_id": 0
|
"storage_warehouse_id": 0,
|
||||||
},
|
"supply_id": 0
|
||||||
"state": "string",
|
}
|
||||||
|
],
|
||||||
"supply_order_id": 0,
|
"supply_order_id": 0,
|
||||||
"supply_order_number": "string",
|
"supply_order_number": "string",
|
||||||
"supply_warehouse": {
|
"timeslot": [
|
||||||
"address": "string",
|
{
|
||||||
"name": "string",
|
"can_not_set_reasons": [
|
||||||
"warehouse_id": 0
|
"string"
|
||||||
},
|
],
|
||||||
"time_left_to_prepare_supply": 0,
|
"can_set": true,
|
||||||
"time_left_to_select_supply_variant": 0,
|
"is_required": true,
|
||||||
"total_items_count": 0,
|
"value": {
|
||||||
"total_quantity": 0,
|
"timeslot": [
|
||||||
"vehicle_info": {
|
{
|
||||||
|
"from": "2019-08-24T14:15:22Z",
|
||||||
|
"to": "2019-08-24T14:15:22Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timezone_info": [
|
||||||
|
{
|
||||||
|
"iana_name": "string",
|
||||||
|
"offset": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vehicle": [
|
||||||
|
{
|
||||||
|
"can_not_set_reasons": [
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"can_set": true,
|
||||||
|
"is_required": true,
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
"driver_name": "string",
|
"driver_name": "string",
|
||||||
"driver_phone": "string",
|
"driver_phone": "string",
|
||||||
"vehicle_model": "string",
|
"vehicle_model": "string",
|
||||||
"vehicle_number": "string"
|
"vehicle_number": "string"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"warehouses": [
|
||||||
|
{
|
||||||
|
"address": "string",
|
||||||
|
"name": "string",
|
||||||
|
"warehouse_id": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
@@ -565,3 +579,694 @@ func TestGetWarehouseWorkload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSupplyOrdersByStatus(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"},
|
||||||
|
`{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"count": 0,
|
||||||
|
"order_state": "ORDER_STATE_UNSPECIFIED"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// 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))
|
||||||
|
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||||
|
resp, err := c.FBO().GetSupplyOrdersByStatus(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetSupplyOrdersByStatusResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSupplyTimeslots(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetSupplyTimeslotsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetSupplyTimeslotsParams{
|
||||||
|
SupplyOrderId: 0,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"timeslots": [
|
||||||
|
{
|
||||||
|
"from": "2019-08-24T14:15:22Z",
|
||||||
|
"to": "2019-08-24T14:15:22Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timezone": [
|
||||||
|
{
|
||||||
|
"iana_name": "string",
|
||||||
|
"offset": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetSupplyTimeslotsParams{},
|
||||||
|
`{
|
||||||
|
"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().GetSupplyTimeslots(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetSupplyTimeslotsResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSupplyTimeslot(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *UpdateSupplyTimeslotParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&UpdateSupplyTimeslotParams{
|
||||||
|
SupplyOrderId: 0,
|
||||||
|
Timeslot: SupplyTimeslotValueTimeslot{
|
||||||
|
From: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"errors": [
|
||||||
|
"UPDATE_TIMESLOT_ERROR_UNSPECIFIED"
|
||||||
|
],
|
||||||
|
"operation_id": "string"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&UpdateSupplyTimeslotParams{},
|
||||||
|
`{
|
||||||
|
"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().UpdateSupplyTimeslot(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &UpdateSupplyTimeslotResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSupplyTimeslotStatus(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetSupplyTimeslotStatusParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetSupplyTimeslotStatusParams{
|
||||||
|
OperationId: "string",
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"errors": [
|
||||||
|
"UPDATE_TIMESLOT_ERROR_UNSPECIFIED"
|
||||||
|
],
|
||||||
|
"status": "STATUS_UNSPECIFIED"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetSupplyTimeslotStatusParams{},
|
||||||
|
`{
|
||||||
|
"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().GetSupplyTimeslotStatus(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetSupplyTimeslotStatusResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreatePass(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *CreatePassParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&CreatePassParams{
|
||||||
|
SupplyOrderId: 123,
|
||||||
|
Vehicle: GetSupplyRequestInfoVehicle{
|
||||||
|
DriverName: "string",
|
||||||
|
DriverPhone: "string",
|
||||||
|
VehicleModel: "string",
|
||||||
|
VehicleNumber: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"error_reasons": [
|
||||||
|
"SET_VEHICLE_ERROR_UNSPECIFIED"
|
||||||
|
],
|
||||||
|
"operation_id": "string"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&CreatePassParams{},
|
||||||
|
`{
|
||||||
|
"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().CreatePass(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &CreatePassResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPass(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetPassParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetPassParams{
|
||||||
|
OperationId: "string",
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"errors": [
|
||||||
|
"SET_VEHICLE_ERROR_UNSPECIFIED"
|
||||||
|
],
|
||||||
|
"result": "Unknown"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetPassParams{},
|
||||||
|
`{
|
||||||
|
"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().GetPass(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetPassResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSupplyContent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetSupplyContentParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetSupplyContentParams{
|
||||||
|
BundleIds: []string{"string"},
|
||||||
|
IsAsc: true,
|
||||||
|
Limit: 0,
|
||||||
|
Query: "string",
|
||||||
|
SortField: "UNSPECIFIED",
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"icon_path": "string",
|
||||||
|
"sku": 0,
|
||||||
|
"name": "string",
|
||||||
|
"quantity": 0,
|
||||||
|
"barcode": "string",
|
||||||
|
"product_id": 0,
|
||||||
|
"quant": 0,
|
||||||
|
"is_quant_editable": true,
|
||||||
|
"volume_in_litres": 0,
|
||||||
|
"total_volume_in_litres": 0,
|
||||||
|
"contractor_item_code": "string",
|
||||||
|
"sfbo_attribute": "ITEM_SFBO_ATTRIBUTE_UNSPECIFIED",
|
||||||
|
"shipment_type": "BUNDLE_ITEM_SHIPMENT_TYPE_UNSPECIFIED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_count": 0,
|
||||||
|
"has_next": true,
|
||||||
|
"last_id": "string"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetSupplyContentParams{},
|
||||||
|
`{
|
||||||
|
"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().GetSupplyContent(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetSupplyContentResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
250
ozon/fbs.go
250
ozon/fbs.go
@@ -53,6 +53,11 @@ type ListUnprocessedShipmentsFilter struct {
|
|||||||
// Delivery method identifier
|
// Delivery method identifier
|
||||||
DeliveryMethodId []int64 `json:"delivery_method_id"`
|
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:
|
// Filter for shipments delivered from partner warehouse (FBP). You can pass one of the following values:
|
||||||
//
|
//
|
||||||
// Default value is all.
|
// Default value is all.
|
||||||
@@ -106,6 +111,9 @@ type FBSPosting struct {
|
|||||||
// Analytics data
|
// Analytics data
|
||||||
AnalyticsData FBSPostingAnalyticsData `json:"analytics_data"`
|
AnalyticsData FBSPostingAnalyticsData `json:"analytics_data"`
|
||||||
|
|
||||||
|
// Available actions and shipment information
|
||||||
|
AvailableActions []string `json:"available_actions"`
|
||||||
|
|
||||||
// Shipment barcodes
|
// Shipment barcodes
|
||||||
Barcodes FBSBarcode `json:"barcodes"`
|
Barcodes FBSBarcode `json:"barcodes"`
|
||||||
|
|
||||||
@@ -178,6 +186,44 @@ type FBSPosting struct {
|
|||||||
|
|
||||||
// Shipment tracking number
|
// Shipment tracking number
|
||||||
TrackingNumber string `json:"tracking_number"`
|
TrackingNumber string `json:"tracking_number"`
|
||||||
|
|
||||||
|
// Details on shipping rate
|
||||||
|
Tariffication []FBSPostingTariffication `json:"tariffication"`
|
||||||
|
|
||||||
|
// Economy product identifier
|
||||||
|
QuantumId int64 `json:"quantum_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FBSPostingTariffication struct {
|
||||||
|
// Current shipping rate as a percentage
|
||||||
|
CurrentTariffRate float64 `json:"current_tariff_rate"`
|
||||||
|
|
||||||
|
// Type of shipping rate adjustment: discount or surcharge
|
||||||
|
CurrentTariffType string `json:"current_tariff_type"`
|
||||||
|
|
||||||
|
// Current amount of discount or surcharge
|
||||||
|
CurrentTariffCharge string `json:"current_tariff_charge"`
|
||||||
|
|
||||||
|
// Currency of the amount
|
||||||
|
CurrencyTariffCurrencyCode string `json:"current_tariff_charge_currency_code"`
|
||||||
|
|
||||||
|
// Percentage by which the shipping rate is adjusted
|
||||||
|
// after the time specified in the next_tariff_starts_at parameter
|
||||||
|
NextTariffRate float64 `json:"next_tariff_rate"`
|
||||||
|
|
||||||
|
// The adjustment type applied to the shipping rate
|
||||||
|
// after the time specified in the next_tariff_starts_at parameter:
|
||||||
|
// discount or surcharge
|
||||||
|
NextTariffType string `json:"next_tariff_type"`
|
||||||
|
|
||||||
|
// Discount or surcharge amount applied during the next shipping rate adjustment step
|
||||||
|
NextTariffCharge string `json:"next_tariff_charge"`
|
||||||
|
|
||||||
|
// Date and time when the new shipping rate is applied
|
||||||
|
NextTariffStartsAt time.Time `json:"next_tariff_starts_at"`
|
||||||
|
|
||||||
|
// New shipping rate currency
|
||||||
|
NextTariffCurrencyCode string `json:"next_tariff_charge_currency_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FBSPostingAddressee struct {
|
type FBSPostingAddressee struct {
|
||||||
@@ -191,7 +237,7 @@ type FBSPostingAddressee struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FBSPostingAnalyticsData struct {
|
type FBSPostingAnalyticsData struct {
|
||||||
// Delivery city
|
// Delivery city. Only for rFBS shipments
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
|
|
||||||
// Delivery start date and time
|
// Delivery start date and time
|
||||||
@@ -215,7 +261,7 @@ type FBSPostingAnalyticsData struct {
|
|||||||
// Payment method
|
// Payment method
|
||||||
PaymentTypeGroupName PaymentTypeGroupName `json:"payment_type_group_name"`
|
PaymentTypeGroupName PaymentTypeGroupName `json:"payment_type_group_name"`
|
||||||
|
|
||||||
// Delivery region
|
// Delivery region. Only for rFBS shipments
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
|
||||||
// Delivery service
|
// Delivery service
|
||||||
@@ -541,6 +587,11 @@ type GetFBSShipmentsListFilter struct {
|
|||||||
// Order identifier
|
// Order identifier
|
||||||
OrderId int64 `json:"order_id"`
|
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
|
// Delivery service identifier
|
||||||
ProviderId []int64 `json:"provider_id"`
|
ProviderId []int64 `json:"provider_id"`
|
||||||
|
|
||||||
@@ -912,6 +963,9 @@ type GetShipmentDataByIdentifierResult struct {
|
|||||||
// Analytics data
|
// Analytics data
|
||||||
AnalyticsData GetShipmentDataByIdentifierResultAnalyticsData `json:"analytics_data"`
|
AnalyticsData GetShipmentDataByIdentifierResultAnalyticsData `json:"analytics_data"`
|
||||||
|
|
||||||
|
// Available actions and shipment information
|
||||||
|
AvailableActions []string `json:"available_actions"`
|
||||||
|
|
||||||
// Shipment barcodes
|
// Shipment barcodes
|
||||||
Barcodes FBSBarcode `json:"barcodes"`
|
Barcodes FBSBarcode `json:"barcodes"`
|
||||||
|
|
||||||
@@ -999,6 +1053,9 @@ type GetShipmentDataByIdentifierResult struct {
|
|||||||
|
|
||||||
// Shipment tracking number
|
// Shipment tracking number
|
||||||
TrackingNumber string `json:"tracking_number"`
|
TrackingNumber string `json:"tracking_number"`
|
||||||
|
|
||||||
|
// Details on shipping rate
|
||||||
|
Tariffication []FBSPostingTariffication `json:"tariffication"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetShipmentDataByIdentifierResultAdditionalData struct {
|
type GetShipmentDataByIdentifierResultAdditionalData struct {
|
||||||
@@ -1020,7 +1077,7 @@ type GetShipmentDataByIdentifierResultAddressee struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
||||||
// Delivery city
|
// Delivery city. Only for rFBS shipments
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
|
|
||||||
// Delivery start date and time
|
// Delivery start date and time
|
||||||
@@ -1043,7 +1100,7 @@ type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
|||||||
// Payment method
|
// Payment method
|
||||||
PaymentTypeGroupName string `json:"payment_type_group_name"`
|
PaymentTypeGroupName string `json:"payment_type_group_name"`
|
||||||
|
|
||||||
// Delivery region
|
// Delivery region. Only for rFBS shipments
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
|
||||||
// Delivery service
|
// Delivery service
|
||||||
@@ -2188,6 +2245,12 @@ type AvailableFreightsListResult struct {
|
|||||||
// Number of already packaged shipments
|
// Number of already packaged shipments
|
||||||
MandatoryPackagedCount int32 `json:"mandatory_packaged_count"`
|
MandatoryPackagedCount int32 `json:"mandatory_packaged_count"`
|
||||||
|
|
||||||
|
// Recommended local time of shipping to the pick-up point
|
||||||
|
RecommendedTimeLocal string `json:"recommended_time_local"`
|
||||||
|
|
||||||
|
// Time zone offset of the recommended shipping time from UTC-0 in minutes
|
||||||
|
RecommendedTimeUTCOffset int32 `json:"recommended_time_utc_offset_in_minutes"`
|
||||||
|
|
||||||
// Delivery service icon link
|
// Delivery service icon link
|
||||||
TPLProviderIconURL string `json:"tpl_provider_icon_url"`
|
TPLProviderIconURL string `json:"tpl_provider_icon_url"`
|
||||||
|
|
||||||
@@ -2525,20 +2588,11 @@ type CancelSendingResponse struct {
|
|||||||
|
|
||||||
// Use this method if you cannot send some of the products from the shipment.
|
// Use this method if you cannot send some of the products from the shipment.
|
||||||
//
|
//
|
||||||
// If you are using the rFBS scheme, you have the following cancellation reason identifiers (`cancel_reason_id`) available:
|
// To get the cancel_reason_id cancellation reason identifiers
|
||||||
//
|
// when working with the FBS or rFBS schemes,
|
||||||
// 352—product is out of stock;
|
// use the /v2/posting/fbs/cancel-reason/list method.
|
||||||
// 400—only defective products left;
|
|
||||||
// 401—cancellation from arbitration;
|
|
||||||
// 402—other reason;
|
|
||||||
// 665—the customer did not pick the order;
|
|
||||||
// 666—delivery is not available in the region;
|
|
||||||
// 667—order was lost by the delivery service.
|
|
||||||
// The last 4 reasons are available for shipments in the "Delivering" and "Courier on the way" statuses.
|
|
||||||
//
|
//
|
||||||
// You can't cancel presumably delivered orders.
|
// You can't cancel presumably delivered orders.
|
||||||
//
|
|
||||||
// If `cancel_reason_id` parameter value is 402, fill the `cancel_reason_message` field.
|
|
||||||
func (c FBS) CancelSending(ctx context.Context, params *CancelSendingParams) (*CancelSendingResponse, error) {
|
func (c FBS) CancelSending(ctx context.Context, params *CancelSendingParams) (*CancelSendingResponse, error) {
|
||||||
url := "/v2/posting/fbs/product/cancel"
|
url := "/v2/posting/fbs/product/cancel"
|
||||||
|
|
||||||
@@ -3016,3 +3070,167 @@ func (c FBS) GetCarriage(ctx context.Context, params *GetCarriageParams) (*GetCa
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetCancellationReasonsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result []GetCancellationReasonsResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetCancellationReasonsResult struct {
|
||||||
|
// Cancellation reasons identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
|
// Shipment cancellation result. true if the request is available for cancellation
|
||||||
|
IsAvailableForCancellation bool `json:"is_available_for_cancellation"`
|
||||||
|
|
||||||
|
// Category name
|
||||||
|
Title string `json:"title"`
|
||||||
|
|
||||||
|
// Shipment cancellation initiator
|
||||||
|
TypeId string `json:"type_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBS) GetCancellationReasons(ctx context.Context) (*GetCancellationReasonsResponse, error) {
|
||||||
|
url := "/v1/posting/fbo/cancel-reason/list"
|
||||||
|
|
||||||
|
resp := &GetCancellationReasonsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(ctx, http.MethodPost, url, nil, resp, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.CopyCommonResponse(&resp.CommonResponse)
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetShippingDateParams struct {
|
||||||
|
// New shipping date
|
||||||
|
NewCutoffDate time.Time `json:"new_cutoff_date"`
|
||||||
|
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetShippingDateResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// true if the new date is set
|
||||||
|
Result bool `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBS) SetShippingDate(ctx context.Context, params *SetShippingDateParams) (*SetShippingDateResponse, error) {
|
||||||
|
url := "/v1/posting/cutoff/set"
|
||||||
|
|
||||||
|
resp := &SetShippingDateResponse{}
|
||||||
|
|
||||||
|
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 SplitOrderParams struct {
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Shipments list the order will be split into. You can split one order per one request
|
||||||
|
Postings []SplitOrderParamPosting `json:"postings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitOrderParamPosting struct {
|
||||||
|
Products []SplitOrderPostingProduct `json:"products"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitOrderResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Original shipment details
|
||||||
|
ParentPosting SplitOrderPosting `json:"parent_posting"`
|
||||||
|
|
||||||
|
// List of shipments the order was split into
|
||||||
|
Postings []SplitOrderPosting `json:"postings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitOrderPosting struct {
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// List of products in the shipment
|
||||||
|
Products []SplitOrderPostingProduct `json:"products"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitOrderPostingProduct struct {
|
||||||
|
// FBS product identifier in the Ozon system, SKU
|
||||||
|
ProductId int64 `json:"product_id"`
|
||||||
|
|
||||||
|
// Product quantity
|
||||||
|
Quantity int64 `json:"quantity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBS) SplitOrder(ctx context.Context, params *SplitOrderParams) (*SplitOrderResponse, error) {
|
||||||
|
url := "/v1/posting/fbs/split"
|
||||||
|
|
||||||
|
resp := &SplitOrderResponse{}
|
||||||
|
|
||||||
|
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 ListUnpaidProductsParams struct {
|
||||||
|
// Cursor for the next data sample
|
||||||
|
Cursor string `json:"cursor"`
|
||||||
|
|
||||||
|
// Number of values in the response
|
||||||
|
Limit int32 `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListUnpaidProductsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
Products []UnpaidProduct `json:"products"`
|
||||||
|
|
||||||
|
// Cursor for the next data sample
|
||||||
|
Cursor string `json:"cursor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnpaidProduct struct {
|
||||||
|
// Product identifier
|
||||||
|
ProductId int64 `json:"product_id"`
|
||||||
|
|
||||||
|
// Product identifier in the seller's system
|
||||||
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
|
// Product quantity, pcs
|
||||||
|
Quantity int32 `json:"quantity"`
|
||||||
|
|
||||||
|
// Product name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Link to product image
|
||||||
|
ImageURL string `json:"image_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c FBS) ListUnpaidProducts(ctx context.Context, params *ListUnpaidProductsParams) (*ListUnpaidProductsResponse, error) {
|
||||||
|
url := "/v1/posting/unpaid-legal/product/list"
|
||||||
|
|
||||||
|
resp := &ListUnpaidProductsResponse{}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
304
ozon/fbs_test.go
304
ozon/fbs_test.go
@@ -66,6 +66,7 @@ func TestListUnprocessedShipments(t *testing.T) {
|
|||||||
"cancellation_initiator": ""
|
"cancellation_initiator": ""
|
||||||
},
|
},
|
||||||
"customer": null,
|
"customer": null,
|
||||||
|
"quantum_id": 0,
|
||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
"currency_code": "RUB",
|
"currency_code": "RUB",
|
||||||
@@ -146,7 +147,20 @@ func TestListUnprocessedShipments(t *testing.T) {
|
|||||||
"requirements": {
|
"requirements": {
|
||||||
"products_requiring_gtd": [],
|
"products_requiring_gtd": [],
|
||||||
"products_requiring_country": []
|
"products_requiring_country": []
|
||||||
|
},
|
||||||
|
"tariffication": [
|
||||||
|
{
|
||||||
|
"current_tariff_rate": 0,
|
||||||
|
"current_tariff_type": "",
|
||||||
|
"current_tariff_charge": "",
|
||||||
|
"current_tariff_charge_currency_code": "",
|
||||||
|
"next_tariff_rate": 0,
|
||||||
|
"next_tariff_type": "",
|
||||||
|
"next_tariff_charge": "",
|
||||||
|
"next_tariff_starts_at": "2023-11-13T08:05:57.657Z",
|
||||||
|
"next_tariff_charge_currency_code": ""
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"count": 55
|
"count": 55
|
||||||
@@ -264,11 +278,25 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
|||||||
"analytics_data": null,
|
"analytics_data": null,
|
||||||
"financial_data": null,
|
"financial_data": null,
|
||||||
"is_express": false,
|
"is_express": false,
|
||||||
|
"quantum_id": 0,
|
||||||
"requirements": {
|
"requirements": {
|
||||||
"products_requiring_gtd": [],
|
"products_requiring_gtd": [],
|
||||||
"products_requiring_country": [],
|
"products_requiring_country": [],
|
||||||
"products_requiring_mandatory_mark": []
|
"products_requiring_mandatory_mark": []
|
||||||
|
},
|
||||||
|
"tariffication": [
|
||||||
|
{
|
||||||
|
"current_tariff_rate": 0,
|
||||||
|
"current_tariff_type": "",
|
||||||
|
"current_tariff_charge": "",
|
||||||
|
"current_tariff_charge_currency_code": "",
|
||||||
|
"next_tariff_rate": 0,
|
||||||
|
"next_tariff_type": "",
|
||||||
|
"next_tariff_charge": "",
|
||||||
|
"next_tariff_starts_at": "2023-11-13T08:05:57.657Z",
|
||||||
|
"next_tariff_charge_currency_code": ""
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_next": true
|
"has_next": true
|
||||||
@@ -616,7 +644,20 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
|||||||
"products_requiring_gtd": [],
|
"products_requiring_gtd": [],
|
||||||
"products_requiring_country": []
|
"products_requiring_country": []
|
||||||
},
|
},
|
||||||
"product_exemplars": null
|
"product_exemplars": null,
|
||||||
|
"tariffication": [
|
||||||
|
{
|
||||||
|
"current_tariff_rate": 0,
|
||||||
|
"current_tariff_type": "",
|
||||||
|
"current_tariff_charge": "",
|
||||||
|
"current_tariff_charge_currency_code": "",
|
||||||
|
"next_tariff_rate": 0,
|
||||||
|
"next_tariff_type": "",
|
||||||
|
"next_tariff_charge": "",
|
||||||
|
"next_tariff_starts_at": "2023-11-13T08:05:57.657Z",
|
||||||
|
"next_tariff_charge_currency_code": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
@@ -1883,6 +1924,8 @@ func TestAvailableFreightsList(t *testing.T) {
|
|||||||
"has_entrusted_acceptance": true,
|
"has_entrusted_acceptance": true,
|
||||||
"mandatory_postings_count": 0,
|
"mandatory_postings_count": 0,
|
||||||
"mandatory_packaged_count": 0,
|
"mandatory_packaged_count": 0,
|
||||||
|
"recommended_time_local": "string",
|
||||||
|
"recommended_time_utc_offset_in_minutes": 0,
|
||||||
"tpl_provider_icon_url": "string",
|
"tpl_provider_icon_url": "string",
|
||||||
"tpl_provider_name": "string",
|
"tpl_provider_name": "string",
|
||||||
"warehouse_city": "string",
|
"warehouse_city": "string",
|
||||||
@@ -3010,3 +3053,262 @@ func TestGetCarriage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetCancellationReasons(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"},
|
||||||
|
`{
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"id": 352,
|
||||||
|
"title": "The products ran out at the seller's warehouse",
|
||||||
|
"type_id": "seller",
|
||||||
|
"is_available_for_cancellation": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 401,
|
||||||
|
"title": "Seller rejects arbitration",
|
||||||
|
"type_id": "seller",
|
||||||
|
"is_available_for_cancellation": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 402,
|
||||||
|
"title": "Other (seller's fault)",
|
||||||
|
"type_id": "seller",
|
||||||
|
"is_available_for_cancellation": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 666,
|
||||||
|
"title": "Return from the delivery service: there is no delivery to the specified region",
|
||||||
|
"type_id": "seller",
|
||||||
|
"is_available_for_cancellation": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// 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))
|
||||||
|
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||||
|
resp, err := c.FBS().GetCancellationReasons(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetCancellationReasonsResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetShippingDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *SetShippingDateParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&SetShippingDateParams{
|
||||||
|
NewCutoffDate: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": true
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&SetShippingDateParams{},
|
||||||
|
`{
|
||||||
|
"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.FBS().SetShippingDate(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &SetShippingDateResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSplitOrder(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *SplitOrderParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&SplitOrderParams{
|
||||||
|
PostingNumber: "string",
|
||||||
|
Postings: []SplitOrderParamPosting{
|
||||||
|
{
|
||||||
|
Products: []SplitOrderPostingProduct{
|
||||||
|
{
|
||||||
|
ProductId: 1,
|
||||||
|
Quantity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"parent_posting": {
|
||||||
|
"posting_number": "string",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"product_id": 0,
|
||||||
|
"quantity": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"postings": [
|
||||||
|
{
|
||||||
|
"posting_number": "string",
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"product_id": 0,
|
||||||
|
"quantity": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&SplitOrderParams{},
|
||||||
|
`{
|
||||||
|
"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.FBS().SplitOrder(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &SplitOrderResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListUnpaidProducts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ListUnpaidProductsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ListUnpaidProductsParams{
|
||||||
|
Cursor: "hCGiPPopcBFMgMErdzaCEpzQfinuPyEhUoSmBMADuoFAhBjXeA==",
|
||||||
|
Limit: 1000,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"product_id": 145123054,
|
||||||
|
"offer_id": "10032",
|
||||||
|
"quantity": 1,
|
||||||
|
"name": "Телевизор LG",
|
||||||
|
"image_url": "https://cdn1.ozon.ru/multimedia/10741275.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cursor": "hCGiPPopcBFMgMErdzaCEpzQfinuPyEhUoSmBMADuoFAhBjXeA=="
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ListUnpaidProductsParams{},
|
||||||
|
`{
|
||||||
|
"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.FBS().ListUnpaidProducts(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ListUnpaidProductsResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
159
ozon/finance.go
159
ozon/finance.go
@@ -13,8 +13,11 @@ type Finance struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ReportOnSoldProductsParams struct {
|
type ReportOnSoldProductsParams struct {
|
||||||
// Time period in the `YYYY-MM` format
|
// Month
|
||||||
Date string `json:"date"`
|
Month int32 `json:"month"`
|
||||||
|
|
||||||
|
// Year
|
||||||
|
Year int32 `json:"year"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReportOnSoldProductsResponse struct {
|
type ReportOnSoldProductsResponse struct {
|
||||||
@@ -34,7 +37,7 @@ type ReportonSoldProductsResult struct {
|
|||||||
|
|
||||||
type ReportOnSoldProductsResultHeader struct {
|
type ReportOnSoldProductsResultHeader struct {
|
||||||
// Report ID
|
// Report ID
|
||||||
Id string `json:"num"`
|
Id string `json:"number"`
|
||||||
|
|
||||||
// Report generation date
|
// Report generation date
|
||||||
DocDate string `json:"doc_date"`
|
DocDate string `json:"doc_date"`
|
||||||
@@ -43,10 +46,10 @@ type ReportOnSoldProductsResultHeader struct {
|
|||||||
ContractDate string `json:"contract_date"`
|
ContractDate string `json:"contract_date"`
|
||||||
|
|
||||||
// Offer agreement number
|
// Offer agreement number
|
||||||
ContractNum string `json:"contract_num"`
|
ContractNum string `json:"contract_number"`
|
||||||
|
|
||||||
// Currency of your prices
|
// Currency of your prices
|
||||||
CurrencyCode string `json:"currency_code"`
|
CurrencySysName string `json:"currency_sys_name"`
|
||||||
|
|
||||||
// Amount to accrue
|
// Amount to accrue
|
||||||
DocAmount float64 `json:"doc_amount"`
|
DocAmount float64 `json:"doc_amount"`
|
||||||
@@ -64,13 +67,13 @@ type ReportOnSoldProductsResultHeader struct {
|
|||||||
PayerName string `json:"payer_name"`
|
PayerName string `json:"payer_name"`
|
||||||
|
|
||||||
// Recipient's TIN
|
// Recipient's TIN
|
||||||
RecipientINN string `json:"rcv_inn"`
|
RecipientINN string `json:"receiver_inn"`
|
||||||
|
|
||||||
// Recipient's Tax Registration Reason Code (KPP)
|
// Recipient's Tax Registration Reason Code (KPP)
|
||||||
RecipientKPP string `json:"rcv_kpp"`
|
RecipientKPP string `json:"receiver_kpp"`
|
||||||
|
|
||||||
// Recipient's name
|
// Recipient's name
|
||||||
RecipientName string `json:"rcv_name"`
|
RecipientName string `json:"receiver_name"`
|
||||||
|
|
||||||
// Period start in the report
|
// Period start in the report
|
||||||
StartDate string `json:"start_date"`
|
StartDate string `json:"start_date"`
|
||||||
@@ -81,13 +84,28 @@ type ReportOnSoldProductsResultHeader struct {
|
|||||||
|
|
||||||
type ReportOnSoldProductsResultRow struct {
|
type ReportOnSoldProductsResultRow struct {
|
||||||
// Row number
|
// Row number
|
||||||
RowNumber int32 `json:"row_number"`
|
RowNumber int32 `json:"rowNumber"`
|
||||||
|
|
||||||
// Product ID
|
// Product Information
|
||||||
ProductId int64 `json:"product_id"`
|
Item ReturnOnSoldProduct `json:"item"`
|
||||||
|
|
||||||
|
// Commission including the quantity of products, discounts and extra charges.
|
||||||
|
// Ozon compensates it for the returned products
|
||||||
|
ReturnCommission ReturnCommission `json:"return_commission"`
|
||||||
|
|
||||||
|
// Percentage of sales commission by category
|
||||||
|
CommissionRatio float64 `json:"commission_ratio"`
|
||||||
|
|
||||||
|
// Delivery fee
|
||||||
|
DeliveryCommission ReturnCommission `json:"delivery_commission"`
|
||||||
|
|
||||||
|
// Seller's discounted price
|
||||||
|
SellerPricePerInstance float64 `json:"seller_price_per_instance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnOnSoldProduct struct {
|
||||||
// Product name
|
// Product name
|
||||||
ProductName string `json:"product_name"`
|
ProductName string `json:"name"`
|
||||||
|
|
||||||
// Product barcode
|
// Product barcode
|
||||||
Barcode string `json:"barcode"`
|
Barcode string `json:"barcode"`
|
||||||
@@ -95,58 +113,39 @@ type ReportOnSoldProductsResultRow struct {
|
|||||||
// Product identifier in the seller's system
|
// Product identifier in the seller's system
|
||||||
OfferId string `json:"offer_id"`
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
// Sales commission by category
|
SKU int64 `json:"sku"`
|
||||||
CommissionPercent float64 `json:"commission_percent"`
|
}
|
||||||
|
|
||||||
// Seller's price with their discount
|
type ReturnCommission struct {
|
||||||
Price float64 `json:"price"`
|
// Amount
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
|
||||||
// Selling price: the price at which the customer purchased the product. For sold products
|
// Points for discounts
|
||||||
PriceSale float64 `json:"price_sale"`
|
Bonus float64 `json:"bonus"`
|
||||||
|
|
||||||
// Sold for amount.
|
|
||||||
//
|
|
||||||
// Sold products cost considering the quantity and regional coefficients. Calculation is made by the sale_amount price
|
|
||||||
SaleAmount float64 `json:"sale_amount"`
|
|
||||||
|
|
||||||
// Commission for sold products, including discounts and extra charges
|
// Commission for sold products, including discounts and extra charges
|
||||||
SaleCommission float64 `json:"sale_commission"`
|
Commission float64 `json:"commission"`
|
||||||
|
|
||||||
// Extra charge at the expense of Ozon.
|
// Additional payment at the expense of Ozon
|
||||||
//
|
Compensation float64 `json:"compensation"`
|
||||||
// Amount that Ozon will compensate the seller if the Ozon discount is greater than or equal to the sales commission
|
|
||||||
SaleDiscount float64 `json:"sale_discount"`
|
|
||||||
|
|
||||||
// Total accrual for the products sold.
|
// Price per item
|
||||||
//
|
PricePerInstance float64 `json:"price_per_instance"`
|
||||||
// Amount after deduction of sales commission, application of discounts and extra charges
|
|
||||||
SalePriceSeller float64 `json:"sale_price_seller"`
|
|
||||||
|
|
||||||
// Quantity of products sold at the price_sale price
|
// Product quantity
|
||||||
SaleQuantity int32 `json:"sale_qty"`
|
Quantity int32 `json:"quantity"`
|
||||||
|
|
||||||
// Price at which the customer purchased the product. For returned products
|
// Ozon referral fee
|
||||||
ReturnSale float64 `json:"return_sale"`
|
StandardFee float64 `json:"standard_fee"`
|
||||||
|
|
||||||
// Cost of returned products, taking into account the quantity and regional coefficients.
|
// Payouts on partner loyalty mechanics: green prices
|
||||||
// Calculation is carried out at the return_sale price
|
BankCoinvestment float64 `json:"bank_coinvestment"`
|
||||||
ReturnAmount float64 `json:"return_amount"`
|
|
||||||
|
|
||||||
// Commission including the quantity of products, discounts and extra charges.
|
// Payouts on partner loyalty mechanics: stars
|
||||||
// Ozon compensates it for the returned products
|
Stars float64 `json:"stars"`
|
||||||
ReturnCommission float64 `json:"return_commission"`
|
|
||||||
|
|
||||||
// Extra charge at the expense of Ozon.
|
// Total accrual
|
||||||
//
|
Total float64 `json:"total"`
|
||||||
// Amount of the discount at the expense of Ozon on returned products.
|
|
||||||
// Ozon will compensate it to the seller if the Ozon discount is greater than or equal to the sales commission
|
|
||||||
ReturnDiscount float64 `json:"return_discount"`
|
|
||||||
|
|
||||||
// Amount charged to the seller for returned products after deducing sales commissions, applying discounts and extra charges
|
|
||||||
ReturnPriceSeller float64 `json:"return_price_seller"`
|
|
||||||
|
|
||||||
// Quantity of returned products
|
|
||||||
ReturnQuantity int32 `json:"return_qty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns information on products sold and returned within a month. Canceled or non-purchased products are not included.
|
// Returns information on products sold and returned within a month. Canceled or non-purchased products are not included.
|
||||||
@@ -404,3 +403,57 @@ func (c Finance) ListTransactions(ctx context.Context, params *ListTransactionsP
|
|||||||
|
|
||||||
return resp, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,48 +23,63 @@ func TestReportOnSoldProducts(t *testing.T) {
|
|||||||
http.StatusOK,
|
http.StatusOK,
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
&ReportOnSoldProductsParams{
|
&ReportOnSoldProductsParams{
|
||||||
Date: "2022-09",
|
Month: 9,
|
||||||
|
Year: 2022,
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
"result": {
|
"result": {
|
||||||
"header": {
|
"header": {
|
||||||
"doc_date": "2022-09-22",
|
"contract_date": "string",
|
||||||
"num": "string",
|
"contract_number": "string",
|
||||||
"start_date": "2022-09-02",
|
"currency_sys_name": "string",
|
||||||
"stop_date": "2022-09-22",
|
"doc_amount": 0,
|
||||||
"contract_date": "2022-09-02",
|
"doc_date": "string",
|
||||||
"contract_num": "string",
|
"number": "string",
|
||||||
"payer_name": "string",
|
|
||||||
"payer_inn": "string",
|
"payer_inn": "string",
|
||||||
"payer_kpp": "string",
|
"payer_kpp": "string",
|
||||||
"rcv_name": "string",
|
"payer_name": "string",
|
||||||
"rcv_inn": "string",
|
"receiver_inn": "string",
|
||||||
"rcv_kpp": "string",
|
"receiver_kpp": "string",
|
||||||
"doc_amount": 1,
|
"receiver_name": "string",
|
||||||
"vat_amount": 1,
|
"start_date": "string",
|
||||||
"currency_code": "string"
|
"stop_date": "string",
|
||||||
|
"vat_amount": 0
|
||||||
},
|
},
|
||||||
"rows": [
|
"rows": [
|
||||||
{
|
{
|
||||||
"row_number": 0,
|
"commission_ratio": 0,
|
||||||
"product_id": 0,
|
"delivery_commission": {
|
||||||
"product_name": "string",
|
"amount": 0,
|
||||||
"offer_id": "string",
|
"bonus": 0,
|
||||||
|
"commission": 0,
|
||||||
|
"compensation": 0,
|
||||||
|
"price_per_instance": 0,
|
||||||
|
"quantity": 0,
|
||||||
|
"standard_fee": 0,
|
||||||
|
"bank_coinvestment": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"total": 0
|
||||||
|
},
|
||||||
|
"item": {
|
||||||
"barcode": "string",
|
"barcode": "string",
|
||||||
"price": 0,
|
"name": "string",
|
||||||
"commission_percent": 0,
|
"offer_id": "string",
|
||||||
"price_sale": 0,
|
"sku": 0
|
||||||
"sale_qty": 0,
|
},
|
||||||
"sale_amount": 0,
|
"return_commission": {
|
||||||
"sale_discount": 0,
|
"amount": 0,
|
||||||
"sale_commission": 0,
|
"bonus": 0,
|
||||||
"sale_price_seller": 0,
|
"commission": 0,
|
||||||
"return_sale": 0,
|
"compensation": 0,
|
||||||
"return_qty": 0,
|
"price_per_instance": 0,
|
||||||
"return_amount": 0,
|
"quantity": 0,
|
||||||
"return_discount": 0,
|
"standard_fee": 0,
|
||||||
"return_commission": 0,
|
"bank_coinvestment": 0,
|
||||||
"return_price_seller": 0
|
"stars": 0,
|
||||||
|
"total": 0
|
||||||
|
},
|
||||||
|
"rowNumber": 0,
|
||||||
|
"seller_price_per_instance": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -271,3 +286,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
|
strategies *Strategies
|
||||||
barcodes *Barcodes
|
barcodes *Barcodes
|
||||||
passes *Passes
|
passes *Passes
|
||||||
|
clusters *Clusters
|
||||||
|
quants *Quants
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) Analytics() *Analytics {
|
func (c Client) Analytics() *Analytics {
|
||||||
@@ -124,6 +126,14 @@ func (c Client) Passes() *Passes {
|
|||||||
return c.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)
|
type ClientOption func(c *ClientOptions)
|
||||||
|
|
||||||
func WithHttpClient(httpClient core.HttpClient) ClientOption {
|
func WithHttpClient(httpClient core.HttpClient) ClientOption {
|
||||||
@@ -188,6 +198,8 @@ func NewClient(opts ...ClientOption) *Client {
|
|||||||
strategies: &Strategies{client: coreClient},
|
strategies: &Strategies{client: coreClient},
|
||||||
barcodes: &Barcodes{client: coreClient},
|
barcodes: &Barcodes{client: coreClient},
|
||||||
passes: &Passes{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},
|
strategies: &Strategies{client: coreClient},
|
||||||
barcodes: &Barcodes{client: coreClient},
|
barcodes: &Barcodes{client: coreClient},
|
||||||
passes: &Passes{client: coreClient},
|
passes: &Passes{client: coreClient},
|
||||||
|
clusters: &Clusters{client: coreClient},
|
||||||
|
quants: &Quants{client: coreClient},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
375
ozon/products.go
375
ozon/products.go
@@ -525,6 +525,14 @@ type UpdateQuantityStockProductsStock struct {
|
|||||||
// Product identifier
|
// Product identifier
|
||||||
ProductId int64 `json:"product_id"`
|
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
|
// Quantity
|
||||||
Stock int64 `json:"stock"`
|
Stock int64 `json:"stock"`
|
||||||
|
|
||||||
@@ -549,6 +557,12 @@ type UpdateQuantityStockProductsResult struct {
|
|||||||
// Product identifier
|
// Product identifier
|
||||||
ProductId int64 `json:"product_id"`
|
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
|
// If the request was completed successfully and the stocks are updated — true
|
||||||
Updated bool `json:"updated"`
|
Updated bool `json:"updated"`
|
||||||
|
|
||||||
@@ -686,6 +700,14 @@ type UpdatePricesPrice struct {
|
|||||||
|
|
||||||
// Product identifier
|
// Product identifier
|
||||||
ProductId int64 `json:"product_id"`
|
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 {
|
type UpdatePricesResponse struct {
|
||||||
@@ -714,8 +736,8 @@ type UpdatePricesResultError struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allows you to change a price of one or more products.
|
// Allows you to change a price of one or more products.
|
||||||
// You can change prices for 1000 products in one request.
|
// The price of each product can be updated no more than 10 times per hour.
|
||||||
// To reset old_price, set 0 for this parameter
|
// To reset old_price, set 0 for this parameter.
|
||||||
func (c Products) UpdatePrices(ctx context.Context, params *UpdatePricesParams) (*UpdatePricesResponse, error) {
|
func (c Products) UpdatePrices(ctx context.Context, params *UpdatePricesParams) (*UpdatePricesResponse, error) {
|
||||||
url := "/v1/product/import/prices"
|
url := "/v1/product/import/prices"
|
||||||
|
|
||||||
@@ -822,6 +844,12 @@ type CreateOrUpdateProductItem struct {
|
|||||||
// - IS_NO_CODE_SERVICE
|
// - IS_NO_CODE_SERVICE
|
||||||
ServiceType string `json:"service_type" default:"IS_CODE_SERVICE"`
|
ServiceType string `json:"service_type" default:"IS_CODE_SERVICE"`
|
||||||
|
|
||||||
|
// Product type identifier.
|
||||||
|
// You can get values from the type_id parameter in the `/v1/description-category/tree` method response.
|
||||||
|
// When filling this parameter in,
|
||||||
|
// you can leave out the attributes attribute if it has the `id:8229` parameter
|
||||||
|
TypeId int64 `json:"type_id"`
|
||||||
|
|
||||||
// VAT rate for the product:
|
// VAT rate for the product:
|
||||||
// - 0 — not subject to VAT,
|
// - 0 — not subject to VAT,
|
||||||
// - 0.1 — 10%,
|
// - 0.1 — 10%,
|
||||||
@@ -958,6 +986,10 @@ type GetListOfProductsResultItem struct {
|
|||||||
ProductId int64 `json:"product_id"`
|
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) {
|
func (c Products) GetListOfProducts(ctx context.Context, params *GetListOfProductsParams) (*GetListOfProductsResponse, error) {
|
||||||
url := "/v2/product/list"
|
url := "/v2/product/list"
|
||||||
|
|
||||||
@@ -1168,9 +1200,11 @@ type CreateProductByOzonIDResult struct {
|
|||||||
UnmatchedSKUList []int64 `json:"unmatched_sku_list"`
|
UnmatchedSKUList []int64 `json:"unmatched_sku_list"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a product by the specified Ozon ID. The number of products is unlimited.
|
// The method creates a copy of the product description page with the specified SKU.
|
||||||
//
|
//
|
||||||
// It's not possible to update products using Ozon ID
|
// You cannot create a copy if the seller has prohibited the copying of their PDPs.
|
||||||
|
//
|
||||||
|
// It's not possible to update products using SKU.
|
||||||
func (c Products) CreateProductByOzonID(ctx context.Context, params *CreateProductByOzonIDParams) (*CreateProductByOzonIDResponse, error) {
|
func (c Products) CreateProductByOzonID(ctx context.Context, params *CreateProductByOzonIDParams) (*CreateProductByOzonIDResponse, error) {
|
||||||
url := "/v1/product/import-by-sku"
|
url := "/v1/product/import-by-sku"
|
||||||
|
|
||||||
@@ -1412,9 +1446,6 @@ type GetDescriptionOfProductResult struct {
|
|||||||
// Product characteristic identifier
|
// Product characteristic identifier
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
// Identifier for subsequent batch loading of images
|
|
||||||
ImageGroupId string `json:"image_group_id"`
|
|
||||||
|
|
||||||
// Array of links to product images
|
// Array of links to product images
|
||||||
Images []GetDescriptionOfProductResultImage `json:"images"`
|
Images []GetDescriptionOfProductResultImage `json:"images"`
|
||||||
|
|
||||||
@@ -1430,6 +1461,9 @@ type GetDescriptionOfProductResult struct {
|
|||||||
// Array of PDF files
|
// Array of PDF files
|
||||||
PDFList []GetDescriptionOfProductResultPDF `json:"pdf_list"`
|
PDFList []GetDescriptionOfProductResultPDF `json:"pdf_list"`
|
||||||
|
|
||||||
|
// Product type identifier
|
||||||
|
TypeId int64 `json:"type_id"`
|
||||||
|
|
||||||
// Weight of product in the package
|
// Weight of product in the package
|
||||||
Weight int32 `json:"weight"`
|
Weight int32 `json:"weight"`
|
||||||
|
|
||||||
@@ -1524,6 +1558,92 @@ func (c Products) GetDescriptionOfProduct(ctx context.Context, params *GetDescri
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsFilter struct {
|
||||||
|
ProductId []string `json:"product_id,omitempty"`
|
||||||
|
OfferId []string `json:"offer_id,omitempty"`
|
||||||
|
Sku []string `json:"sku,omitempty"`
|
||||||
|
Visibility string `json:"visibility,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsParams struct {
|
||||||
|
Filter GetDescriptionOfProductsFilter `json:"filter"`
|
||||||
|
LastId string `json:"last_id,omitempty"`
|
||||||
|
Limit int64 `json:"limit,omitempty"`
|
||||||
|
SortBy string `json:"sort_by,omitempty"`
|
||||||
|
SortDirection string `json:"sort_dir,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
Result []GetDescriptionOfProductsResult `json:"result"`
|
||||||
|
Total int32 `json:"total"`
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsResult struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
OfferId string `json:"offer_id"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
Depth int32 `json:"depth"`
|
||||||
|
Width int32 `json:"width"`
|
||||||
|
DimensionUnit string `json:"dimension_unit"`
|
||||||
|
Weight int32 `json:"weight"`
|
||||||
|
WeightUnit string `json:"weight_unit"`
|
||||||
|
DescriptionCategoryId int64 `json:"description_category_id"`
|
||||||
|
TypeId int64 `json:"type_id"`
|
||||||
|
PrimaryImage string `json:"primary_image"`
|
||||||
|
|
||||||
|
// new "model_info" structure
|
||||||
|
ModelInfo *ModelInfo `json:"model_info,omitempty"`
|
||||||
|
|
||||||
|
Images []string `json:"images"`
|
||||||
|
PDFList []string `json:"pdf_list"`
|
||||||
|
|
||||||
|
Attributes []GetDescriptionOfProductsAttribute `json:"attributes"`
|
||||||
|
ComplexAttributes []GetDescriptionOfProductsComplexAttribute `json:"complex_attributes"`
|
||||||
|
ColorImage string `json:"color_image"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModelInfo struct {
|
||||||
|
ModelId int64 `json:"model_id"`
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsAttribute struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
ComplexId int64 `json:"complex_id"`
|
||||||
|
Values []GetDescriptionOfProductsAttributeValue `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsAttributeValue struct {
|
||||||
|
DictionaryValueId int64 `json:"dictionary_value_id"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductsComplexAttribute struct {
|
||||||
|
Id int64 `json:"id,omitempty"`
|
||||||
|
ComplexId int64 `json:"complex_id,omitempty"`
|
||||||
|
Values []GetDescriptionOfProductsAttributeValue `json:"values,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// /v4/product/info/attributes
|
||||||
|
func (c Products) GetDescriptionOfProducts(ctx context.Context, params *GetDescriptionOfProductsParams) (*GetDescriptionOfProductsResponse, error) {
|
||||||
|
url := "/v4/product/info/attributes"
|
||||||
|
|
||||||
|
resp := &GetDescriptionOfProductsResponse{}
|
||||||
|
|
||||||
|
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 GetProductDescriptionParams struct {
|
type GetProductDescriptionParams struct {
|
||||||
// Product identifier in the seller's system
|
// Product identifier in the seller's system
|
||||||
OfferId string `json:"offer_id"`
|
OfferId string `json:"offer_id"`
|
||||||
@@ -1756,67 +1876,6 @@ func (c Products) RemoveProductWithoutSKU(ctx context.Context, params *RemovePro
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListGeoRestrictionsParams struct {
|
|
||||||
// Filter. To get all geo-restrictions, leave names blank and specify true in the only_visible parameter
|
|
||||||
Filter ListGeoRestrictionsFilter `json:"filter"`
|
|
||||||
|
|
||||||
// Order number of geo-restriction from which to output data in the response.
|
|
||||||
//
|
|
||||||
// If you specify 23 in this parameter, the first item in the restrictions list will output order_number = 24.
|
|
||||||
// If you want to get all geo-restrictions, pass 0 in this parameter
|
|
||||||
LastOrderNumber int64 `json:"last_order_number"`
|
|
||||||
|
|
||||||
// Number of items in the response
|
|
||||||
Limit int64 `json:"limit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListGeoRestrictionsFilter struct {
|
|
||||||
// List with city names
|
|
||||||
Names []string `json:"names"`
|
|
||||||
|
|
||||||
// Value visibility. We recommend always passing true in this parameter
|
|
||||||
OnlyVisible bool `json:"only_visible"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListGeoRestrictionsResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Restrictions
|
|
||||||
Restrictions []ListGeoRestrictionsRestriction `json:"restrictions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListGeoRestrictionsRestriction struct {
|
|
||||||
// Geo-restriction identifier
|
|
||||||
Id string `json:"id"`
|
|
||||||
|
|
||||||
// Item visibility
|
|
||||||
IsVisible bool `json:"is_visible"`
|
|
||||||
|
|
||||||
// City name
|
|
||||||
Name string `json:"name"`
|
|
||||||
|
|
||||||
// Geo-restriction order number.
|
|
||||||
//
|
|
||||||
// If you specify 23 in the last_order_number parameter in the request,
|
|
||||||
// the first item in the restrictions list will have order_number = 24
|
|
||||||
OrderNumber int64 `json:"order_number"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Get a list of geo-restrictions for services
|
|
||||||
func (c Products) ListGeoRestrictions(ctx context.Context, params *ListGeoRestrictionsParams) (*ListGeoRestrictionsResponse, error) {
|
|
||||||
url := "/v1/products/geo-restrictions-catalog-by-filter"
|
|
||||||
|
|
||||||
resp := &ListGeoRestrictionsResponse{}
|
|
||||||
|
|
||||||
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 UploadActivationCodesParams struct {
|
type UploadActivationCodesParams struct {
|
||||||
// Digital activation codes
|
// Digital activation codes
|
||||||
DigitalCodes []string `json:"digital_codes"`
|
DigitalCodes []string `json:"digital_codes"`
|
||||||
@@ -2403,3 +2462,183 @@ func (c Products) GetRelatedSKUs(ctx context.Context, params *GetRelatedSKUsPara
|
|||||||
|
|
||||||
return resp, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1611,7 +1611,6 @@ func TestGetDescriptionOfProduct(t *testing.T) {
|
|||||||
"index": 2
|
"index": 2
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"image_group_id": "",
|
|
||||||
"images360": [],
|
"images360": [],
|
||||||
"pdf_list": [],
|
"pdf_list": [],
|
||||||
"attributes": [
|
"attributes": [
|
||||||
@@ -1790,6 +1789,113 @@ func TestGetDescriptionOfProduct(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetDescriptionOfProductV4(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *GetDescriptionOfProductsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&GetDescriptionOfProductsParams{
|
||||||
|
Filter: GetDescriptionOfProductsFilter{
|
||||||
|
ProductId: []string{"330186294"},
|
||||||
|
Visibility: "ALL",
|
||||||
|
},
|
||||||
|
Limit: 100,
|
||||||
|
SortDirection: "ASC",
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"id": 330186294,
|
||||||
|
"barcode": "OZN653473453",
|
||||||
|
"name": "PC ЮКОМС Ryzen 7 5700G ...",
|
||||||
|
"offer_id": "ju-cas2-r5700g-bl",
|
||||||
|
"height": 360,
|
||||||
|
"depth": 420,
|
||||||
|
"width": 220,
|
||||||
|
"dimension_unit": "mm",
|
||||||
|
"weight": 4500,
|
||||||
|
"weight_unit": "g",
|
||||||
|
"description_category_id": 17028619,
|
||||||
|
"type_id": 91476,
|
||||||
|
"primary_image": "https://cdn1.ozone.ru/s3/multimedia-1-3/7084786431.jpg",
|
||||||
|
"model_info": {
|
||||||
|
"model_id": 379410772,
|
||||||
|
"count": 126
|
||||||
|
},
|
||||||
|
"images": [
|
||||||
|
"https://cdn1.ozone.ru/s3/multimedia-1-0/7084786428.jpg",
|
||||||
|
"https://cdn1.ozone.ru/s3/multimedia-1-k/7084786304.jpg"
|
||||||
|
],
|
||||||
|
"pdf_list": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"id": 85,
|
||||||
|
"complex_id": 0,
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"dictionary_value_id": 971195426,
|
||||||
|
"value": "ЮКОМС"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"complex_attributes": [],
|
||||||
|
"color_image": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"last_id": ""
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&GetDescriptionOfProductsParams{},
|
||||||
|
`{
|
||||||
|
"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, cancel := context.WithTimeout(context.Background(), testTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resp, err := c.Products().GetDescriptionOfProducts(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &GetDescriptionOfProductsResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("wrong status code: got: %d, want: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.statusCode == http.StatusOK {
|
||||||
|
if len(resp.Result) == 0 {
|
||||||
|
t.Error("expected non-empty result in success case")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetProductDescription(t *testing.T) {
|
func TestGetProductDescription(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -2141,79 +2247,6 @@ func TestRemoveProductWithoutSKU(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListGeoRestrictions(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *ListGeoRestrictionsParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&ListGeoRestrictionsParams{
|
|
||||||
Filter: ListGeoRestrictionsFilter{
|
|
||||||
OnlyVisible: true,
|
|
||||||
},
|
|
||||||
LastOrderNumber: 0,
|
|
||||||
Limit: 3,
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"restrictions": [
|
|
||||||
{
|
|
||||||
"id": "world",
|
|
||||||
"name": "Весь Мир",
|
|
||||||
"is_visible": true,
|
|
||||||
"order_number": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "42fb1c32-0cfe-5c96-9fb5-7f8e8449f28c",
|
|
||||||
"name": "Все города РФ",
|
|
||||||
"is_visible": true,
|
|
||||||
"order_number": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "moscow",
|
|
||||||
"name": "Москва",
|
|
||||||
"is_visible": true,
|
|
||||||
"order_number": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&ListGeoRestrictionsParams{},
|
|
||||||
`{
|
|
||||||
"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().ListGeoRestrictions(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &ListGeoRestrictionsResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUploadActivationCodes(t *testing.T) {
|
func TestUploadActivationCodes(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -2760,3 +2793,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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,11 @@ type GetCurrentSellerRatingInfoResponse struct {
|
|||||||
// Rating groups list
|
// Rating groups list
|
||||||
Groups []GetCurrentSellerRatingInfoGroup `json:"groups"`
|
Groups []GetCurrentSellerRatingInfoGroup `json:"groups"`
|
||||||
|
|
||||||
|
// Localization index details.
|
||||||
|
// If you had no sales in the last 14 days,
|
||||||
|
// the parameter fields will be empty
|
||||||
|
LocalizationIndex []LocalizationIndex `json:"localization_index"`
|
||||||
|
|
||||||
// An indication that the penalty points balance is exceeded
|
// An indication that the penalty points balance is exceeded
|
||||||
PenaltyScoreExceeded bool `json:"penalty_score_exceeded"`
|
PenaltyScoreExceeded bool `json:"penalty_score_exceeded"`
|
||||||
|
|
||||||
@@ -25,6 +30,14 @@ type GetCurrentSellerRatingInfoResponse struct {
|
|||||||
Premium bool `json:"premium"`
|
Premium bool `json:"premium"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LocalizationIndex struct {
|
||||||
|
// Date of localization index calculation
|
||||||
|
CalculationDate time.Time `json:"calculation_date"`
|
||||||
|
|
||||||
|
// Localization index value
|
||||||
|
LocalizationPercentage int32 `json:"localization_percentage"`
|
||||||
|
}
|
||||||
|
|
||||||
type GetCurrentSellerRatingInfoGroup struct {
|
type GetCurrentSellerRatingInfoGroup struct {
|
||||||
// Ratings group name
|
// Ratings group name
|
||||||
GroupName string `json:"group_name"`
|
GroupName string `json:"group_name"`
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ func TestGetCurrentRatingInfo(t *testing.T) {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"localization_index": [
|
||||||
|
{
|
||||||
|
"calculation_date": "2019-08-24T14:15:22Z",
|
||||||
|
"localization_percentage": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
"penalty_score_exceeded": true,
|
"penalty_score_exceeded": true,
|
||||||
"premium": true
|
"premium": true
|
||||||
}`,
|
}`,
|
||||||
|
|||||||
@@ -463,8 +463,15 @@ type GetReturnsReportsFilter struct {
|
|||||||
// Order delivery scheme: fbs — delivery from seller's warehouse
|
// Order delivery scheme: fbs — delivery from seller's warehouse
|
||||||
DeliverySchema string `json:"delivery_schema"`
|
DeliverySchema string `json:"delivery_schema"`
|
||||||
|
|
||||||
// Order identifier
|
// Date from which the data is displayed in the report.
|
||||||
OrderId int64 `json:"order_id"`
|
//
|
||||||
|
// Available for the last three months only
|
||||||
|
DateFrom time.Time `json:"date_from"`
|
||||||
|
|
||||||
|
// Date up to which the data is displayed in the report.
|
||||||
|
//
|
||||||
|
// Available for the last three months only
|
||||||
|
DateTo time.Time `json:"date_to"`
|
||||||
|
|
||||||
// Order status
|
// Order status
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
@@ -473,20 +480,13 @@ type GetReturnsReportsFilter struct {
|
|||||||
type GetReturnsReportResponse struct {
|
type GetReturnsReportResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
// Method result
|
|
||||||
Result GetReturnReportResult `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetReturnReportResult struct {
|
|
||||||
// Unique report identifier. The report is available for downloading within 3 days after making a request.
|
// Unique report identifier. The report is available for downloading within 3 days after making a request.
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// The report contains information about returned products that were accepted from the customer, ready for pickup, or delivered to the seller.
|
// Method for getting a report on FBO and FBS returns
|
||||||
//
|
|
||||||
// The method is only suitable for orders shipped from the seller's warehouse
|
|
||||||
func (c Reports) GetReturns(ctx context.Context, params *GetReturnsReportParams) (*GetReturnsReportResponse, error) {
|
func (c Reports) GetReturns(ctx context.Context, params *GetReturnsReportParams) (*GetReturnsReportResponse, error) {
|
||||||
url := "/v1/report/returns/create"
|
url := "/v2/report/returns/create"
|
||||||
|
|
||||||
resp := &GetReturnsReportResponse{}
|
resp := &GetReturnsReportResponse{}
|
||||||
|
|
||||||
|
|||||||
@@ -380,9 +380,7 @@ func TestGetReturnsReport(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
"result": {
|
"code": "REPORT_seller_products_924336_1720170405_a9ea2f27-a473-4b13-99f9-d0cfcb5b1a69"
|
||||||
"code": "d55f4517-8347-4e24-9d93-d6e736c1c07c"
|
|
||||||
}
|
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
@@ -407,16 +405,12 @@ func TestGetReturnsReport(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &GetReturnsReportResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
if resp.StatusCode != test.statusCode {
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", 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.StatusCode == http.StatusOK {
|
||||||
if resp.Result.Code == "" {
|
compareJsonResponse(t, test.response, &GetReturnsReportResponse{})
|
||||||
t.Errorf("Code cannot be empty")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
476
ozon/returns.go
476
ozon/returns.go
@@ -12,125 +12,6 @@ type Returns struct {
|
|||||||
client *core.Client
|
client *core.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetFBOReturnsParams struct {
|
|
||||||
// Filter
|
|
||||||
Filter *GetFBOReturnsFilter `json:"filter,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the last value on the page. Leave this field blank in the first request.
|
|
||||||
//
|
|
||||||
// To get the next values, specify the recieved value in the next request in the `last_id` parameter
|
|
||||||
LastId int64 `json:"last_id"`
|
|
||||||
|
|
||||||
// Number of values in the response
|
|
||||||
Limit int64 `json:"limit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetFBOReturnsFilter struct {
|
|
||||||
// Shipment number
|
|
||||||
PostingNumber string `json:"posting_number"`
|
|
||||||
|
|
||||||
// Return status
|
|
||||||
Status []GetFBOReturnsFilterStatus `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetFBOReturnsResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Identifier of the last value on the page
|
|
||||||
LastId int64 `json:"last_id"`
|
|
||||||
|
|
||||||
// Returns information
|
|
||||||
Returns []GetFBOReturnsReturn `json:"returns"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetFBOReturnsReturn struct {
|
|
||||||
// Time when a return was received from the customer
|
|
||||||
AcceptedFromCustomerMoment time.Time `json:"accepted_from_customer_moment"`
|
|
||||||
|
|
||||||
// Seller identifier
|
|
||||||
CompanyId int64 `json:"company_id"`
|
|
||||||
|
|
||||||
// Current return location
|
|
||||||
CurrentPlaceName string `json:"current_place_name"`
|
|
||||||
|
|
||||||
// Return destination
|
|
||||||
DestinationPlaceName string `json:"dst_place_name"`
|
|
||||||
|
|
||||||
// Return shipment identifier
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
|
|
||||||
// Indication that the package has been opened. true, if it has been
|
|
||||||
IsOpened bool `json:"is_opened"`
|
|
||||||
|
|
||||||
// Shipment number
|
|
||||||
PostingNumber string `json:"posting_number"`
|
|
||||||
|
|
||||||
// Unique return record identifier
|
|
||||||
ReturnId int64 `json:"return_id"`
|
|
||||||
|
|
||||||
// Return reason
|
|
||||||
ReturnReasonName string `json:"return_reason_name"`
|
|
||||||
|
|
||||||
// Return delivery time to the Ozon warehouse
|
|
||||||
ReturnedToOzonMoment time.Time `json:"returned_to_ozon_moment"`
|
|
||||||
|
|
||||||
// Product identifier in the Ozon system, SKU
|
|
||||||
SKU int64 `json:"sku"`
|
|
||||||
|
|
||||||
// Return status
|
|
||||||
Status GetFBOReturnsReturnStatus `json:"status_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method for getting information on returned products that are sold from the Ozon warehouse
|
|
||||||
func (c Returns) GetFBOReturns(ctx context.Context, params *GetFBOReturnsParams) (*GetFBOReturnsResponse, error) {
|
|
||||||
url := "/v3/returns/company/fbo"
|
|
||||||
|
|
||||||
resp := &GetFBOReturnsResponse{}
|
|
||||||
|
|
||||||
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 GetFBSReturnsParams struct {
|
|
||||||
// Filter
|
|
||||||
Filter *GetFBSReturnsFilter `json:"filter,omitempty"`
|
|
||||||
|
|
||||||
// Number of values in the response:
|
|
||||||
// - maximum — 1000,
|
|
||||||
// - minimum — 1
|
|
||||||
Limit int64 `json:"limit"`
|
|
||||||
|
|
||||||
// Return identifier that was loaded the last time.
|
|
||||||
// Return identifiers with the higher value than `last_id`
|
|
||||||
// will be returned in the response.
|
|
||||||
LastId int64 `json:"offset"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetFBSReturnsFilter struct {
|
|
||||||
// Last day of free storage
|
|
||||||
LastFreeWaitingDay GetFBSReturnsFilterTimeRange `json:"last_free_waiting_dat"`
|
|
||||||
|
|
||||||
// Order ID
|
|
||||||
OrderId int64 `json:"order_id"`
|
|
||||||
|
|
||||||
// Shipment ID
|
|
||||||
PostingNumber []string `json:"posting_number"`
|
|
||||||
|
|
||||||
// Product name
|
|
||||||
ProductName string `json:"product_name"`
|
|
||||||
|
|
||||||
// Product ID
|
|
||||||
ProductOfferId string `json:"product_offer_id"`
|
|
||||||
|
|
||||||
// Return status
|
|
||||||
Status GetFBSReturnsFilterStatus `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetFBSReturnsFilterTimeRange struct {
|
type GetFBSReturnsFilterTimeRange struct {
|
||||||
// The beginning of the period.
|
// The beginning of the period.
|
||||||
//
|
//
|
||||||
@@ -147,118 +28,6 @@ type GetFBSReturnsFilterTimeRange struct {
|
|||||||
TimeTo time.Time `json:"time_to"`
|
TimeTo time.Time `json:"time_to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetFBSReturnsResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Return identifier that was loaded the last time.
|
|
||||||
// Return identifiers with the higher value than `last_id`
|
|
||||||
// will be returned in the response
|
|
||||||
LastId int64 `json:"last_id"`
|
|
||||||
|
|
||||||
// Returns information
|
|
||||||
Returns []GetFBSReturnResultReturn `json:"returns"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetFBSReturnResultReturn struct {
|
|
||||||
// Bottom barcode on the product label
|
|
||||||
ClearingId int64 `json:"clearing_id"`
|
|
||||||
|
|
||||||
// Commission fee
|
|
||||||
Commission float64 `json:"commission"`
|
|
||||||
|
|
||||||
// Commission percentage
|
|
||||||
CommissionPercent float64 `json:"commission_percent"`
|
|
||||||
|
|
||||||
// Product item identifier in the Ozon logistics system
|
|
||||||
ExemplarId int64 `json:"exemplar_id"`
|
|
||||||
|
|
||||||
// Return identifier in the Ozon accounting system
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
|
|
||||||
// If the product is in transit — true
|
|
||||||
IsMoving bool `json:"is_moving"`
|
|
||||||
|
|
||||||
// Indication that the package has been opened. true, if it has been
|
|
||||||
IsOpened bool `json:"is_opened"`
|
|
||||||
|
|
||||||
// Last day of free storage
|
|
||||||
LastFreeWaitingDay string `json:"last_free_waiting_day"`
|
|
||||||
|
|
||||||
// ID of the warehouse the product is being transported to
|
|
||||||
PlaceId int64 `json:"place_id"`
|
|
||||||
|
|
||||||
// Intermediate return point
|
|
||||||
MovingToPlaceName string `json:"moving_to_place_name"`
|
|
||||||
|
|
||||||
// Delivery cost
|
|
||||||
PickingAmount float64 `json:"picking_amount"`
|
|
||||||
|
|
||||||
// Shipment number
|
|
||||||
PostingNumber string `json:"posting_number"`
|
|
||||||
|
|
||||||
PickingTag string `json:"picking_tag"`
|
|
||||||
|
|
||||||
// Current product price without a discount
|
|
||||||
Price float64 `json:"price"`
|
|
||||||
|
|
||||||
// Product price without commission
|
|
||||||
PriceWithoutCommission float64 `json:"price_without_commission"`
|
|
||||||
|
|
||||||
// Product identifier — SKU
|
|
||||||
ProductId int64 `json:"product_id"`
|
|
||||||
|
|
||||||
// Product name
|
|
||||||
ProductName string `json:"product_name"`
|
|
||||||
|
|
||||||
// Product quantity
|
|
||||||
Quantity int64 `json:"quantity"`
|
|
||||||
|
|
||||||
// Barcode on the return label. Use this parameter value to work with the return label
|
|
||||||
ReturnBarcode string `json:"return_barcode"`
|
|
||||||
|
|
||||||
// Package unit identifier in the Ozon logistics system
|
|
||||||
ReturnClearingId int64 `json:"return_clearing_id"`
|
|
||||||
|
|
||||||
// Product return date
|
|
||||||
ReturnDate string `json:"return_date"`
|
|
||||||
|
|
||||||
// Return reason
|
|
||||||
ReturnReasonName string `json:"return_reason_name"`
|
|
||||||
|
|
||||||
// Date when the product is ready to be handed over to the seller
|
|
||||||
WaitingForSellerDate string `json:"waiting_for_seller_date_time"`
|
|
||||||
|
|
||||||
// Date of handing over the product to the seller
|
|
||||||
ReturnedToSellerDate string `json:"returned_to_seller_date_time"`
|
|
||||||
|
|
||||||
// Return storage period in days
|
|
||||||
WaitingForSellerDays int64 `json:"waiting_for_seller_days"`
|
|
||||||
|
|
||||||
// Return storage cost
|
|
||||||
ReturnsKeepingCost float64 `json:"returns_keeping_cost"`
|
|
||||||
|
|
||||||
// Product identifier in the Ozon system, SKU
|
|
||||||
SKU int64 `json:"sku"`
|
|
||||||
|
|
||||||
// Return status
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method for getting information on returned products that are sold from the seller's warehouse
|
|
||||||
func (c Returns) GetFBSReturns(ctx context.Context, params *GetFBSReturnsParams) (*GetFBSReturnsResponse, error) {
|
|
||||||
url := "/v3/returns/company/fbs"
|
|
||||||
|
|
||||||
resp := &GetFBSReturnsResponse{}
|
|
||||||
|
|
||||||
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 GetRFBSReturnsParams struct {
|
type GetRFBSReturnsParams struct {
|
||||||
// Filter
|
// Filter
|
||||||
Filter *GetRFBSReturnsFilter `json:"filter,omitempty"`
|
Filter *GetRFBSReturnsFilter `json:"filter,omitempty"`
|
||||||
@@ -943,7 +712,250 @@ func (c Returns) FBSQuantity(ctx context.Context, params *GetFBSQuantityReturnsP
|
|||||||
|
|
||||||
resp := &GetFBSQuantityReturnsResponse{}
|
resp := &GetFBSQuantityReturnsResponse{}
|
||||||
|
|
||||||
response, err := c.client.Request(ctx, http.MethodPost, url, nil, resp, nil)
|
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 ListReturnsParams struct {
|
||||||
|
// Filter
|
||||||
|
Filter *ListReturnsFilter `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Number of loaded returns. The maximum value is 500
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
|
||||||
|
// Identifier of the last loaded return
|
||||||
|
LastId int64 `json:"last_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListReturnsFilter struct {
|
||||||
|
// Filter by return creation date
|
||||||
|
LogisticReturnDate *GetFBSReturnsFilterTimeRange `json:"logistic_return_date"`
|
||||||
|
|
||||||
|
// Filter by storage fees start date
|
||||||
|
StorageTarifficationDate *GetFBSReturnsFilterTimeRange `json:"storage_tariffication_start_date"`
|
||||||
|
|
||||||
|
// Filter by date the return status changed
|
||||||
|
VisualStatusChangeMoment *GetFBSReturnsFilterTimeRange `json:"visual_status_change_moment"`
|
||||||
|
|
||||||
|
// Filter by order identifier
|
||||||
|
OrderId int64 `json:"order_id,omitempty"`
|
||||||
|
|
||||||
|
// Filter by shipment number
|
||||||
|
PostingNumbers []string `json:"posting_numbers,omitempty"`
|
||||||
|
|
||||||
|
// Filter by product name
|
||||||
|
ProductName string `json:"product_name,omitempty"`
|
||||||
|
|
||||||
|
// Filter by product identifier in the seller's system
|
||||||
|
OfferId string `json:"offer_id,omitempty"`
|
||||||
|
|
||||||
|
// Filter by return status
|
||||||
|
VisualStatusName VisualStatus `json:"visual_status_name,omitempty"`
|
||||||
|
|
||||||
|
// Filter by warehouse identifier
|
||||||
|
WarehouseId int64 `json:"warehouse_id,omitempty"`
|
||||||
|
|
||||||
|
// Filter by return label barcode
|
||||||
|
Barcode string `json:"barcode,omitempty"`
|
||||||
|
|
||||||
|
// Filter by delivery scheme: FBS or FBO
|
||||||
|
ReturnSchema string `json:"return_schema,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListReturnsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Returns details
|
||||||
|
Returns []Return `json:"returns"`
|
||||||
|
|
||||||
|
// true, if the seller has other returns
|
||||||
|
HasNext bool `json:"has_next"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Return struct {
|
||||||
|
// Product items data
|
||||||
|
Exemplars []ReturnExemplar `json:"exemplars"`
|
||||||
|
|
||||||
|
// Return identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
|
// Company identifier
|
||||||
|
CompanyId int64 `json:"company_id"`
|
||||||
|
|
||||||
|
// Return reason
|
||||||
|
ReturnReasonName string `json:"return_reason_name"`
|
||||||
|
|
||||||
|
// Return type
|
||||||
|
Type string `json:"type"`
|
||||||
|
|
||||||
|
// Return scheme
|
||||||
|
Schema string `json:"schema"`
|
||||||
|
|
||||||
|
// Order identifier
|
||||||
|
OrderId int64 `json:"order_id"`
|
||||||
|
|
||||||
|
// Order number
|
||||||
|
OrderNumber string `json:"order_number"`
|
||||||
|
|
||||||
|
// Warehouse where the return is stored
|
||||||
|
Place ReturnPlace `json:"place"`
|
||||||
|
|
||||||
|
// Warehouse where returns are sent to
|
||||||
|
TargetPlace ReturnPlace `json:"target_place"`
|
||||||
|
|
||||||
|
// Storage details
|
||||||
|
Storage ReturnStorage `json:"storage"`
|
||||||
|
|
||||||
|
// Product details
|
||||||
|
Product ReturnProduct `json:"product"`
|
||||||
|
|
||||||
|
// Return details
|
||||||
|
Logistic ReturnLogistic `json:"logistic"`
|
||||||
|
|
||||||
|
// Return status details
|
||||||
|
Visual ReturnVisual `json:"visual"`
|
||||||
|
|
||||||
|
// Additional information
|
||||||
|
AdditionalInfo ReturnAdditionalInfo `json:"additional_info"`
|
||||||
|
|
||||||
|
// Previous return identifier
|
||||||
|
SourceId int64 `json:"source_id"`
|
||||||
|
|
||||||
|
// Shipment number
|
||||||
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
|
// Original shipment barcode
|
||||||
|
ClearingId int64 `json:"clearing_id"`
|
||||||
|
|
||||||
|
// Package unit identifier in the Ozon logistics system
|
||||||
|
ReturnClearingId int64 `json:"return_clearing_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnExemplar struct {
|
||||||
|
// Product identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnPlace struct {
|
||||||
|
// Warehouse identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
|
// Warehouse name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Warehouse address
|
||||||
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnStorage struct {
|
||||||
|
// Storage cost details
|
||||||
|
Sum ReturnSum `json:"sum"`
|
||||||
|
|
||||||
|
// First day of charging for storage
|
||||||
|
TarifficationsFirstDate time.Time `json:"tariffication_first_date"`
|
||||||
|
|
||||||
|
// Start date for storage fees
|
||||||
|
TarifficationsStartDate time.Time `json:"tariffication_start_date"`
|
||||||
|
|
||||||
|
// Date when the return was ready for handover
|
||||||
|
ArrivedMoment time.Time `json:"arrived_moment"`
|
||||||
|
|
||||||
|
// Number of days the return has been waiting for handover
|
||||||
|
Days int64 `json:"days"`
|
||||||
|
|
||||||
|
// Disposal cost details
|
||||||
|
UtilizationSum ReturnSum `json:"utilization_sum"`
|
||||||
|
|
||||||
|
// Planned disposal date
|
||||||
|
UtilizationForecastDate string `json:"utilization_forecast_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnSum struct {
|
||||||
|
// Currency
|
||||||
|
CurrencyCode string `json:"currency_code"`
|
||||||
|
|
||||||
|
// Disposal cost
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnProduct struct {
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
|
// Product identifier in the seller's system
|
||||||
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
|
// product name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Product price details
|
||||||
|
Price ReturnSum `json:"price"`
|
||||||
|
|
||||||
|
// Product cost without commission
|
||||||
|
PriceWithoutCommission ReturnSum `json:"price_without_commission"`
|
||||||
|
|
||||||
|
// Sales commission by category
|
||||||
|
CommissionPercent float64 `json:"commission_percent"`
|
||||||
|
|
||||||
|
// Commission details
|
||||||
|
Commission ReturnSum `json:"commission"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnLogistic struct {
|
||||||
|
// Date when the order was placed for technical return
|
||||||
|
TechnicalReturnMoment time.Time `json:"technical_return_moment"`
|
||||||
|
|
||||||
|
// Date when the return arrived to the warehouse or was handed over to the seller
|
||||||
|
FinalMoment time.Time `json:"final_moment"`
|
||||||
|
|
||||||
|
// Date when the seller received compensation for the return
|
||||||
|
CancelledWithCompensationMoment time.Time `json:"cancelled_with_compensation_moment"`
|
||||||
|
|
||||||
|
// Date when the customer returned the product
|
||||||
|
ReturnDate time.Time `json:"return_date"`
|
||||||
|
|
||||||
|
// Return label barcode
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnVisual struct {
|
||||||
|
// Return status
|
||||||
|
Status ReturnVisualStatus `json:"status"`
|
||||||
|
|
||||||
|
// Date the return status changed
|
||||||
|
ChangeMoment time.Time `json:"change_moment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnVisualStatus struct {
|
||||||
|
// Return status identifier
|
||||||
|
Id int32 `json:"id"`
|
||||||
|
|
||||||
|
// Return status name
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
|
||||||
|
// System name of the return status
|
||||||
|
SystemName string `json:"sys_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnAdditionalInfo struct {
|
||||||
|
// true, if the return package is opened
|
||||||
|
IsOpened bool `json:"is_opened"`
|
||||||
|
|
||||||
|
// true, if the return belongs to Super Economy products
|
||||||
|
IsSuperEconom bool `json:"is_super_econom"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Returns) List(ctx context.Context, params *ListReturnsParams) (*ListReturnsResponse, error) {
|
||||||
|
url := "/v1/returns/list"
|
||||||
|
|
||||||
|
resp := &ListReturnsResponse{}
|
||||||
|
|
||||||
|
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,193 +8,6 @@ import (
|
|||||||
core "github.com/diphantxm/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetFBOReturns(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *GetFBOReturnsParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&GetFBOReturnsParams{
|
|
||||||
Filter: &GetFBOReturnsFilter{
|
|
||||||
PostingNumber: "some number",
|
|
||||||
},
|
|
||||||
LastId: 123,
|
|
||||||
Limit: 100,
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"last_id": 0,
|
|
||||||
"returns": [
|
|
||||||
{
|
|
||||||
"accepted_from_customer_moment": "2019-08-24T14:15:22Z",
|
|
||||||
"company_id": 123456789,
|
|
||||||
"current_place_name": "my-place",
|
|
||||||
"dst_place_name": "that-place",
|
|
||||||
"id": 123456789,
|
|
||||||
"is_opened": true,
|
|
||||||
"posting_number": "some number",
|
|
||||||
"return_reason_name": "ripped",
|
|
||||||
"returned_to_ozon_moment": "2019-08-24T14:15:22Z",
|
|
||||||
"sku": 123456789,
|
|
||||||
"status_name": "delivering"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&GetFBOReturnsParams{},
|
|
||||||
`{
|
|
||||||
"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.Returns().GetFBOReturns(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &GetFBOReturnsResponse{})
|
|
||||||
|
|
||||||
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 len(resp.Returns) > 0 {
|
|
||||||
if resp.Returns[0].Id == 0 {
|
|
||||||
t.Errorf("Id cannot be 0")
|
|
||||||
}
|
|
||||||
if resp.Returns[0].CompanyId == 0 {
|
|
||||||
t.Errorf("Company id cannot be 0")
|
|
||||||
}
|
|
||||||
if resp.Returns[0].SKU == 0 {
|
|
||||||
t.Errorf("SKU cannot be 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFBSReturns(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *GetFBSReturnsParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&GetFBSReturnsParams{
|
|
||||||
Filter: &GetFBSReturnsFilter{
|
|
||||||
PostingNumber: []string{"07402477-0022-2"},
|
|
||||||
Status: "returned_to_seller",
|
|
||||||
},
|
|
||||||
Limit: 1000,
|
|
||||||
LastId: 0,
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"last_id": 0,
|
|
||||||
"returns": [
|
|
||||||
{
|
|
||||||
"clearing_id": 23,
|
|
||||||
"commission": 21,
|
|
||||||
"commission_percent": 0,
|
|
||||||
"exemplar_id": 42,
|
|
||||||
"id": 123,
|
|
||||||
"is_moving": true,
|
|
||||||
"is_opened": true,
|
|
||||||
"last_free_waiting_day": "string",
|
|
||||||
"place_id": 122,
|
|
||||||
"moving_to_place_name": "string",
|
|
||||||
"picking_amount": 0,
|
|
||||||
"posting_number": "string",
|
|
||||||
"picking_tag": "string",
|
|
||||||
"price": 0,
|
|
||||||
"price_without_commission": 0,
|
|
||||||
"product_id": 2222,
|
|
||||||
"product_name": "string",
|
|
||||||
"quantity": 0,
|
|
||||||
"return_barcode": "string",
|
|
||||||
"return_clearing_id": 0,
|
|
||||||
"return_date": "string",
|
|
||||||
"return_reason_name": "string",
|
|
||||||
"waiting_for_seller_date_time": "string",
|
|
||||||
"returned_to_seller_date_time": "string",
|
|
||||||
"waiting_for_seller_days": 0,
|
|
||||||
"returns_keeping_cost": 0,
|
|
||||||
"sku": 33332,
|
|
||||||
"status": "string"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&GetFBSReturnsParams{},
|
|
||||||
`{
|
|
||||||
"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.Returns().GetFBSReturns(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &GetFBSReturnsResponse{})
|
|
||||||
|
|
||||||
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 len(resp.Returns) > 0 {
|
|
||||||
if resp.Returns[0].Id == 0 {
|
|
||||||
t.Errorf("Id cannot be 0")
|
|
||||||
}
|
|
||||||
if resp.Returns[0].ProductId == 0 {
|
|
||||||
t.Errorf("Product id cannot be 0")
|
|
||||||
}
|
|
||||||
if resp.Returns[0].SKU == 0 {
|
|
||||||
t.Errorf("SKU cannot be 0")
|
|
||||||
}
|
|
||||||
if resp.Returns[0].Status == "" {
|
|
||||||
t.Errorf("Status cannot be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetRFBSReturns(t *testing.T) {
|
func TestGetRFBSReturns(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1059,3 +872,153 @@ func TestFBSQuantity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListReturns(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ListReturnsParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ListReturnsParams{
|
||||||
|
Filter: &ListReturnsFilter{
|
||||||
|
LogisticReturnDate: &GetFBSReturnsFilterTimeRange{
|
||||||
|
TimeFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
TimeTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
},
|
||||||
|
StorageTarifficationDate: &GetFBSReturnsFilterTimeRange{
|
||||||
|
TimeFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
TimeTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
},
|
||||||
|
VisualStatusChangeMoment: &GetFBSReturnsFilterTimeRange{
|
||||||
|
TimeFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
TimeTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||||
|
},
|
||||||
|
WarehouseId: 911,
|
||||||
|
ReturnSchema: "FBO",
|
||||||
|
ProductName: "string",
|
||||||
|
},
|
||||||
|
Limit: 500,
|
||||||
|
LastId: 0,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"returns": [
|
||||||
|
{
|
||||||
|
"exemplars": [
|
||||||
|
{
|
||||||
|
"id": 1019562967545956
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": 1000015552,
|
||||||
|
"company_id": 3058,
|
||||||
|
"return_reason_name": "Customer refused on receipt: not satisfied with the quality of the product",
|
||||||
|
"type": "FullReturn",
|
||||||
|
"schema": "Fbs",
|
||||||
|
"order_id": 24540784250,
|
||||||
|
"order_number": "58544282-0057",
|
||||||
|
"place": {
|
||||||
|
"id": 23869688194000,
|
||||||
|
"name": "СЦ_Львовский_Возвраты",
|
||||||
|
"address": "Россия, обл. Московская, г. Подольск, промышленная зона Львовский, ул. Московская, д. 69, стр. 5"
|
||||||
|
},
|
||||||
|
"target_place": {
|
||||||
|
"id": 23869688194000,
|
||||||
|
"name": "СЦ_Львовский_Возвраты",
|
||||||
|
"address": "Россия, обл. Московская, г. Подольск, промышленная зона Львовский, ул. Московская, д. 69, стр. 5"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"sum": {
|
||||||
|
"currency_code": "RUB",
|
||||||
|
"price": 1231
|
||||||
|
},
|
||||||
|
"tariffication_first_date": "2024-07-30T06:15:48.998146Z",
|
||||||
|
"tariffication_start_date": "2024-07-29T06:15:48.998146Z",
|
||||||
|
"arrived_moment": "2024-07-29T06:15:48.998146Z",
|
||||||
|
"days": 0,
|
||||||
|
"utilization_sum": {
|
||||||
|
"currency_code": "RUB",
|
||||||
|
"price": 1231
|
||||||
|
},
|
||||||
|
"utilization_forecast_date": "2024-07-29T06:15:48.998146Z"
|
||||||
|
},
|
||||||
|
"product": {
|
||||||
|
"sku": 1100526203,
|
||||||
|
"offer_id": "81451",
|
||||||
|
"name": "Кукла Дотти Плачущий младенец Cry Babies Dressy Dotty",
|
||||||
|
"price": {
|
||||||
|
"currency_code": "RUB",
|
||||||
|
"price": 3318
|
||||||
|
},
|
||||||
|
"price_without_commission": {
|
||||||
|
"currency_code": "RUB",
|
||||||
|
"price": 3318
|
||||||
|
},
|
||||||
|
"commission_percent": 1.2,
|
||||||
|
"commission": {
|
||||||
|
"currency_code": "RUB",
|
||||||
|
"price": 2312
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logistic": {
|
||||||
|
"technical_return_moment": "2024-07-29T06:15:48.998146Z",
|
||||||
|
"final_moment": "2024-07-29T06:15:48.998146Z",
|
||||||
|
"cancelled_with_compensation_moment": "2024-07-29T06:15:48.998146Z",
|
||||||
|
"return_date": "2024-07-29T06:15:48.998146Z",
|
||||||
|
"barcode": "ii5275210303"
|
||||||
|
},
|
||||||
|
"visual": {
|
||||||
|
"status": {
|
||||||
|
"id": 3,
|
||||||
|
"display_name": "At the pick-up point",
|
||||||
|
"sys_name": "ArrivedAtReturnPlace"
|
||||||
|
},
|
||||||
|
"change_moment": "2024-07-29T06:15:48.998146Z"
|
||||||
|
},
|
||||||
|
"additional_info": {
|
||||||
|
"is_opened": true,
|
||||||
|
"is_super_econom": false
|
||||||
|
},
|
||||||
|
"source_id": 90426223,
|
||||||
|
"posting_number": "58544282-0057-1",
|
||||||
|
"clearing_id": 21190893156000,
|
||||||
|
"return_clearing_id": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_next": false
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ListReturnsParams{},
|
||||||
|
`{
|
||||||
|
"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.Returns().List(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ListReturnsResponse{})
|
||||||
|
|
||||||
|
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
|
// Indication that the warehouse accepts bulky products
|
||||||
IsKGT bool `json:"is_kgt"`
|
IsKGT bool `json:"is_kgt"`
|
||||||
|
|
||||||
|
// true if the warehouse handles economy products
|
||||||
|
IsEconomy bool `json:"is_economy"`
|
||||||
|
|
||||||
// Indication that warehouse schedule can be changed
|
// Indication that warehouse schedule can be changed
|
||||||
IsTimetableEditable bool `json:"is_timetable_editable"`
|
IsTimetableEditable bool `json:"is_timetable_editable"`
|
||||||
|
|
||||||
@@ -189,3 +192,58 @@ func (c Warehouses) GetListOfDeliveryMethods(ctx context.Context, params *GetLis
|
|||||||
|
|
||||||
return resp, nil
|
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