3 Commits

Author SHA1 Message Date
diPhantxm
dcf366d7d4 update 2025-03-02 15:35:05 +03:00
diPhantxm
f5d2d0197b update 2025-03-02 15:28:41 +03:00
Kirill
bd280b54f4 Update February 17, 2025 (#146) 2025-03-02 15:11:00 +03:00
8 changed files with 549 additions and 189 deletions

View File

@@ -200,12 +200,9 @@ type GetStocksOnWarehousesResultRow struct {
// Name of the warehouse where the products are stored
WarehouseName string `json:"warehouse_name"`
// Number of days the stock will last based on your average daily sales
IDC float64 `json:"idc"`
}
// Report on stocks and products movement at Ozon warehouses
// Method for getting a report on leftover stocks and products movement at Ozon warehouses
func (c Analytics) GetStocksOnWarehouses(ctx context.Context, params *GetStocksOnWarehousesParams) (*GetStocksOnWarehousesResponse, error) {
url := "/v2/analytics/stock_on_warehouses"
@@ -340,3 +337,114 @@ func (c Analytics) Stock(ctx context.Context, params *GetStockManagementParams)
return resp, nil
}
type GetProductQueriesParams struct {
// Date when analytics generation starts
DateFrom string `json:"date_from"`
//Date when analytics generation ends
DateTo string `json:"date_to"`
// Number of page returned in the request
Page int32 `json:"page"`
// Number of items on the pag
PageSize int32 `json:"page_size"`
// List of SKUs—product identifiers in the Ozon system.
// Analytics on requests is returned for them.
// Maximum value is 1,000 SKUs
SKUs []string `json:"skus"`
// Parameter by which products are sorted
SortBy string `json:"sort_by"`
// Sorting direction
SortDir string `json:"sort_dir"`
}
type GetProductQueriesResponse struct {
core.CommonResponse
// Period for which the analytics is generated
AnalyticsPeriod AnalyticsPeriod `json:"analytics_period"`
// Product list
Items []GetProductQueriesItem `json:"items"`
// Number of pages
PageCount int64 `json:"page_count"`
// Total number of queries
Total int64 `json:"total"`
}
type AnalyticsPeriod struct {
// Date when analytics generation starts
DateFrom string `json:"date_from"`
// Date when analytics generation ends
DateTo string `json:"date_to"`
}
type GetProductQueriesItem struct {
// Category name
Category string `json:"category"`
// Currency
Currency string `json:"currency"`
// Sales by queries
GMV float64 `json:"gmv"`
// Product name
Name string `json:"name"`
// Product identifier in the seller's system
OfferId string `json:"offer_id"`
// Average product position. Available only with the Premium or Premium Plus subscription, otherwise the field returns empty
Position float64 `json:"position"`
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
// Number of customers who searched for your product on Ozon
UniqueSearchUsers int64 `json:"unique_search_users"`
// Number of customers who have seen your product on Ozon.
// Available only with the Premium or Premium Plus subscription,
// otherwise the field returns empty
UniqueViewUsers int64 `json:"unique_view_users"`
// Conversion from product views.
// Available only with the Premium or Premium Plus subscription,
// otherwise the field returns empty
ViewConversion float64 `json:"view_conversion"`
}
// Use the method to get data about your product queries.
// Full analytics is available with the Premium and Premium Plus subscription.
// Without subscription, you can see a part of the metrics.
// The method is similar to the Products in Search → Queries for my product tab in your personal account.
//
// You can view analytics by queries for certain dates.
// To do this, specify the interval in the date_from and date_to fields.
// Data for the last month are available in any interval except for
// three days from the current date because these days the calculation is performed.
// Analytics for dates later than a month ago is available only with
// the Premium and Premium Plus subscription, and only by weeks.
// Specify the date_from parameter in the request
func (c Analytics) GetProductQueries(ctx context.Context, params *GetProductQueriesParams) (*GetProductQueriesResponse, error) {
url := "/v1/analytics/product-queries"
resp := &GetProductQueriesResponse{}
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
}

View File

@@ -277,3 +277,74 @@ func TestGetStock(t *testing.T) {
}
}
}
func TestGetProductQueries(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetProductQueriesParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetProductQueriesParams{
Page: 1,
PageSize: 10,
SKUs: []string{"string"},
},
`{
"analytics_period": {
"date_from": "string",
"date_to": "string"
},
"items": [
{
"category": "string",
"currency": "string",
"gmv": 0,
"name": "string",
"offer_id": "string",
"position": 0,
"sku": 0,
"unique_search_users": 0,
"unique_view_users": 0,
"view_conversion": 0
}
],
"page_count": 0,
"total": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetProductQueriesParams{},
`{
"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().GetProductQueries(ctx, test.params)
if err != nil {
t.Error(err)
continue
}
compareJsonResponse(t, test.response, &GetProductQueriesResponse{})
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -769,7 +769,7 @@ type ValidateLabelingCodesExemplar struct {
GTD string `json:"gtd"`
// Mandatory “Chestny ZNAK” labeling
MandatoryMark string `json:"mandatory_mark"`
Marks []SetProductItemsDataProductMark `json:"marks"`
// Product batch registration number
RNPT string `json:"rnpt"`
@@ -778,11 +778,6 @@ type ValidateLabelingCodesExemplar struct {
type ValidateLabelingCodesResponse struct {
core.CommonResponse
// Method result
Result ValidateLabelingCodesResult `json:"result"`
}
type ValidateLabelingCodesResult struct {
// Products list
Products []ValidateLabelingCodesResultProduct `json:"products"`
}
@@ -792,7 +787,7 @@ type ValidateLabelingCodesResultProduct struct {
Error string `json:"error"`
// Product items data
Exemplars []FBSProductExemplar `json:"exemplars"`
Exemplars []ValidateLabelingCodesResultExemplar `json:"exemplars"`
// Product identifier
ProductId int64 `json:"product_id"`
@@ -801,11 +796,43 @@ type ValidateLabelingCodesResultProduct struct {
Valid bool `json:"valid"`
}
type ValidateLabelingCodesResultExemplar struct {
// Product item validation errors
Errors []string `json:"errors"`
// Сustoms cargo declaration (CCD) number
GTD string `json:"gtd"`
// List of Control Identification Marks in one copy
Marks []ValidateLabelingCodesMark `json:"marks"`
// Product batch registration number
RNPT string `json:"rnpt"`
// Check result. true if the labeling codes of all product items meet the requirements
Valid bool `json:"valid"`
}
type ValidateLabelingCodesMark struct {
// Errors that appeared during verification of Control Identification Marks
Errors []string `json:"errors"`
// Labeling code meaning
Mark string `json:"mark"`
// Labeling code type
MarkType string `json:"mark_type"`
// Check result. true if the labeling
// codes of all product items meet the requirements
Valid bool `json:"valid"`
}
// Method for checking whether labeling codes meet the "Chestny ZNAK" system requirements on length and symbols.
//
// If you don't have the customs cargo declaration (CCD) number, you don't have to specify it
func (c FBS) ValidateLabelingCodes(ctx context.Context, params *ValidateLabelingCodesParams) (*ValidateLabelingCodesResponse, error) {
url := "/v4/fbs/posting/product/exemplar/validate"
url := "/v5/fbs/posting/product/exemplar/validate"
resp := &ValidateLabelingCodesResponse{}
@@ -1194,39 +1221,23 @@ type ProductDimension struct {
}
type FBSProductExemplar struct {
// Product item validation errors
Errors []string `json:"errors"`
// Item identifier
ExemplarId int64 `json:"exemplar_id"`
// Mandatory “Chestny ZNAK” labeling
MandatoryMark string `json:"mandatory_mark"`
// "Chestny ZNAK" labeling check status
MandatoryMarkCheckStatus MandatoryMarkStatus `json:"mandatory_mark_check_status"`
// "Chestny ZNAK" labeling check error codes
MandatoryMarkErrorCodes []string `json:"mandatory_mark_error_codes"`
// Сustoms cargo declaration (CCD) number
GTD string `json:"gtd"`
// Сustoms cargo declaration (CCD) check status
GTDCheckStatus string `json:"gtd_check_status"`
// Indication that a сustoms cargo declaration (CCD) number hasn't been specified
IsGTDAbsest bool `json:"is_gtd_absent"`
// Сustoms cargo declaration (CCD) check error codes
GTDErrorCodes []string `json:"gtd_error_codes"`
// Product batch registration number
RNPT string `json:"rnpt"`
// Indication that a product batch registration number hasn't been specified
IsRNPTAbsent bool `json:"is_rnpt_absent"`
// Check result.
// `true` if the labeling code of product item meets the requirements
Valid bool `json:"valid"`
}
// Method for getting shipment details by identifier
@@ -1803,7 +1814,7 @@ func (c FBS) GetDropOffPointRestrictions(ctx context.Context, params *GetDropOff
return resp, nil
}
type CheckProductItemsDataParams struct {
type SetProductItemsDataParams struct {
// Quantity of boxes the product is packed in
MultiBoxQuantity int32 `json:"multi_box_qty"`
@@ -1811,20 +1822,26 @@ type CheckProductItemsDataParams struct {
PostingNumber string `json:"posting_number"`
// Product list
Products []CheckProductItemsDataProduct `json:"products"`
Products []SetProductItemsDataProduct `json:"products"`
}
type CheckProductItemsDataProduct struct {
type SetProductItemsDataProduct struct {
// Product items data
Exemplars []CheckProductItemsDataProductExemplar `json:"exemplars"`
Exemplars []SetProductItemsDataProductExemplar `json:"exemplars"`
// Indication that you need to pass the сustoms cargo declaration
// (CCD) number for the product and shipment
IsGTDNeeded bool `json:"is_gtd_needed"`
// Indication that you need to pass the unique identifier of charges of the jewelry
IsJwUINNeeded bool `json:"is_jw_uin_needed"`
// Indication that you need to pass the "Chestny ZNAK" labeling
IsMandatoryMarkNeeded bool `json:"is_mandatory_mark_needed"`
// Indication that you can pass the "Chestny ZNAK" labeling, but it's not mandatory
IsMandatoryMarkPossible bool `json:"is_mandatory_mark_possible"`
// Indication that you need to pass the product batch registration number
IsRNPTNeeded bool `json:"is_rnpt_needed"`
@@ -1835,48 +1852,35 @@ type CheckProductItemsDataProduct struct {
Quantity int32 `json:"quantity"`
}
type CheckProductItemsDataProductExemplar struct {
type SetProductItemsDataProductExemplar struct {
// Item identifier
ExemplarId int64 `json:"exemplar_id"`
// Customs cargo declaration (CCD) number
GTD string `json:"gtd"`
// Сustoms cargo declaration (CCD) check status
GTDCheckStatus string `json:"gtd_check_status"`
// Сustoms cargo declaration (CCD) check error codes
GTDErrorCodes []string `json:"gtd_error_codes"`
// Indication that the customs cargo declaration (CCD) number isn't specified
IsGTDAbsent bool `json:"is_gtd_absent"`
// "Chestny ZNAK" labeling check status
MandatoryMarkCheckStatus MandatoryMarkStatus `json:"mandatory_mark_check_status"`
// "Chestny ZNAK" labeling check error codes
MandatoryMarkErrorCodes []string `json:"mandatory_mark_error_codes"`
// Indication that the product batch registration number isn't specified
IsRNPTAbsent bool `json:"is_rnpt_absent"`
// Mandatory "Chestny ZNAK" labeling
MandatoryMark string `json:"mandatory_mark"`
// Product batch registration number
RNPT string `json:"rnpt"`
// Product batch registration number check status
RNPTCheckStatus string `json:"rnpt_check_status"`
// Product batch registration number check error codes
RNPTErrorCodes []string `json:"rnpt_error_codes"`
// Unique identifier of charges of the jewelry
JWUIN string `json:"jw_uin"`
// Errors that appeared during verification of Control Identification Marks
Marks []SetProductItemsDataProductMark `json:"marks"`
}
type CheckProductItemsDataResponse struct {
type SetProductItemsDataProductMark struct {
// Labeling code meaning
Mark string `json:"mark"`
// Labeling code type
MarkType string `json:"mark_type"`
}
type SetProductItemsDataResponse struct {
core.CommonResponse
// Method result. true if the request was processed successfully
@@ -1887,37 +1891,19 @@ type CheckProductItemsDataResponse struct {
//
// for checking the availability of product items in the “Chestny ZNAK” labeling system;
// for saving product items data.
// To get the checks results, use the /v5/fbs/posting/product/exemplar/status method. To get data about created items, use the /v6/fbs/posting/product/exemplar/create-or-get method.
//
// To get the checks results,
// use the /v4/fbs/posting/product/exemplar/status method.
// To get data about created items,
// use the /v5/fbs/fbs/posting/product/exemplar/create-or-get method.
//
// If necessary, specify the number of the cargo customs declaration
// in the gtd parameter. If it is missing,
// pass the value is_gtd_absent = true.
//
// If you have multiple identical products in a shipment,
// specify one product_id and exemplars array for each product in the shipment.
// If you have multiple identical products in a shipment, specify one product_id and exemplars array for each product in the shipment.
//
// Always pass a complete set of product items data.
//
// For example, you have 10 product items in your system.
// You've passed them for checking and saving.
// Then you added another 60 product items to your system.
// When you pass product items for checking and saving again,
// pass all of them: both old and newly added.
// For example, you have 10 product items in your system. You pass them for checking and saving. Then you add another 60 product items to your system. When you pass product items for checking and saving again, pass all of them: both old and newly added.
//
// Unlike /v4/fbs/posting/product/exemplar/set,
// you can pass more item information in the request.
//
// The 200 response code doesn't guarantee that instance data has been received.
// It indicates that a task for adding the information has been created.
// To check the task status, use the /v4/fbs/posting/product/exemplar/status method.
func (c FBS) CheckProductItemsData(ctx context.Context, params *CheckProductItemsDataParams) (*CheckProductItemsDataResponse, error) {
url := "/v5/fbs/posting/product/exemplar/set"
// The 200 response code doesn't guarantee that instance data has been received. It indicates that a task for adding the information has been created. To check the task status, use the /v5/fbs/posting/product/exemplar/status method.
func (c FBS) SetProductItemsData(ctx context.Context, params *SetProductItemsDataParams) (*SetProductItemsDataResponse, error) {
url := "/v6/fbs/posting/product/exemplar/set"
resp := &CheckProductItemsDataResponse{}
resp := &SetProductItemsDataResponse{}
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
if err != nil {
@@ -1940,19 +1926,71 @@ type GetProductItemsCheckStatusesResponse struct {
PostingNumber string `json:"posting_number"`
// Products list
Products []CheckProductItemsDataProduct `json:"products"`
Products []GetProductItemsCheckStatusProduct `json:"products"`
// Product items check statuses and order collection availability:
// - ship_available — order collection is available,
// - ship_not_available — order collection is unavailable,
// - validation_in_process — product items validation is in progress
// Product items check statuses and order collection availability
Status string `json:"status"`
}
// Method for getting check statuses of product items that were passed in the `/fbs/posting/product/exemplar/set` method.
type GetProductItemsCheckStatusProduct struct {
// Product identifier
ProductId int64 `json:"product_id"`
// Product items data
Exemplars []GetProductItemsCheckStatusExemplar `json:"exemplars"`
}
type GetProductItemsCheckStatusExemplar struct {
// Item identifier
ExemplarId int64 `json:"exemplar_id"`
// Customs cargo declaration (CCD) number
GTD string `json:"gtd"`
// Сustoms cargo declaration (CCD) check status
GTDCheckStatus string `json:"gtd_check_status"`
// Сustoms cargo declaration (CCD) check error codes
GTDErrorCodes []string `json:"gtd_error_codes"`
// Indication that the customs cargo declaration (CCD) number isn't specified
IsGTDAbsent bool `json:"is_gtd_absent"`
// Indication that the product batch registration number isn't specified
IsRNPTAbsent bool `json:"is_rnpt_absent"`
// List of Control Identification Marks in one copy
Marks []GetProductItemsCheckStatusMark `json:"marks"`
// Product batch registration number
RNPT string `json:"rnpt"`
// Product batch registration number check status
RNPTCheckStatus string `json:"rnpt_check_status"`
// Product batch registration number check error codes
RNPTErrorCodes []string `json:"rnpt_error_codes"`
}
type GetProductItemsCheckStatusMark struct {
// Check status
CheckStatus string `json:"check_status"`
// Errors that appeared during verification of Control Identification Marks
ErrorCodes []string `json:"error_codes"`
// Labeling code meaning
Mark string `json:"mark"`
// Labeling code type
MarkType string `json:"mark_type"`
}
// Method for getting product items addition statuses
// that were passed in the /v6/fbs/posting/product/exemplar/set method.
// Also returns data on these product items.
func (c FBS) GetProductItemsCheckStatuses(ctx context.Context, params *GetProductItemsCheckStatusesParams) (*GetProductItemsCheckStatusesResponse, error) {
url := "/v4/fbs/posting/product/exemplar/status"
url := "/v5/fbs/posting/product/exemplar/status"
resp := &GetProductItemsCheckStatusesResponse{}
@@ -2938,11 +2976,37 @@ type CreateOrGetProductExemplarResponse struct {
Products []CheckProductItemsDataProduct `json:"products"`
}
type CheckProductItemsDataProduct struct {
// Data about items
Exemplars []SetProductItemsDataProductExemplar `json:"exemplars"`
// Indication that you need to pass the сustoms cargo declaration (CCD) number for the product and shipment
IsGTDNeeded bool `json:"is_gtd_needed"`
// Indication that you need to pass the unique identifier of charges of the jewelry
IsJwUINNeeded bool `json:"is_jw_uin_needed"`
// Indication that you need to pass the "Chestny ZNAK" labeling
IsMandatoryMarkNeeded bool `json:"is_mandatory_mark_needed"`
// Indication that you can pass the "Chestny ZNAK" labeling, but it's not mandatory
IsMandatoryMarkPossible bool `json:"is_mandatory_mark_possible"`
// Indication that you need to pass the product batch registration number
IsRNPTNeeded bool `json:"is_rnpt_needed"`
// Product ID
ProductId int64 `json:"product_id"`
// Items quantity
Quantity int32 `json:"quantity"`
}
// Method returns the created items data passed in the `/v5/fbs/posting/product/exemplar/set` method.
//
// Use this method to get the `exemplar_id`
func (c FBS) CreateOrGetProductExemplar(ctx context.Context, params *CreateOrGetProductExemplarParams) (*CreateOrGetProductExemplarResponse, error) {
url := "/v5/fbs/posting/product/exemplar/create-or-get"
url := "/v6/fbs/posting/product/exemplar/create-or-get"
resp := &CreateOrGetProductExemplarResponse{}
@@ -3310,3 +3374,29 @@ func (c FBS) VerifyCourierCode(ctx context.Context, params *VerifyCourierCodePar
return resp, nil
}
type UpdateProductsDataParams struct {
// Shipment number
PostingNumber string `json:"posting_number"`
}
type UpdateProductsDataResponse struct {
core.CommonResponse
}
// Use the method after passing item data using the
// /v6/fbs/posting/product/exemplar/set method to
// save updated item data for shipments in the “Awaiting shipment” status
func (c FBS) UpdateProductsData(ctx context.Context, params *UpdateProductsDataParams) (*UpdateProductsDataResponse, error) {
url := "/v1/fbs/posting/product/exemplar/update"
resp := &UpdateProductsDataResponse{}
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
}

View File

@@ -424,7 +424,6 @@ func TestValidateLabelingCodes(t *testing.T) {
Exemplars: []ValidateLabelingCodesExemplar{
{
GTD: "",
MandatoryMark: "010290000151642731tVMohkbfFgunB",
},
},
ProductId: 476925391,
@@ -432,23 +431,33 @@ func TestValidateLabelingCodes(t *testing.T) {
},
},
`{
"result": {
"products": [
{
"product_id": 476925391,
"error": "string",
"exemplars": [
{
"mandatory_mark": "010290000151642731tVMohkbfFgunB",
"gtd": "",
"valid": true,
"errors": []
"errors": [
"string"
],
"gtd": "string",
"marks": [
{
"errors": [
"string"
],
"mark": "string",
"mark_type": "string",
"valid": true
}
],
"valid": true,
"error": ""
"rnpt": "string",
"valid": true
}
],
"product_id": 476925391,
"valid": true
}
]
}
}`,
},
// Test No Client-Id or Api-Key
@@ -480,11 +489,11 @@ func TestValidateLabelingCodes(t *testing.T) {
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result.Products) != len(test.params.Products) {
if len(resp.Products) != len(test.params.Products) {
t.Errorf("Length of products in request and response are not equal")
}
if len(resp.Result.Products) > 0 {
if resp.Result.Products[0].ProductId != test.params.Products[0].ProductId {
if len(resp.Products) > 0 {
if resp.Products[0].ProductId != test.params.Products[0].ProductId {
t.Errorf("Product ids in request and response are not equal")
}
}
@@ -1380,52 +1389,52 @@ func TestGetDropOffPointRestrictions(t *testing.T) {
}
}
func TestCheckProductItemsData(t *testing.T) {
func TestSetProductItemsData(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *CheckProductItemsDataParams
params *SetProductItemsDataParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&CheckProductItemsDataParams{
&SetProductItemsDataParams{
MultiBoxQuantity: 0,
PostingNumber: "1234",
Products: []CheckProductItemsDataProduct{
Products: []SetProductItemsDataProduct{
{
Exemplars: []CheckProductItemsDataProductExemplar{
Exemplars: []SetProductItemsDataProductExemplar{
{
ExemplarId: 1,
GTD: "string",
IsGTDAbsent: true,
IsRNPTAbsent: true,
MandatoryMark: "string",
RNPT: "string",
JWUIN: "string",
},
},
IsGTDNeeded: true,
IsMandatoryMarkNeeded: true,
IsRNPTNeeded: true,
ProductId: 22,
Quantity: 11,
},
},
},
`{
"result": true
"code": 0,
"details": [
{
"typeUrl": "string",
"value": "string"
}
],
"message": "string"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&CheckProductItemsDataParams{},
&SetProductItemsDataParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
@@ -1437,13 +1446,13 @@ func TestCheckProductItemsData(t *testing.T) {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
resp, err := c.FBS().CheckProductItemsData(ctx, test.params)
resp, err := c.FBS().SetProductItemsData(ctx, test.params)
if err != nil {
t.Error(err)
continue
}
compareJsonResponse(t, test.response, &CheckProductItemsDataResponse{})
compareJsonResponse(t, test.response, &SetProductItemsDataResponse{})
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
@@ -1471,21 +1480,37 @@ func TestGetProductItemsCheckStatuses(t *testing.T) {
"posting_number": "23281294-0063-2",
"products": [
{
"product_id": 476925391,
"exemplars": [
{
"mandatory_mark": "010290000151642731tVMohkbfFgunB",
"gtd": "",
"exemplar_id": 0,
"gtd": "string",
"gtd_check_status": "string",
"gtd_error_codes": [
"string"
],
"is_gtd_absent": true,
"mandatory_mark_check_status": "passed",
"mandatory_mark_error_codes": [],
"gtd_check_status": "passed",
"gtd_error_codes": []
"is_rnpt_absent": true,
"marks": [
{
"check_status": "string",
"error_codes": [
"string"
],
"mark": "string",
"mark_type": "string"
}
],
"rnpt": "string",
"rnpt_check_status": "string",
"rnpt_error_codes": [
"string"
]
}
],
"status": "ship_available"
"product_id": 123
}
],
"status": "string"
}`,
},
// Test No Client-Id or Api-Key
@@ -1526,8 +1551,8 @@ func TestGetProductItemsCheckStatuses(t *testing.T) {
if resp.Products[0].ProductId == 0 {
t.Errorf("Product id cannot be 0")
}
if len(resp.Products[0].Exemplars) > 0 {
if resp.Products[0].Exemplars[0].MandatoryMark == "" {
if len(resp.Products[0].Exemplars) > 0 && len(resp.Products[0].Exemplars[0].Marks) > 0 {
if resp.Products[0].Exemplars[0].Marks[0].Mark == "" {
t.Errorf("Mandatory mark cannot be empty")
}
}
@@ -2877,13 +2902,19 @@ func TestCreateOrGetProductExemplar(t *testing.T) {
"gtd": "string",
"is_gtd_absent": true,
"is_rnpt_absent": true,
"mandatory_mark": "string",
"rnpt": "string",
"jw_uin": "string"
"marks": [
{
"mark": "string",
"mark_type": "string"
}
],
"rnpt": "string"
}
],
"is_gtd_needed": true,
"is_jw_uin_needed": true,
"is_mandatory_mark_needed": true,
"is_mandatory_mark_possible": true,
"is_rnpt_needed": true,
"product_id": 0,
"quantity": 0
@@ -3426,3 +3457,60 @@ func TestVerifyCourierCode(t *testing.T) {
}
}
}
func TestUpdateProductsData(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *UpdateProductsDataParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&UpdateProductsDataParams{
PostingNumber: "string",
},
`{
"code": 0,
"details": [
{
"typeUrl": "string",
"value": "string"
}
],
"message": "string"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&UpdateProductsDataParams{},
`{
"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().UpdateProductsData(ctx, test.params)
if err != nil {
t.Error(err)
continue
}
compareJsonResponse(t, test.response, &UpdateProductsDataResponse{})
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -362,11 +362,7 @@ type ListTransactionsResultOperationItem struct {
}
type ListTransactionsResultOperationPosting struct {
// Delivery scheme:
// - FBO — delivery to Ozon warehouse
// - FBS — delivery from seller's warehouse
// - RFBS — delivery service of seller's choice
// - Crossborder — delivery from abroad
// Delivery scheme
DeliverySchema string `json:"delivery_schema"`
// Date the product was accepted for processing

View File

@@ -1564,6 +1564,9 @@ type GetDescriptionOfProductResult struct {
// Barcode
Barcode string `json:"barcode"`
// All product's barcodes
Barcodes []string `json:"barcodes"`
// Category identifier
DescriptionCategoryId int64 `json:"description_category_id"`
@@ -1603,6 +1606,12 @@ type GetDescriptionOfProductResult struct {
// Array of PDF files
PDFList []GetDescriptionOfProductResultPDF `json:"pdf_list"`
// Link to the main product image
PrimaryImage string `json:"primary_image"`
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
// Product type identifier
TypeId int64 `json:"type_id"`
@@ -1687,7 +1696,7 @@ type GetDescriptionOfProductResultPDF struct {
// Returns a product characteristics description by product identifier. You can search for the product by `offer_id` or `product_id`
func (c Products) GetDescriptionOfProduct(ctx context.Context, params *GetDescriptionOfProductParams) (*GetDescriptionOfProductResponse, error) {
url := "/v3/products/info/attributes"
url := "/v4/product/info/attributes"
resp := &GetDescriptionOfProductResponse{}

View File

@@ -1370,37 +1370,33 @@ func TestGetDescriptionOfProduct(t *testing.T) {
{
"id": 213761435,
"barcode": "",
"description_category_id": 17038062,
"barcodes": [
"123124123",
"123342455"
],
"name": "Пленка защитная для Xiaomi Redmi Note 10 Pro 5G",
"offer_id": "21470",
"type_id": 124572394,
"height": 10,
"depth": 210,
"width": 140,
"dimension_unit": "mm",
"weight": 50,
"weight_unit": "g",
"primary_image": "https://cdn1.ozone.ru/s3/multimedia-4/6804736960.jpg",
"sku": 423434534,
"model_info": {
"model_id": 43445453,
"count": 4
},
"images": [
{
"file_name": "https://cdn1.ozone.ru/s3/multimedia-f/6190456071.jpg",
"default": true,
"index": 0
},
{
"file_name": "https://cdn1.ozone.ru/s3/multimedia-7/6190456099.jpg",
"default": false,
"index": 1
},
{
"file_name": "https://cdn1.ozone.ru/s3/multimedia-9/6190456065.jpg",
"default": false,
"index": 2
}
"https://cdn1.ozone.ru/s3/multimedia-4/6804736960.jpg",
"https://cdn1.ozone.ru/s3/multimedia-j/6835412647.jpg"
],
"images360": [],
"pdf_list": [],
"attributes": [
{
"attribute_id": 5219,
"id": 5219,
"complex_id": 0,
"values": [
{
@@ -1410,7 +1406,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 11051,
"id": 11051,
"complex_id": 0,
"values": [
{
@@ -1420,7 +1416,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 10100,
"id": 10100,
"complex_id": 0,
"values": [
{
@@ -1430,7 +1426,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 11794,
"id": 11794,
"complex_id": 0,
"values": [
{
@@ -1440,7 +1436,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 9048,
"id": 9048,
"complex_id": 0,
"values": [
{
@@ -1450,7 +1446,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 5076,
"id": 5076,
"complex_id": 0,
"values": [
{
@@ -1460,7 +1456,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 9024,
"id": 9024,
"complex_id": 0,
"values": [
{
@@ -1470,7 +1466,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 10015,
"id": 10015,
"complex_id": 0,
"values": [
{
@@ -1480,7 +1476,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 85,
"id": 85,
"complex_id": 0,
"values": [
{
@@ -1490,7 +1486,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 9461,
"id": 9461,
"complex_id": 0,
"values": [
{
@@ -1500,7 +1496,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 4180,
"id": 4180,
"complex_id": 0,
"values": [
{
@@ -1510,7 +1506,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 4191,
"id": 4191,
"complex_id": 0,
"values": [
{
@@ -1520,7 +1516,7 @@ func TestGetDescriptionOfProduct(t *testing.T) {
]
},
{
"attribute_id": 8229,
"id": 8229,
"complex_id": 0,
"values": [
{
@@ -1531,7 +1527,8 @@ func TestGetDescriptionOfProduct(t *testing.T) {
}
],
"complex_attributes": [],
"color_image": ""
"color_image": "",
"description_category_id": 71107562
}
],
"total": 1,

View File

@@ -88,7 +88,8 @@ type GetListOfWarehousesResultFirstMile struct {
FirstMileType string `json:"first_mile_type"`
}
// You do not need to specify any parameters in the request. Your company will be identified by the Warehouses ID
// Method returns the list of FBS and rFBS warehouses.
// To get the list of FBO warehouses, use the /v1/cluster/list method.
func (c Warehouses) GetListOfWarehouses(ctx context.Context) (*GetListOfWarehousesResponse, error) {
url := "/v1/warehouse/list"