add some methods for creating/editing/getting products
This commit is contained in:
10
ENDPOINTS.md
10
ENDPOINTS.md
@@ -7,14 +7,14 @@
|
||||
|
||||
## Uploading and updating products
|
||||
- [x] Create or update a product
|
||||
- [ ] Get the product import status
|
||||
- [ ] Create a product by Ozon ID
|
||||
- [ ] Upload and update product images
|
||||
- [ ] Check products images uploading status
|
||||
- [x] Get the product import status
|
||||
- [x] Create a product by Ozon ID
|
||||
- [x] Upload and update product images
|
||||
- [x] Check products images uploading status
|
||||
- [x] List of products
|
||||
- [x] Product details
|
||||
- [x] Get products' content rating by SKU
|
||||
- [ ] Get a list of products by identifiers
|
||||
- [x] Get a list of products by identifiers
|
||||
- [ ] Get a description of the product characteristics
|
||||
- [ ] Get product description
|
||||
- [ ] Product range limit, limits on product creation and update
|
||||
|
||||
705
ozon/products.go
705
ozon/products.go
@@ -105,225 +105,228 @@ type GetProductDetailsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Request results
|
||||
Result struct {
|
||||
// Barcode
|
||||
Barcode string `json:"barcode"`
|
||||
|
||||
// All product barcodes
|
||||
Barcodes []string `json:"barcodes"`
|
||||
|
||||
// Main offer price on Ozon.
|
||||
//
|
||||
// The field is deprecated. Returns an empty string ""
|
||||
BuyboxPrice string `json:"buybox_price"`
|
||||
|
||||
// Category identifier
|
||||
CategoryId int64 `json:"category_id"`
|
||||
|
||||
// Marketing color
|
||||
ColorImage string `json:"color_image"`
|
||||
|
||||
// Commission fees details
|
||||
Commissions []struct {
|
||||
// Delivery cost
|
||||
DeliveryAmount float64 `json:"deliveryAmount"`
|
||||
|
||||
// Minimum commission fee
|
||||
MinValue float64 `json:"minValue"`
|
||||
|
||||
// Commission percentage
|
||||
Percent float64 `json:"percent"`
|
||||
|
||||
// Return cost
|
||||
ReturnAmount float64 `json:"returnAmount"`
|
||||
|
||||
// Sale scheme
|
||||
SaleSchema string `json:"saleSchema"`
|
||||
|
||||
// Commission fee amount
|
||||
Value float64 `json:"value"`
|
||||
} `json:"commissions"`
|
||||
|
||||
// Date and time when the product was created
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// SKU of the product that is sold from the Ozon warehouse (FBO)
|
||||
FBOSKU int64 `json:"fbo_sku"`
|
||||
|
||||
// SKU of the product that is sold from the seller's warehouse (FBS and rFBS)
|
||||
FBSSKU int64 `json:"fbs_sku"`
|
||||
|
||||
// Document generation task number
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// An array of links to images. The images in the array are arranged in the order of their arrangement on the site. If the `primary_image` parameter is not specified, the first image in the list is the main one for the product
|
||||
Images []string `json:"images"`
|
||||
|
||||
// Main product image
|
||||
PrimaryImage string `json:"primary_image"`
|
||||
|
||||
// Array of 360 images
|
||||
Images360 []string `json:"images360"`
|
||||
|
||||
// true if the product has markdown equivalents at the Ozon warehouse
|
||||
HasDiscountedItem bool `json:"has_discounted_item"`
|
||||
|
||||
// Indication of a markdown product:
|
||||
//
|
||||
// * true if the product was created by the seller as a markdown
|
||||
//
|
||||
// * false if the product is not markdown or was marked down by Ozon
|
||||
IsDiscounted bool `json:"is_discounted"`
|
||||
|
||||
// Markdown products stocks
|
||||
DiscountedStocks struct {
|
||||
// Quantity of products to be supplied
|
||||
Coming int32 `json:"coming"`
|
||||
|
||||
// Quantity of products in warehouse
|
||||
Present int32 `json:"present"`
|
||||
|
||||
// Quantity of products reserved
|
||||
Reserved int32 `json:"reserved"`
|
||||
} `json:"discounted_stocks"`
|
||||
|
||||
// Indication of a bulky product
|
||||
IsKGT bool `json:"is_kgt"`
|
||||
|
||||
// Indication of mandatory prepayment for the product:
|
||||
//
|
||||
// * true — to buy a product, you need to make a prepayment.
|
||||
//
|
||||
// * false—prepayment is not required
|
||||
IsPrepayment bool `json:"is_prepayment"`
|
||||
|
||||
// If prepayment is possible, the value is true
|
||||
IsPrepaymentAllowed bool `json:"is_prepayment_allowed"`
|
||||
|
||||
// Currency of your prices. It matches the currency set in the personal account settings
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
|
||||
// The price of the product including all promotion discounts. This value will be shown on the Ozon storefront
|
||||
MarketingPrice string `json:"marketing_price"`
|
||||
|
||||
// Minimum price for similar products on Ozon.
|
||||
//
|
||||
// The field is deprecated. Returns an empty string ""
|
||||
MinOzonPrice string `json:"min_ozon_price"`
|
||||
|
||||
// Minimum product price with all promotions applied
|
||||
MinPrice string `json:"min_price"`
|
||||
|
||||
// Name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Price before discounts. Displayed strikethrough on the product description page
|
||||
OldPrice string `json:"old_price"`
|
||||
|
||||
// Price for customers with an Ozon Premium subscription
|
||||
PremiumPrice string `json:"premium_price"`
|
||||
|
||||
// Product price including discounts. This value is shown on the product description page
|
||||
Price string `json:"price"`
|
||||
|
||||
// Price index. Learn more in Help Center
|
||||
PriceIndex string `json:"price_idnex"`
|
||||
|
||||
// Product price suggested by the system based on similar offers
|
||||
RecommendedPrice string `json:"recommended_price"`
|
||||
|
||||
// Product state description
|
||||
Status struct {
|
||||
// Product state
|
||||
State string `json:"state"`
|
||||
|
||||
// Product state on the transition to which an error occurred
|
||||
StateFailed string `json:"state_failed"`
|
||||
|
||||
// Moderation status
|
||||
ModerateStatus string `json:"moderate_status"`
|
||||
|
||||
// Product decline reasons
|
||||
DeclineReasons []string `json:"decline_reasons"`
|
||||
|
||||
// Validation status
|
||||
ValidationsState string `json:"validation_state"`
|
||||
|
||||
// Product status name
|
||||
StateName string `json:"state_name"`
|
||||
|
||||
// Product state description
|
||||
StateDescription string `json:"state_description"`
|
||||
|
||||
// Indiction that there were errors while creating products
|
||||
IsFailed bool `json:"is_failed"`
|
||||
|
||||
// Indiction that the product was created
|
||||
IsCreated bool `json:"is_created"`
|
||||
|
||||
// Tooltips for the current product state
|
||||
StateTooltip string `json:"state_tooltip"`
|
||||
|
||||
// Product loading errors
|
||||
ItemErrors []GetProductDetailsResponseItemError `json:"item_errors"`
|
||||
|
||||
// The last time product state changed
|
||||
StateUpdatedAt time.Time `json:"state_updated_at"`
|
||||
} `json:"status"`
|
||||
|
||||
// Details about the sources of similar offers. Learn more in Help Сenter
|
||||
Sources []struct {
|
||||
// Indication that the source is taken into account when calculating the market value
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Link to the source
|
||||
Source string `json:"source"`
|
||||
} `json:"sources"`
|
||||
|
||||
// Details about product stocks
|
||||
Stocks struct {
|
||||
// Supply expected
|
||||
Coming int32 `json:"coming"`
|
||||
|
||||
// Currently at the warehouse
|
||||
Present int32 `json:"present"`
|
||||
|
||||
// Reserved
|
||||
Reserved int32 `json:"reserved"`
|
||||
} `json:"stocks"`
|
||||
|
||||
// Date of the last product update
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// Product VAT rate
|
||||
VAT string `json:"vat"`
|
||||
|
||||
// Product visibility settings
|
||||
VisibilityDetails struct {
|
||||
// If the product is active, the value is true
|
||||
ActiveProduct bool `json:"active_product"`
|
||||
|
||||
// If the price is set, the value is true
|
||||
HasPrice bool `json:"has_price"`
|
||||
|
||||
// If there is stock at the warehouses, the value is true
|
||||
HasStock bool `json:"has_stock"`
|
||||
} `json:"visibility_details"`
|
||||
|
||||
// If the product is on sale, the value is true
|
||||
Visible bool `json:"visible"`
|
||||
|
||||
// Product volume weight
|
||||
VolumeWeight float64 `json:"volume_weights"`
|
||||
} `json:"Result"`
|
||||
Result ProductDetails `json:"Result"`
|
||||
}
|
||||
|
||||
type ProductDetails struct {
|
||||
// Barcode
|
||||
Barcode string `json:"barcode"`
|
||||
|
||||
// All product barcodes
|
||||
Barcodes []string `json:"barcodes"`
|
||||
|
||||
// Main offer price on Ozon.
|
||||
//
|
||||
// The field is deprecated. Returns an empty string ""
|
||||
BuyboxPrice string `json:"buybox_price"`
|
||||
|
||||
// Category identifier
|
||||
CategoryId int64 `json:"category_id"`
|
||||
|
||||
// Marketing color
|
||||
ColorImage string `json:"color_image"`
|
||||
|
||||
// Commission fees details
|
||||
Commissions []struct {
|
||||
// Delivery cost
|
||||
DeliveryAmount float64 `json:"deliveryAmount"`
|
||||
|
||||
// Minimum commission fee
|
||||
MinValue float64 `json:"minValue"`
|
||||
|
||||
// Commission percentage
|
||||
Percent float64 `json:"percent"`
|
||||
|
||||
// Return cost
|
||||
ReturnAmount float64 `json:"returnAmount"`
|
||||
|
||||
// Sale scheme
|
||||
SaleSchema string `json:"saleSchema"`
|
||||
|
||||
// Commission fee amount
|
||||
Value float64 `json:"value"`
|
||||
} `json:"commissions"`
|
||||
|
||||
// Date and time when the product was created
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// SKU of the product that is sold from the Ozon warehouse (FBO)
|
||||
FBOSKU int64 `json:"fbo_sku"`
|
||||
|
||||
// SKU of the product that is sold from the seller's warehouse (FBS and rFBS)
|
||||
FBSSKU int64 `json:"fbs_sku"`
|
||||
|
||||
// Document generation task number
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// An array of links to images. The images in the array are arranged in the order of their arrangement on the site. If the `primary_image` parameter is not specified, the first image in the list is the main one for the product
|
||||
Images []string `json:"images"`
|
||||
|
||||
// Main product image
|
||||
PrimaryImage string `json:"primary_image"`
|
||||
|
||||
// Array of 360 images
|
||||
Images360 []string `json:"images360"`
|
||||
|
||||
// true if the product has markdown equivalents at the Ozon warehouse
|
||||
HasDiscountedItem bool `json:"has_discounted_item"`
|
||||
|
||||
// Indication of a markdown product:
|
||||
//
|
||||
// * true if the product was created by the seller as a markdown
|
||||
//
|
||||
// * false if the product is not markdown or was marked down by Ozon
|
||||
IsDiscounted bool `json:"is_discounted"`
|
||||
|
||||
// Markdown products stocks
|
||||
DiscountedStocks ProductDiscountedStocks `json:"discounted_stocks"`
|
||||
|
||||
// Indication of a bulky product
|
||||
IsKGT bool `json:"is_kgt"`
|
||||
|
||||
// Indication of mandatory prepayment for the product:
|
||||
//
|
||||
// * true — to buy a product, you need to make a prepayment.
|
||||
//
|
||||
// * false—prepayment is not required
|
||||
IsPrepayment bool `json:"is_prepayment"`
|
||||
|
||||
// If prepayment is possible, the value is true
|
||||
IsPrepaymentAllowed bool `json:"is_prepayment_allowed"`
|
||||
|
||||
// Currency of your prices. It matches the currency set in the personal account settings
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
|
||||
// The price of the product including all promotion discounts. This value will be shown on the Ozon storefront
|
||||
MarketingPrice string `json:"marketing_price"`
|
||||
|
||||
// Minimum price for similar products on Ozon.
|
||||
//
|
||||
// The field is deprecated. Returns an empty string ""
|
||||
MinOzonPrice string `json:"min_ozon_price"`
|
||||
|
||||
// Minimum product price with all promotions applied
|
||||
MinPrice string `json:"min_price"`
|
||||
|
||||
// Name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Price before discounts. Displayed strikethrough on the product description page
|
||||
OldPrice string `json:"old_price"`
|
||||
|
||||
// Price for customers with an Ozon Premium subscription
|
||||
PremiumPrice string `json:"premium_price"`
|
||||
|
||||
// Product price including discounts. This value is shown on the product description page
|
||||
Price string `json:"price"`
|
||||
|
||||
// Price index. Learn more in Help Center
|
||||
PriceIndex string `json:"price_idnex"`
|
||||
|
||||
// Product price suggested by the system based on similar offers
|
||||
RecommendedPrice string `json:"recommended_price"`
|
||||
|
||||
// Product state description
|
||||
Status struct {
|
||||
// Product state
|
||||
State string `json:"state"`
|
||||
|
||||
// Product state on the transition to which an error occurred
|
||||
StateFailed string `json:"state_failed"`
|
||||
|
||||
// Moderation status
|
||||
ModerateStatus string `json:"moderate_status"`
|
||||
|
||||
// Product decline reasons
|
||||
DeclineReasons []string `json:"decline_reasons"`
|
||||
|
||||
// Validation status
|
||||
ValidationsState string `json:"validation_state"`
|
||||
|
||||
// Product status name
|
||||
StateName string `json:"state_name"`
|
||||
|
||||
// Product state description
|
||||
StateDescription string `json:"state_description"`
|
||||
|
||||
// Indiction that there were errors while creating products
|
||||
IsFailed bool `json:"is_failed"`
|
||||
|
||||
// Indiction that the product was created
|
||||
IsCreated bool `json:"is_created"`
|
||||
|
||||
// Tooltips for the current product state
|
||||
StateTooltip string `json:"state_tooltip"`
|
||||
|
||||
// Product loading errors
|
||||
ItemErrors []GetProductDetailsResponseItemError `json:"item_errors"`
|
||||
|
||||
// The last time product state changed
|
||||
StateUpdatedAt time.Time `json:"state_updated_at"`
|
||||
} `json:"status"`
|
||||
|
||||
// Details about the sources of similar offers. Learn more in Help Сenter
|
||||
Sources []struct {
|
||||
// Indication that the source is taken into account when calculating the market value
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Link to the source
|
||||
Source string `json:"source"`
|
||||
} `json:"sources"`
|
||||
|
||||
// Details about product stocks
|
||||
Stocks struct {
|
||||
// Supply expected
|
||||
Coming int32 `json:"coming"`
|
||||
|
||||
// Currently at the warehouse
|
||||
Present int32 `json:"present"`
|
||||
|
||||
// Reserved
|
||||
Reserved int32 `json:"reserved"`
|
||||
} `json:"stocks"`
|
||||
|
||||
// Date of the last product update
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// Product VAT rate
|
||||
VAT string `json:"vat"`
|
||||
|
||||
// Product visibility settings
|
||||
VisibilityDetails struct {
|
||||
// If the product is active, the value is true
|
||||
ActiveProduct bool `json:"active_product"`
|
||||
|
||||
// If the price is set, the value is true
|
||||
HasPrice bool `json:"has_price"`
|
||||
|
||||
// If there is stock at the warehouses, the value is true
|
||||
HasStock bool `json:"has_stock"`
|
||||
} `json:"visibility_details"`
|
||||
|
||||
// If the product is on sale, the value is true
|
||||
Visible bool `json:"visible"`
|
||||
|
||||
// Product volume weight
|
||||
VolumeWeight float64 `json:"volume_weights"`
|
||||
}
|
||||
|
||||
type ProductDiscountedStocks struct {
|
||||
// Quantity of products to be supplied
|
||||
Coming int32 `json:"coming"`
|
||||
|
||||
// Quantity of products in warehouse
|
||||
Present int32 `json:"present"`
|
||||
|
||||
// Quantity of products reserved
|
||||
Reserved int32 `json:"reserved"`
|
||||
}
|
||||
type GetProductDetailsResponseItemError struct {
|
||||
// Error code
|
||||
Code string `json:"code"`
|
||||
@@ -927,3 +930,271 @@ func (c Products) GetProductsRatingBySKU(params *GetProductsRatingBySKUParams) (
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetProductImportStatusParams struct {
|
||||
// Importing products task code
|
||||
TaskId int64 `json:"task_id"`
|
||||
}
|
||||
|
||||
type GetProductImportStatusResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result struct {
|
||||
// Product details
|
||||
Items []struct {
|
||||
// Product identifier in the seller's system.
|
||||
//
|
||||
// The maximum length of a string is 50 characters
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Product creation status. Product information is processed in queues. Possible parameter values:
|
||||
// - pending — product in the processing queue;
|
||||
// - imported — product loaded successfully;
|
||||
// - failed — product loaded with errors
|
||||
Status string `json:"status"`
|
||||
|
||||
// Array of errors
|
||||
Errors []struct {
|
||||
GetProductDetailsResponseItemError
|
||||
|
||||
// Error technical description
|
||||
Message string `json:"message"`
|
||||
} `json:"errors"`
|
||||
} `json:"items"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
Total int32 `json:"total"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
// Allows you to get the status of a product description page creation process
|
||||
func (c Products) GetProductImportStatus(params *GetProductImportStatusParams) (*GetProductImportStatusResponse, error) {
|
||||
url := "/v1/product/import/info"
|
||||
|
||||
resp := &GetProductImportStatusResponse{}
|
||||
|
||||
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type CreateProductByOzonIDParams struct {
|
||||
// Products details
|
||||
Items []CreateProductsByOzonIDItem `json:"items"`
|
||||
}
|
||||
|
||||
type CreateProductsByOzonIDItem struct {
|
||||
// Product name. Up to 500 characters
|
||||
Name string `json:"name"`
|
||||
|
||||
// Product identifier in the seller's system.
|
||||
//
|
||||
// The maximum length of a string is 50 characters
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Price before discounts. Displayed strikethrough on the product description page. Specified in rubles.
|
||||
// The fractional part is separated by decimal point, up to two digits after the decimal point
|
||||
OldPrice string `json:"old_price"`
|
||||
|
||||
// Price for customers with an Ozon Premium subscription
|
||||
PremiumPrice string `json:"premium_price"`
|
||||
|
||||
// Product price including discounts. This value is shown on the product description page.
|
||||
// If there are no discounts, pass the old_price value in this parameter
|
||||
Price string `json:"price"`
|
||||
|
||||
// Currency of your prices. The passed value must be the same as the one set in the personal account settings.
|
||||
// By default, the passed value is RUB, Russian ruble.
|
||||
//
|
||||
// For example, if your currency set in the settings is yuan, pass the value CNY, otherwise an error will be returned
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// VAT rate for the product:
|
||||
// - 0 — not subject to VAT,
|
||||
// - 0.1 — 10%,
|
||||
// - 0.2 — 20%
|
||||
VAT string `json:"vat"`
|
||||
}
|
||||
|
||||
type CreateProductByOzonIDResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Products import task code
|
||||
TaskId int64 `json:"task_id"`
|
||||
|
||||
// Products identifiers list
|
||||
UnmatchedSKUList []int64 `json:"unmatched_sku_list"`
|
||||
}
|
||||
|
||||
// Creates a product by the specified Ozon ID. The number of products is unlimited.
|
||||
//
|
||||
// It's not possible to update products using Ozon ID
|
||||
func (c Products) CreateProductByOzonID(params *CreateProductByOzonIDParams) (*CreateProductByOzonIDResponse, error) {
|
||||
url := "/v1/product/import-by-sku"
|
||||
|
||||
resp := &CreateProductByOzonIDResponse{}
|
||||
|
||||
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type UpdateProductImagesParams struct {
|
||||
// Marketing color
|
||||
ColorImage string `json:"color_image"`
|
||||
|
||||
// Array of links to images. The images in the array are arranged in the order of their arrangement on the site.
|
||||
// The first image in the list is the main one for the product.
|
||||
//
|
||||
// Pass links to images in the public cloud storage. The image format is JPG
|
||||
Images []string `json:"images"`
|
||||
|
||||
// Array of 360 images—up to 70 files
|
||||
Images360 []string `json:"images360"`
|
||||
|
||||
// Product identfier
|
||||
ProductId int64 `json:"product_id"`
|
||||
}
|
||||
|
||||
type ProductInfoResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result struct {
|
||||
// Pictures
|
||||
Pictures []struct {
|
||||
// Attribute of a 360 image
|
||||
Is360 bool `json:"is_360"`
|
||||
|
||||
// Attribute of a marketing color
|
||||
IsColor bool `json:"is_color"`
|
||||
|
||||
// Attribute of a marketing color
|
||||
IsPrimary bool `json:"is_primary"`
|
||||
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Image uploading status.
|
||||
//
|
||||
// If the `/v1/product/pictures/import` method was called, the response will always be imported—image not processed.
|
||||
// To see the final status, call the `/v1/product/pictures/info` method after about 10 seconds.
|
||||
//
|
||||
// If you called the `/v1/product/pictures/info` method, one of the statuses will appear:
|
||||
// - uploaded — image uploaded;
|
||||
// - failed — image was not uploaded
|
||||
State string `json:"state"`
|
||||
|
||||
// The link to the image in the public cloud storage. The image format is JPG or PNG
|
||||
URL string `json:"url"`
|
||||
} `json:"pictures"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
// The method for uploading and updating product images.
|
||||
//
|
||||
// Each time you call the method, pass all the images that should be on the product description page.
|
||||
// For example, if you call a method and upload 10 images,
|
||||
// and then call the method a second time and load one imahe, then all 10 previous ones will be erased.
|
||||
//
|
||||
// To upload image, pass a link to it in a public cloud storage. The image format is JPG or PNG.
|
||||
//
|
||||
// Arrange the pictures in the images array as you want to see them on the site.
|
||||
// The first picture in the array will be the main one for the product.
|
||||
//
|
||||
// You can upload up to 15 pictures for each product.
|
||||
//
|
||||
// To upload 360 images, use the images360 field, and to upload a marketing color use color_image.
|
||||
//
|
||||
// If you want to add, remove, or replace some images, or change their order,
|
||||
// first get the details using `/v2/product/info` or `/v2/product/info/list` methods.
|
||||
// Using them you can get the current list of images and their order.
|
||||
// Copy the data from the images, images360, and color_image fields and make the necessary changes to it
|
||||
func (c Products) UpdateProductImages(params *UpdateProductImagesParams) (*ProductInfoResponse, error) {
|
||||
url := "/v1/product/pictures/import"
|
||||
|
||||
resp := &ProductInfoResponse{}
|
||||
|
||||
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type CheckImageUploadingStatusParams struct {
|
||||
// Product identifiers list
|
||||
ProductId []int64 `json:"product_id"`
|
||||
}
|
||||
|
||||
// Check products images uploading status
|
||||
func (c Products) CheckImageUploadingStatus(params *CheckImageUploadingStatusParams) (*ProductInfoResponse, error) {
|
||||
url := "/v1/product/pictures/info"
|
||||
|
||||
resp := &ProductInfoResponse{}
|
||||
|
||||
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ListProductsByIDsParams struct {
|
||||
// Product identifier in the seller's system
|
||||
OfferId []string `json:"offer_id"`
|
||||
|
||||
// Product identifier
|
||||
ProductId []int64 `json:"product_id"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU []int64 `json:"sku"`
|
||||
}
|
||||
|
||||
type ListProductsByIDsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Request results
|
||||
Result struct {
|
||||
// Data array
|
||||
Items []ProductDetails `json:"items"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
// Method for getting an array of products by their identifiers.
|
||||
//
|
||||
// The request body must contain an array of identifiers of the same type. The response will contain an items array.
|
||||
//
|
||||
// For each shipment in the items array the fields match the ones recieved in the /v2/product/info method
|
||||
func (c Products) ListProductsByIDs(params *ListProductsByIDsParams) (*ListProductsByIDsResponse, error) {
|
||||
url := "/v2/product/info/list"
|
||||
|
||||
resp := &ListProductsByIDsResponse{}
|
||||
|
||||
response, err := c.client.Request(http.MethodPost, url, params, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -1006,3 +1006,472 @@ func TestGetProductsRatingBySKU(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProductImportStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetProductImportStatusParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetProductImportStatusParams{
|
||||
TaskId: 172549793,
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"items": [
|
||||
{
|
||||
"offer_id": "143210608",
|
||||
"product_id": 137285792,
|
||||
"status": "imported",
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetProductImportStatusParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
resp, err := c.Products().GetProductImportStatus(test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Result.Items) > 0 {
|
||||
if resp.Result.Items[0].ProductId == 0 {
|
||||
t.Errorf("Product id cannot be 0")
|
||||
}
|
||||
if resp.Result.Items[0].OfferId == "" {
|
||||
t.Errorf("Offer id cannot be empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProductByOzonID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *CreateProductByOzonIDParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&CreateProductByOzonIDParams{
|
||||
Items: []CreateProductsByOzonIDItem{
|
||||
{
|
||||
Name: "string",
|
||||
OfferId: "91132",
|
||||
OldPrice: "2590",
|
||||
Price: "2300",
|
||||
PremiumPrice: "2200",
|
||||
CurrencyCode: "RUB",
|
||||
SKU: 298789742,
|
||||
VAT: "0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"task_id": 176594213,
|
||||
"unmatched_sku_list": []
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&CreateProductByOzonIDParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
resp, err := c.Products().CreateProductByOzonID(test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateProductImages(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *UpdateProductImagesParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&UpdateProductImagesParams{
|
||||
ColorImage: "string",
|
||||
Images: []string{"string"},
|
||||
Images360: []string{"string"},
|
||||
ProductId: 12345,
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"pictures": [
|
||||
{
|
||||
"is_360": true,
|
||||
"is_color": true,
|
||||
"is_primary": true,
|
||||
"product_id": 12345,
|
||||
"state": "string",
|
||||
"url": "string"
|
||||
},
|
||||
{
|
||||
"is_360": false,
|
||||
"is_color": true,
|
||||
"is_primary": true,
|
||||
"product_id": 12345,
|
||||
"state": "string",
|
||||
"url": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&UpdateProductImagesParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
resp, err := c.Products().UpdateProductImages(test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Result.Pictures) != len(test.params.Images)+len(test.params.Images360) {
|
||||
t.Errorf("Amount of pictures in request and response are not equal")
|
||||
}
|
||||
if len(resp.Result.Pictures) > 0 {
|
||||
if resp.Result.Pictures[0].ProductId != test.params.ProductId {
|
||||
t.Errorf("Product ids in request and response are not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckImageUploadingStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *CheckImageUploadingStatusParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&CheckImageUploadingStatusParams{
|
||||
ProductId: []int64{123456},
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"pictures": [
|
||||
{
|
||||
"is_360": true,
|
||||
"is_color": true,
|
||||
"is_primary": true,
|
||||
"product_id": 123456,
|
||||
"state": "string",
|
||||
"url": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&CheckImageUploadingStatusParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
resp, err := c.Products().CheckImageUploadingStatus(test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Result.Pictures) > 0 {
|
||||
if resp.Result.Pictures[0].ProductId != test.params.ProductId[0] {
|
||||
t.Errorf("Product ids in request and response are not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListProductsByIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListProductsByIDsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListProductsByIDsParams{
|
||||
OfferId: []string{"010", "23"},
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"items": [
|
||||
{
|
||||
"id": 78712196,
|
||||
"name": "Как выбрать детские музыкальные инструменты. Ксилофон, бубен, маракасы и другие инструменты для детей до 6 лет. Мастер-класс о раннем музыкальном развитии от Монтессори-педагога",
|
||||
"offer_id": "010",
|
||||
"barcode": "",
|
||||
"barcodes": [
|
||||
"2335900005",
|
||||
"7533900005"
|
||||
],
|
||||
"buybox_price": "",
|
||||
"category_id": 93726157,
|
||||
"created_at": "2021-06-03T03:40:05.871465Z",
|
||||
"images": [],
|
||||
"has_discounted_item": true,
|
||||
"is_discounted": true,
|
||||
"discounted_stocks": {
|
||||
"coming": 0,
|
||||
"present": 0,
|
||||
"reserved": 0
|
||||
},
|
||||
"currency_code": "RUB",
|
||||
"marketing_price": "",
|
||||
"min_price": "",
|
||||
"old_price": "1000.0000",
|
||||
"premium_price": "590.0000",
|
||||
"price": "690.0000",
|
||||
"recommended_price": "",
|
||||
"sources": [
|
||||
{
|
||||
"is_enabled": true,
|
||||
"sku": 269628393,
|
||||
"source": "fbo"
|
||||
},
|
||||
{
|
||||
"is_enabled": true,
|
||||
"sku": 269628396,
|
||||
"source": "fbs"
|
||||
}
|
||||
],
|
||||
"state": "",
|
||||
"stocks": {
|
||||
"coming": 0,
|
||||
"present": 13,
|
||||
"reserved": 0
|
||||
},
|
||||
"errors": [],
|
||||
"updated_at": "2023-02-09T06:46:44.152Z",
|
||||
"vat": "0.0",
|
||||
"visible": true,
|
||||
"visibility_details": {
|
||||
"has_price": false,
|
||||
"has_stock": true,
|
||||
"active_product": false,
|
||||
"reasons": {}
|
||||
},
|
||||
"price_index": "0.00",
|
||||
"images360": [],
|
||||
"is_kgt": false,
|
||||
"color_image": "",
|
||||
"primary_image": "https://cdn1.ozone.ru/s3/multimedia-y/6077810038.jpg",
|
||||
"status": {
|
||||
"state": "price_sent",
|
||||
"state_failed": "",
|
||||
"moderate_status": "approved",
|
||||
"decline_reasons": [],
|
||||
"validation_state": "success",
|
||||
"state_name": "Продается",
|
||||
"state_description": "",
|
||||
"is_failed": false,
|
||||
"is_created": true,
|
||||
"state_tooltip": "",
|
||||
"item_errors": [],
|
||||
"state_updated_at": "2021-07-26T04:50:08.486697Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 76723583,
|
||||
"name": "Онлайн-курс по дрессировке собак \"Собака: инструкция по применению. Одинокий волк\"",
|
||||
"offer_id": "23",
|
||||
"barcode": "",
|
||||
"buybox_price": "",
|
||||
"category_id": 90635895,
|
||||
"created_at": "2021-05-26T20:26:07.565586Z",
|
||||
"images": [],
|
||||
"marketing_price": "",
|
||||
"min_price": "",
|
||||
"old_price": "12200.0000",
|
||||
"premium_price": "5490.0000",
|
||||
"price": "6100.0000",
|
||||
"recommended_price": "",
|
||||
"sources": [
|
||||
{
|
||||
"is_enabled": true,
|
||||
"sku": 267684495,
|
||||
"source": "fbo"
|
||||
},
|
||||
{
|
||||
"is_enabled": true,
|
||||
"sku": 267684498,
|
||||
"source": "fbs"
|
||||
}
|
||||
],
|
||||
"state": "",
|
||||
"stocks": {
|
||||
"coming": 0,
|
||||
"present": 19,
|
||||
"reserved": 0
|
||||
},
|
||||
"errors": [],
|
||||
"updated_at": "2023-02-09T06:46:44.152Z",
|
||||
"vat": "0.0",
|
||||
"visible": true,
|
||||
"visibility_details": {
|
||||
"has_price": false,
|
||||
"has_stock": true,
|
||||
"active_product": false,
|
||||
"reasons": {}
|
||||
},
|
||||
"price_index": "0.00",
|
||||
"images360": [],
|
||||
"is_kgt": false,
|
||||
"color_image": "",
|
||||
"primary_image": "https://cdn1.ozone.ru/s3/multimedia-v/6062554531.jpg",
|
||||
"status": {
|
||||
"state": "price_sent",
|
||||
"state_failed": "",
|
||||
"moderate_status": "approved",
|
||||
"decline_reasons": [],
|
||||
"validation_state": "success",
|
||||
"state_name": "Продается",
|
||||
"state_description": "",
|
||||
"is_failed": false,
|
||||
"is_created": true,
|
||||
"state_tooltip": "",
|
||||
"item_errors": [],
|
||||
"state_updated_at": "2021-05-31T12:35:09.714641Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListProductsByIDsParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
resp, err := c.Products().ListProductsByIDs(test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Result.Items) != len(test.params.OfferId) {
|
||||
t.Errorf("Amount of offer ids in request and response are not equal")
|
||||
}
|
||||
if len(resp.Result.Items) > 0 {
|
||||
if resp.Result.Items[0].OfferId != test.params.OfferId[0] {
|
||||
t.Errorf("Offer ids in request and response are not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user