Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c609b7c7a |
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
178
ENDPOINTS.md
Normal file
178
ENDPOINTS.md
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
# Supported Endpoints
|
||||||
|
|
||||||
|
## Ozon attributes and characteristics
|
||||||
|
- [x] Product category tree
|
||||||
|
- [x] Category characteristics list
|
||||||
|
- [x] Characteristics value directory
|
||||||
|
- [x] Search characteristics value directory
|
||||||
|
|
||||||
|
## Uploading and updating products
|
||||||
|
- [x] Create or update a product
|
||||||
|
- [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
|
||||||
|
- [x] Get a list of products by identifiers
|
||||||
|
- [x] Get a description of the product characteristics
|
||||||
|
- [x] Get product description
|
||||||
|
- [x] Product range limit, limits on product creation and update
|
||||||
|
- [x] Change product identifiers from the seller's system
|
||||||
|
- [x] Archive a product
|
||||||
|
- [x] Unarchive a product
|
||||||
|
- [x] Remove a product without an SKU from the archive
|
||||||
|
- [x] Get a list of geo-restrictions for services
|
||||||
|
- [x] Upload activation codes for services and digital products
|
||||||
|
- [x] Status of uploading activation codes
|
||||||
|
|
||||||
|
## Prices and Stocks
|
||||||
|
- [x] Update stocks
|
||||||
|
- [x] Update the quantity of products in stock
|
||||||
|
- [x] Information about product quantity
|
||||||
|
- [x] Stocks in seller's warehouses (FBS и rFBS)
|
||||||
|
- [x] Update prices
|
||||||
|
- [x] Get product price information
|
||||||
|
- [x] Get information about the markdown and the main product by the markdown product SKU
|
||||||
|
- [x] Set a discount on a markdown product
|
||||||
|
|
||||||
|
## Promotions
|
||||||
|
- [x] Available promotions
|
||||||
|
- [x] Products that can participate in a promotion
|
||||||
|
- [x] Products in a promotion
|
||||||
|
- [x] Add products to promotion
|
||||||
|
- [x] Remove products from promotion
|
||||||
|
- [x] List of available Hot Sale promotions
|
||||||
|
- [x] List of products participating in the Hot Sale promotion
|
||||||
|
- [x] Add products to the Hot Sale promotion
|
||||||
|
- [x] Remove product from the Hot Sale promotion
|
||||||
|
- [x] List of discount requests
|
||||||
|
- [x] Approve a discount request
|
||||||
|
- [x] Decline a discount request
|
||||||
|
|
||||||
|
## Brand certificates
|
||||||
|
- [x] List of certified brands
|
||||||
|
|
||||||
|
## Quality certificates
|
||||||
|
- [x] List of accordance types (version 2)
|
||||||
|
- [x] Directory of document types
|
||||||
|
- [x] List of certified categories
|
||||||
|
- [x] Adding certificates for products
|
||||||
|
- [x] Link the certificate to the product
|
||||||
|
- [x] Delete certificate
|
||||||
|
- [x] Certificate information
|
||||||
|
- [x] Certificates list
|
||||||
|
- [x] Product statuses list
|
||||||
|
- [x] List of products associated with the certificate
|
||||||
|
- [x] Unbind products from a certificate
|
||||||
|
- [x] Possible certificate rejection reasons
|
||||||
|
- [x] Possible certificate statuses
|
||||||
|
|
||||||
|
## Warehouses
|
||||||
|
- [x] List of warehouses
|
||||||
|
- [x] List of delivery methods for a warehouse
|
||||||
|
|
||||||
|
## Polygons
|
||||||
|
- [x] Create delivery polygon
|
||||||
|
- [x] Link delivery method to a delivery polygon
|
||||||
|
- [x] Delete polygon
|
||||||
|
|
||||||
|
## FBO
|
||||||
|
- [x] Shipments list
|
||||||
|
- [x] Shipment details
|
||||||
|
|
||||||
|
## FBS and rFBS products labeling
|
||||||
|
- [x] Validate labeling codes
|
||||||
|
- [x] Check and save product items data
|
||||||
|
- [x] Get product items check statuses
|
||||||
|
- [x] Pack the order (version 4)
|
||||||
|
|
||||||
|
## FBS and rFBS
|
||||||
|
- [x] List of unprocessed shipments (version 3)
|
||||||
|
- [x] Shipments list (version 3)
|
||||||
|
- [x] Get shipment details by identifier (version 3)
|
||||||
|
- [x] Get shipment data by barcode
|
||||||
|
- [x] List of manufacturing countries
|
||||||
|
- [x] Set the manufacturing country
|
||||||
|
- [x] Specify number of boxes for multi-box shipments
|
||||||
|
- [x] Get drop-off point restrictions
|
||||||
|
- [x] Partial pack the order
|
||||||
|
- [x] Create an acceptance and transfer certificate and a waybill
|
||||||
|
- [x] Status of acceptance and transfer certificate and waybill
|
||||||
|
- [x] Available freights list
|
||||||
|
- [x] Get acceptance and transfer certificate and waybill
|
||||||
|
- [x] Generating status of digital acceptance and transfer certificate and waybill
|
||||||
|
- [x] Get digital shipment certificate
|
||||||
|
- [x] Print the labeling
|
||||||
|
- [x] Create a task to generate labeling
|
||||||
|
- [x] Get a labeling file
|
||||||
|
- [x] Package unit labels
|
||||||
|
- [x] Open a dispute over a shipment
|
||||||
|
- [x] Pass the shipment to shipping
|
||||||
|
- [x] Shipment cancellation reasons
|
||||||
|
- [x] Shipments cancellation reasons
|
||||||
|
- [x] Cancel the shipment
|
||||||
|
- [x] Add weight for bulk products in a shipment
|
||||||
|
- [x] Cancel sending some products in the shipment
|
||||||
|
- [x] List of shipment certificates
|
||||||
|
- [x] Sign shipment certificates
|
||||||
|
- [x] List of shipments in the certificate
|
||||||
|
- [x] Change the status to "Delivering"
|
||||||
|
- [x] Add tracking numbers
|
||||||
|
- [x] Change the status to "Last Mile"
|
||||||
|
- [x] Change the status to "Delivered"
|
||||||
|
- [x] Change status to "Sent by seller"
|
||||||
|
- [x] Dates available for delivery reschedule
|
||||||
|
- [x] Reschedule shipment delivery date
|
||||||
|
- [x] ETGB customs declarations
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
- [x] Get information about FBO returns (version 3)
|
||||||
|
- [x] Get information about FBS returns
|
||||||
|
|
||||||
|
## Cancellations
|
||||||
|
- [x] Get information about a rFBS cancellation request
|
||||||
|
- [x] Get a list of rFBS cancellation requests
|
||||||
|
- [x] Approve a rFBS cancellation request
|
||||||
|
- [x] Reject a rFBS cancellation request
|
||||||
|
|
||||||
|
## Chats with customers
|
||||||
|
- [x] Chats list
|
||||||
|
- [x] Send message
|
||||||
|
- [x] Send file
|
||||||
|
- [x] Chat history
|
||||||
|
- [x] Update chat
|
||||||
|
- [x] Create a new chat
|
||||||
|
- [x] Mark messages as read
|
||||||
|
|
||||||
|
## Invoices
|
||||||
|
- [x] Create or edit proforma invoice link
|
||||||
|
- [x] Get a proforma invoice link
|
||||||
|
- [x] Delete the proforma invoice link
|
||||||
|
|
||||||
|
## Reports
|
||||||
|
- [x] Report details
|
||||||
|
- [x] Reports list
|
||||||
|
- [x] Products report
|
||||||
|
- [x] Stocks report
|
||||||
|
- [x] Report on products movement
|
||||||
|
- [x] Returns report
|
||||||
|
- [x] Shipment report
|
||||||
|
- [x] Financial report
|
||||||
|
- [x] Issue a report on discounted products
|
||||||
|
- [x] Report on discounted products
|
||||||
|
- [x] List of reports on discounted products
|
||||||
|
|
||||||
|
## Analytics
|
||||||
|
- [x] Analytics data
|
||||||
|
- [x] Stocks and products report (version 2)
|
||||||
|
|
||||||
|
## Finance
|
||||||
|
- [x] Report on sold products
|
||||||
|
- [x] Transactions list (version 3)
|
||||||
|
- [x] Total transactions sum
|
||||||
|
|
||||||
|
## Seller rating
|
||||||
|
- [x] Get information on current seller ratings
|
||||||
|
- [x] Get information on seller ratings for the period
|
||||||
15
README.md
15
README.md
@@ -2,19 +2,21 @@
|
|||||||
A Ozon Seller API client written in Golang
|
A Ozon Seller API client written in Golang
|
||||||
|
|
||||||
[](https://coveralls.io/github/diPhantxm/ozon-api-client)
|
[](https://coveralls.io/github/diPhantxm/ozon-api-client)
|
||||||

|

|
||||||
|
|
||||||
[Ozon](https://ozon.ru) is a marketplace for small and medium enterprises to launch and grow their businesses in Russia.
|
[Ozon](https://ozon.ru) is a marketplace for small and medium enterprises to launch and grow their businesses in Russia.
|
||||||
|
|
||||||
Read full [documentation](https://docs.ozon.ru/api/seller/en/#tag/Introduction)
|
Read full [documentation](https://docs.ozon.ru/api/seller/en/#tag/Introduction)
|
||||||
|
|
||||||
|
You can check [list of supported endpoints](ENDPOINTS.md)
|
||||||
|
|
||||||
## How to start
|
## How to start
|
||||||
### API
|
### API
|
||||||
Get Client-Id and Api-Key in your seller profile [here](https://seller.ozon.ru/app/settings/api-keys?locale=en)
|
Get Client-Id and Api-Key in your seller profile [here](https://seller.ozon.ru/app/settings/api-keys?locale=en)
|
||||||
|
|
||||||
Just add dependency to your project and you're ready to go.
|
Just add dependency to your project and you're ready to go.
|
||||||
```bash
|
```bash
|
||||||
go get git.denco.store/fakz9/ozon-api-client
|
go get github.com/diphantxm/ozon-api-client
|
||||||
```
|
```
|
||||||
A simple example on how to use this library:
|
A simple example on how to use this library:
|
||||||
```Golang
|
```Golang
|
||||||
@@ -26,7 +28,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.denco.store/fakz9/ozon-api-client/ozon"
|
"github.com/diphantxm/ozon-api-client/ozon"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -65,7 +67,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.denco.store/fakz9/ozon-api-client/ozon/notifications"
|
"github.com/diphantxm/ozon-api-client/ozon/notifications"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -89,3 +91,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
If you need some endpoints ASAP, create an issue and list all the endpoints. I will add them to library soon.
|
||||||
|
|
||||||
|
Or you can implement them and contribute to the project. Contribution to the project is welcome.
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -1,4 +1,4 @@
|
|||||||
module git.denco.store/fakz9/ozon-api-client
|
module github.com/diphantxm/ozon-api-client
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Analytics struct {
|
type Analytics struct {
|
||||||
@@ -200,9 +200,12 @@ type GetStocksOnWarehousesResultRow struct {
|
|||||||
|
|
||||||
// Name of the warehouse where the products are stored
|
// Name of the warehouse where the products are stored
|
||||||
WarehouseName string `json:"warehouse_name"`
|
WarehouseName string `json:"warehouse_name"`
|
||||||
|
|
||||||
|
// Number of days the stock will last based on your average daily sales
|
||||||
|
IDC float64 `json:"idc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method for getting a report on leftover stocks and products movement at Ozon warehouses
|
// Report on stocks and products movement at Ozon warehouses
|
||||||
func (c Analytics) GetStocksOnWarehouses(ctx context.Context, params *GetStocksOnWarehousesParams) (*GetStocksOnWarehousesResponse, error) {
|
func (c Analytics) GetStocksOnWarehouses(ctx context.Context, params *GetStocksOnWarehousesParams) (*GetStocksOnWarehousesResponse, error) {
|
||||||
url := "/v2/analytics/stock_on_warehouses"
|
url := "/v2/analytics/stock_on_warehouses"
|
||||||
|
|
||||||
@@ -337,114 +340,3 @@ func (c Analytics) Stock(ctx context.Context, params *GetStockManagementParams)
|
|||||||
|
|
||||||
return resp, nil
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetAnalyticsData(t *testing.T) {
|
func TestGetAnalyticsData(t *testing.T) {
|
||||||
@@ -277,74 +277,3 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Barcodes struct {
|
type Barcodes struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateBarcodes(t *testing.T) {
|
func TestGenerateBarcodes(t *testing.T) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Brands struct {
|
type Brands struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListCertifiedBrands(t *testing.T) {
|
func TestListCertifiedBrands(t *testing.T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Cancellations struct {
|
type Cancellations struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetCancellationInfo(t *testing.T) {
|
func TestGetCancellationInfo(t *testing.T) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Categories struct {
|
type Categories struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetProductTree(t *testing.T) {
|
func TestGetProductTree(t *testing.T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Certificates struct {
|
type Certificates struct {
|
||||||
@@ -99,6 +99,11 @@ type ListOfCertifiedCategoriesParams struct {
|
|||||||
type ListOfCertifiedCategoriesResponse struct {
|
type ListOfCertifiedCategoriesResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result ListOfCertifiedCategoriesResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListOfCertifiedCategoriesResult struct {
|
||||||
// Certified categories details
|
// Certified categories details
|
||||||
Certification []ListOfCertifiedCategoriesResultCert `json:"certification"`
|
Certification []ListOfCertifiedCategoriesResultCert `json:"certification"`
|
||||||
|
|
||||||
@@ -107,25 +112,16 @@ type ListOfCertifiedCategoriesResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListOfCertifiedCategoriesResultCert struct {
|
type ListOfCertifiedCategoriesResultCert struct {
|
||||||
// Identifier of the certified category
|
|
||||||
CategoryId int64 `json:"category_id"`
|
|
||||||
|
|
||||||
// Category name
|
// Category name
|
||||||
CategoryName string `json:"category_name"`
|
CategoryName string `json:"category_name"`
|
||||||
|
|
||||||
// Indication of a mandatory category
|
// Indication of a mandatory category
|
||||||
IsRequired bool `json:"is_required"`
|
IsRequired bool `json:"is_required"`
|
||||||
|
|
||||||
// Type identifier of the certified category
|
|
||||||
TypeId int64 `json:"type_id"`
|
|
||||||
|
|
||||||
// Name of the type of certified category
|
|
||||||
TypeName string `json:"type_name"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of certified categories
|
// List of certified categories
|
||||||
func (c Certificates) ListOfCertifiedCategories(ctx context.Context, params *ListOfCertifiedCategoriesParams) (*ListOfCertifiedCategoriesResponse, error) {
|
func (c Certificates) ListOfCertifiedCategories(ctx context.Context, params *ListOfCertifiedCategoriesParams) (*ListOfCertifiedCategoriesResponse, error) {
|
||||||
url := "/v2/product/certification/list"
|
url := "/v1/product/certification/list"
|
||||||
|
|
||||||
resp := &ListOfCertifiedCategoriesResponse{}
|
resp := &ListOfCertifiedCategoriesResponse{}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListOfAccordanceTypes(t *testing.T) {
|
func TestListOfAccordanceTypes(t *testing.T) {
|
||||||
@@ -151,16 +151,15 @@ func TestListOfCertifiedCategories(t *testing.T) {
|
|||||||
PageSize: 100,
|
PageSize: 100,
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
|
"result": {
|
||||||
"certification": [
|
"certification": [
|
||||||
{
|
{
|
||||||
"category_id": 0,
|
|
||||||
"category_name": "string",
|
|
||||||
"is_required": true,
|
"is_required": true,
|
||||||
"type_id": 0,
|
"category_name": "Витаминно-минеральные комплексы для взрослых"
|
||||||
"type_name": "string"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total": 1
|
"total": 1
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Chats struct {
|
type Chats struct {
|
||||||
@@ -164,8 +164,6 @@ type ChatHistoryParams struct {
|
|||||||
// The default value is `Backward`. You can set the number of messages in the limit parameter
|
// The default value is `Backward`. You can set the number of messages in the limit parameter
|
||||||
Direction string `json:"direction" default:"Backward"`
|
Direction string `json:"direction" default:"Backward"`
|
||||||
|
|
||||||
Filter *ChatHistoryFilter `json:"filter,omitempty"`
|
|
||||||
|
|
||||||
// Identifier of the message from which the chat history will be displayed.
|
// Identifier of the message from which the chat history will be displayed.
|
||||||
// Default value is the last visible message
|
// Default value is the last visible message
|
||||||
FromMessageId string `json:"from_message_id"`
|
FromMessageId string `json:"from_message_id"`
|
||||||
@@ -174,10 +172,6 @@ type ChatHistoryParams struct {
|
|||||||
Limit int64 `json:"limit" default:"50"`
|
Limit int64 `json:"limit" default:"50"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatHistoryFilter struct {
|
|
||||||
MessageIds []string `json:"message_ids"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatHistoryResponse struct {
|
type ChatHistoryResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
@@ -189,33 +183,22 @@ type ChatHistoryResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChatHistoryMessage struct {
|
type ChatHistoryMessage struct {
|
||||||
Context *ChatHistoryContext `json:"context,omitempty"`
|
|
||||||
|
|
||||||
// Message creation date
|
// Message creation date
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
||||||
// Array with message content in Markdown format
|
// Array with message content in Markdown format
|
||||||
Data []string `json:"data"`
|
Data []string `json:"data"`
|
||||||
|
|
||||||
IsImage bool `json:"is_image"`
|
|
||||||
|
|
||||||
// Indication of the read message
|
// Indication of the read message
|
||||||
IsRead bool `json:"is_read"`
|
IsRead bool `json:"is_read"`
|
||||||
|
|
||||||
// Message identifier
|
// Message identifier
|
||||||
MessageId string `json:"message_id"`
|
MessageId string `json:"message_id"`
|
||||||
|
|
||||||
ModarateImageStatus string `json:"moderate_image_status"`
|
|
||||||
|
|
||||||
// Chat participant identifier
|
// Chat participant identifier
|
||||||
User ChatHistoryMessageUser `json:"user"`
|
User ChatHistoryMessageUser `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatHistoryContext struct {
|
|
||||||
OrderNumber string `json:"order_number"`
|
|
||||||
SKU string `json:"sku"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatHistoryMessageUser struct {
|
type ChatHistoryMessageUser struct {
|
||||||
// Chat participant identifier
|
// Chat participant identifier
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@@ -231,7 +214,7 @@ type ChatHistoryMessageUser struct {
|
|||||||
|
|
||||||
// Returns the history of chat messages. By default messages are shown from newest to oldest.
|
// Returns the history of chat messages. By default messages are shown from newest to oldest.
|
||||||
func (c Chats) History(ctx context.Context, params *ChatHistoryParams) (*ChatHistoryResponse, error) {
|
func (c Chats) History(ctx context.Context, params *ChatHistoryParams) (*ChatHistoryResponse, error) {
|
||||||
url := "/v3/chat/history"
|
url := "/v2/chat/history"
|
||||||
|
|
||||||
resp := &ChatHistoryResponse{}
|
resp := &ChatHistoryResponse{}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListChats(t *testing.T) {
|
func TestListChats(t *testing.T) {
|
||||||
@@ -212,22 +212,16 @@ func TestChatHistory(t *testing.T) {
|
|||||||
"has_next": true,
|
"has_next": true,
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"context": {
|
|
||||||
"order_number": "123456789",
|
|
||||||
"sku": "987654321"
|
|
||||||
},
|
|
||||||
"created_at": "2019-08-24T14:15:22Z",
|
|
||||||
"data": [
|
|
||||||
"Здравствуйте, у меня вопрос по вашему товару \"Стекло защитное для смартфонов\", артикул 11223. Подойдет ли он на данную [ модель ](https://www.ozon.ru/product/smartfon-samsung-galaxy-a03s-4-64-gb-chernyy) телефона?"
|
|
||||||
],
|
|
||||||
"is_image": true,
|
|
||||||
"is_read": true,
|
|
||||||
"message_id": "3000000000817031942",
|
"message_id": "3000000000817031942",
|
||||||
"moderate_image_status": "SUCCESS",
|
|
||||||
"user": {
|
"user": {
|
||||||
"id": "115568",
|
"id": "115568",
|
||||||
"type": "Сustomer"
|
"type": "Сustomer"
|
||||||
}
|
},
|
||||||
|
"created_at": "2022-07-18T20:58:04.528Z",
|
||||||
|
"is_read": true,
|
||||||
|
"data": [
|
||||||
|
"Здравствуйте, у меня вопрос по вашему товару \"Стекло защитное для смартфонов\", артикул 11223. Подойдет ли он на данную [ модель ](https://www.ozon.ru/product/smartfon-samsung-galaxy-a03s-4-64-gb-chernyy) телефона?"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`,
|
}`,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Clusters struct {
|
type Clusters struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListClusters(t *testing.T) {
|
func TestListClusters(t *testing.T) {
|
||||||
|
|||||||
@@ -923,13 +923,3 @@ const (
|
|||||||
// awaiting shipping
|
// awaiting shipping
|
||||||
VisualStatusWaitingShipment VisualStatus = "WaitingShipment"
|
VisualStatusWaitingShipment VisualStatus = "WaitingShipment"
|
||||||
)
|
)
|
||||||
|
|
||||||
type VAT string
|
|
||||||
|
|
||||||
const (
|
|
||||||
VAT0 VAT = "0"
|
|
||||||
VAT005 VAT = "0.05"
|
|
||||||
VAT007 VAT = "0.07"
|
|
||||||
VAT01 VAT = "0.1"
|
|
||||||
VAT02 VAT = "0.2"
|
|
||||||
)
|
|
||||||
|
|||||||
184
ozon/fbo.go
184
ozon/fbo.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FBO struct {
|
type FBO struct {
|
||||||
@@ -338,9 +338,6 @@ type GetSupplyRequestInfoResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SupplyOrder struct {
|
type SupplyOrder struct {
|
||||||
// true if the supply request can be canceled
|
|
||||||
CanCancel bool `json:"can_cancel"`
|
|
||||||
|
|
||||||
// Date of supply request creation
|
// Date of supply request creation
|
||||||
CreationDate string `json:"creation_date"`
|
CreationDate string `json:"creation_date"`
|
||||||
|
|
||||||
@@ -353,18 +350,6 @@ type SupplyOrder struct {
|
|||||||
// Supply warehouse identifier
|
// Supply warehouse identifier
|
||||||
DropoffWarehouseId int64 `json:"dropoff_warehouse_id"`
|
DropoffWarehouseId int64 `json:"dropoff_warehouse_id"`
|
||||||
|
|
||||||
// true if the supply request contains Super Economy products
|
|
||||||
IsEconom bool `json:"is_econom"`
|
|
||||||
|
|
||||||
// true if the seller has Super supplies enabled
|
|
||||||
IsSuperFBO bool `json:"is_super_fbo"`
|
|
||||||
|
|
||||||
// true if the supply request is virtual
|
|
||||||
IsVirtual bool `json:"is_virtual"`
|
|
||||||
|
|
||||||
// true if the supply request contains Super products
|
|
||||||
ProductSuperFBO bool `json:"product_super_fbo"`
|
|
||||||
|
|
||||||
// Filter by supply status
|
// Filter by supply status
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
|
|
||||||
@@ -388,33 +373,13 @@ type Supply struct {
|
|||||||
// Supply contents identifier. Used in the /v1/supply-order/bundle method
|
// Supply contents identifier. Used in the /v1/supply-order/bundle method
|
||||||
BundleId string `json:"bundle_id"`
|
BundleId string `json:"bundle_id"`
|
||||||
|
|
||||||
// Filter by supply status
|
|
||||||
SupplyState string `json:"supply_state"`
|
|
||||||
|
|
||||||
// Storage warehouse identifier
|
// Storage warehouse identifier
|
||||||
StorageWarehouseId int64 `json:"storage_warehouse_id"`
|
StorageWarehouseId int64 `json:"storage_warehouse_id"`
|
||||||
|
|
||||||
// Product tags in the supply request
|
|
||||||
SupplyTags []SupplyTag `json:"supply_tags"`
|
|
||||||
|
|
||||||
// Supply identifier
|
// Supply identifier
|
||||||
Id int64 `json:"supply_id"`
|
Id int64 `json:"supply_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SupplyTag struct {
|
|
||||||
// true if the supply request contains products certified in the Mercury system
|
|
||||||
IsEVSDRequired bool `json:"is_evsd_required"`
|
|
||||||
|
|
||||||
// true if the supply request contains jewelry
|
|
||||||
IsJewelry bool `json:"is_jewelry"`
|
|
||||||
|
|
||||||
// true if the supply request contains products for which labeling is possible
|
|
||||||
IsMarkingPossible bool `json:"is_marking_possible"`
|
|
||||||
|
|
||||||
// true if the supply request contains products for which labeling is mandatory
|
|
||||||
IsMarkingRequired bool `json:"is_marking_required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SupplyTimeslot struct {
|
type SupplyTimeslot struct {
|
||||||
// Reason why you can't select the supply time slot
|
// Reason why you can't select the supply time slot
|
||||||
Reasons []string `json:"can_not_set_reasons"`
|
Reasons []string `json:"can_not_set_reasons"`
|
||||||
@@ -507,6 +472,64 @@ func (c FBO) GetSupplyRequestInfo(ctx context.Context, params *GetSupplyRequestI
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ListProductsInSupplyRequestParams struct {
|
||||||
|
// Number of the page returned in the query
|
||||||
|
Page int32 `json:"page"`
|
||||||
|
|
||||||
|
// Number of elements on the page
|
||||||
|
PageSize int32 `json:"page_size"`
|
||||||
|
|
||||||
|
// Supply request identifier
|
||||||
|
SupplyOrderId int64 `json:"supply_order_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListProductsInSupplyRequestResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Indicates that the response contains not the entire array of supply requests:
|
||||||
|
// - true — make a new request with a different page and page_size values to get the remaining products;
|
||||||
|
// - false — the entire array of product was returned in the response
|
||||||
|
HasNext bool `json:"has_next"`
|
||||||
|
|
||||||
|
// Products list
|
||||||
|
Items []ListProductsInSupplyRequestItem `json:"items"`
|
||||||
|
|
||||||
|
// Total number of products in the request
|
||||||
|
TotalItemsCount int32 `json:"total_items_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListProductsInSupplyRequestItem struct {
|
||||||
|
// Link to product image
|
||||||
|
IconPath string `json:"icon_path"`
|
||||||
|
|
||||||
|
// Product name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Product ID
|
||||||
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
|
// Product quantity
|
||||||
|
Quantity int64 `json:"quantity"`
|
||||||
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of products in the sullpy request
|
||||||
|
func (c FBO) ListProductsInSupplyRequest(ctx context.Context, params *ListProductsInSupplyRequestParams) (*ListProductsInSupplyRequestResponse, error) {
|
||||||
|
url := "/v1/supply-order/items"
|
||||||
|
|
||||||
|
resp := &ListProductsInSupplyRequestResponse{}
|
||||||
|
|
||||||
|
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 GetWarehouseWorkloadResponse struct {
|
type GetWarehouseWorkloadResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
@@ -748,16 +771,16 @@ func (c FBO) GetPass(ctx context.Context, params *GetPassParams) (*GetPassRespon
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetSupplyContentParams struct {
|
type GetSupplyContentParams struct {
|
||||||
// Identifiers of supply contents. You can get them using the /v2/supply-order/get method.
|
// 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"`
|
BundleIds []string `json:"bundle_ids"`
|
||||||
|
|
||||||
// true, to sort in ascending order
|
// true, to sort in ascending order
|
||||||
IsAsc bool `json:"is_asc"`
|
IsAsc bool `json:"is_asc"`
|
||||||
|
|
||||||
// Identifier of the last SKU value on the page.
|
// Identifier of the last value on the page
|
||||||
LastId string `json:"last_id"`
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
// Number of products on the page.
|
// Number of values on the page. Minimum is 1, maximum is 1000
|
||||||
Limit int32 `json:"limit"`
|
Limit int32 `json:"limit"`
|
||||||
|
|
||||||
// Search query, for example: by name, article code, or SKU
|
// Search query, for example: by name, article code, or SKU
|
||||||
@@ -926,7 +949,7 @@ type SupplyDraftWarehouse struct {
|
|||||||
// Warehouse name
|
// Warehouse name
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Bundle of products that don't come in a shipment. Use the parameter in the /v1/supply-order/bundle method to get details.
|
// Bundle of products that don't come in a shipment
|
||||||
RestrictedBundleId string `json:"restricted_bundle_id"`
|
RestrictedBundleId string `json:"restricted_bundle_id"`
|
||||||
|
|
||||||
// Warehouse availability
|
// Warehouse availability
|
||||||
@@ -951,7 +974,7 @@ type SupplyDraftWarehouse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SupplyDraftWarehouseBundle struct {
|
type SupplyDraftWarehouseBundle struct {
|
||||||
// Bundle identifier. Use the parameter in the /v1/supply-order/bundle method to get details
|
// Bundle identifier
|
||||||
Id string `json:"bundle_id"`
|
Id string `json:"bundle_id"`
|
||||||
|
|
||||||
// Indicates that the UTD is to be passed
|
// Indicates that the UTD is to be passed
|
||||||
@@ -1115,82 +1138,3 @@ func (c FBO) GetDraftTimeslots(ctx context.Context, params *GetDraftTimeslotsPar
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CancelSuppyOrderParams struct {
|
|
||||||
// Supply request identifier
|
|
||||||
OrderId int64 `json:"order_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CancelSuppyOrderResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Operation identifier for canceling the request
|
|
||||||
OperationId string `json:"operation_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel supply request
|
|
||||||
func (c FBO) CancelSuppyOrder(ctx context.Context, params *CancelSuppyOrderParams) (*CancelSuppyOrderResponse, error) {
|
|
||||||
url := "/v1/supply-order/cancel"
|
|
||||||
|
|
||||||
resp := &CancelSuppyOrderResponse{}
|
|
||||||
|
|
||||||
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 StatusCancelledSupplyOrderParams struct {
|
|
||||||
// Operation identifier for canceling the supply request
|
|
||||||
OperationId string `json:"operation_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatusCancelledSupplyOrderResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Reason why the supply request can't be canceled
|
|
||||||
ErrorReasons []string `json:"error_reasons"`
|
|
||||||
|
|
||||||
// Details on supply request cancellation
|
|
||||||
Result StatusCancelledSupplyOrderResult `json:"result"`
|
|
||||||
|
|
||||||
// Cancellation status of the supply request
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatusCancelledSupplyOrderResult struct {
|
|
||||||
// true, if supply request is canceled
|
|
||||||
IsOrderCancelled bool `json:"is_order_cancelled"`
|
|
||||||
|
|
||||||
// List of canceled supplies
|
|
||||||
Supplies []StatusCancelledSupplyOrderSupply `json:"supplies"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatusCancelledSupplyOrderSupply struct {
|
|
||||||
// Reason why supplies can't be canceled
|
|
||||||
ErrorReasons []string `json:"error_reasons"`
|
|
||||||
|
|
||||||
// true, if supply is canceled
|
|
||||||
IsSupplyCancelled bool `json:"is_supply_cancelled"`
|
|
||||||
|
|
||||||
// Supply identifier
|
|
||||||
SupplyId int64 `json:"supply_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get status of canceled supply request
|
|
||||||
func (c FBO) StatusCancelledSupplyOrder(ctx context.Context, params *StatusCancelledSupplyOrderParams) (*StatusCancelledSupplyOrderResponse, error) {
|
|
||||||
url := "/v1/supply-order/cancel/status"
|
|
||||||
|
|
||||||
resp := &StatusCancelledSupplyOrderResponse{}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
195
ozon/fbo_test.go
195
ozon/fbo_test.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetFBOShipmentsList(t *testing.T) {
|
func TestGetFBOShipmentsList(t *testing.T) {
|
||||||
@@ -361,30 +361,16 @@ func TestGetSupplyRequestInfo(t *testing.T) {
|
|||||||
`{
|
`{
|
||||||
"orders": [
|
"orders": [
|
||||||
{
|
{
|
||||||
"can_cancel": true,
|
|
||||||
"creation_date": "string",
|
"creation_date": "string",
|
||||||
"creation_flow": "string",
|
"creation_flow": "string",
|
||||||
"data_filling_deadline_utc": "2019-08-24T14:15:22Z",
|
"data_filling_deadline_utc": "2019-08-24T14:15:22Z",
|
||||||
"dropoff_warehouse_id": 0,
|
"dropoff_warehouse_id": 0,
|
||||||
"is_econom": true,
|
|
||||||
"is_super_fbo": true,
|
|
||||||
"is_virtual": true,
|
|
||||||
"product_super_fbo": true,
|
|
||||||
"state": "ORDER_STATE_UNSPECIFIED",
|
"state": "ORDER_STATE_UNSPECIFIED",
|
||||||
"supplies": [
|
"supplies": [
|
||||||
{
|
{
|
||||||
"bundle_id": "string",
|
"bundle_id": "string",
|
||||||
"storage_warehouse_id": 0,
|
"storage_warehouse_id": 0,
|
||||||
"supply_id": 0,
|
"supply_id": 0
|
||||||
"supply_state": "SUPPLY_STATE_UNSPECIFIED",
|
|
||||||
"supply_tags": [
|
|
||||||
{
|
|
||||||
"is_evsd_required": true,
|
|
||||||
"is_jewelry": true,
|
|
||||||
"is_marking_possible": true,
|
|
||||||
"is_marking_required": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"supply_order_id": 0,
|
"supply_order_id": 0,
|
||||||
@@ -470,6 +456,68 @@ func TestGetSupplyRequestInfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListProductsInSupplyRequest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ListProductsInSupplyRequestParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ListProductsInSupplyRequestParams{
|
||||||
|
Page: 0,
|
||||||
|
PageSize: 0,
|
||||||
|
SupplyOrderId: 0,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"has_next": true,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"icon_path": "string",
|
||||||
|
"name": "string",
|
||||||
|
"offer_id": "string",
|
||||||
|
"quantity": 0,
|
||||||
|
"sku": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_items_count": 0
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ListProductsInSupplyRequestParams{},
|
||||||
|
`{
|
||||||
|
"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().ListProductsInSupplyRequest(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ListProductsInSupplyRequestResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetWarehouseWorkload(t *testing.T) {
|
func TestGetWarehouseWorkload(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1222,118 +1270,3 @@ func TestGetDraftTimeslots(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCancelSuppyOrder(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *CancelSuppyOrderParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&CancelSuppyOrderParams{
|
|
||||||
OrderId: 11,
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"operation_id": "string"
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&CancelSuppyOrderParams{},
|
|
||||||
`{
|
|
||||||
"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().CancelSuppyOrder(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &CancelSuppyOrderResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatusCancelledSupplyOrder(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *StatusCancelledSupplyOrderParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&StatusCancelledSupplyOrderParams{
|
|
||||||
OperationId: "123",
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"error_reasons": [
|
|
||||||
"INVALID_ORDER_STATE"
|
|
||||||
],
|
|
||||||
"result": {
|
|
||||||
"is_order_cancelled": true,
|
|
||||||
"supplies": [
|
|
||||||
{
|
|
||||||
"error_reasons": [
|
|
||||||
"INVALID_SUPPLY_STATE"
|
|
||||||
],
|
|
||||||
"is_supply_cancelled": true,
|
|
||||||
"supply_id": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"status": "SUCCESS"
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&StatusCancelledSupplyOrderParams{},
|
|
||||||
`{
|
|
||||||
"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().StatusCancelledSupplyOrder(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &StatusCancelledSupplyOrderResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
436
ozon/fbs.go
436
ozon/fbs.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FBS struct {
|
type FBS struct {
|
||||||
@@ -154,9 +154,6 @@ type FBSPosting struct {
|
|||||||
// Number of the parent shipment which split resulted in the current shipment
|
// Number of the parent shipment which split resulted in the current shipment
|
||||||
ParentPostingNumber string `json:"parent_posting_number"`
|
ParentPostingNumber string `json:"parent_posting_number"`
|
||||||
|
|
||||||
// Date and time of successful verification of the courier code
|
|
||||||
PickupCodeVerifiedAt time.Time `json:"pickup_code_verified_at"`
|
|
||||||
|
|
||||||
// Shipment number
|
// Shipment number
|
||||||
PostingNumber string `json:"posting_number"`
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
@@ -195,14 +192,6 @@ type FBSPosting struct {
|
|||||||
|
|
||||||
// Economy product identifier
|
// Economy product identifier
|
||||||
QuantumId int64 `json:"quantum_id"`
|
QuantumId int64 `json:"quantum_id"`
|
||||||
|
|
||||||
// List of products with additional characteristics
|
|
||||||
Optional FBSPostingOptional `json:"optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FBSPostingOptional struct {
|
|
||||||
// List of products with optional labeling
|
|
||||||
ProductsWithPossibleMandatoryMark []int `json:"products_with_possible_mandatory_mark"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FBSPostingTariffication struct {
|
type FBSPostingTariffication struct {
|
||||||
@@ -248,7 +237,7 @@ type FBSPostingAddressee struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FBSPostingAnalyticsData struct {
|
type FBSPostingAnalyticsData struct {
|
||||||
// Delivery city. Only for rFBS shipments and sellers from CIS countries
|
// Delivery city. Only for rFBS shipments
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
|
|
||||||
// Delivery start date and time
|
// Delivery start date and time
|
||||||
@@ -381,6 +370,9 @@ type FBSRequirements struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PostingProduct struct {
|
type PostingProduct struct {
|
||||||
|
// Mandatory product labeling
|
||||||
|
MandatoryMark []string `json:"mandatory_mark"`
|
||||||
|
|
||||||
// Product name
|
// Product name
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
@@ -398,9 +390,6 @@ type PostingProduct struct {
|
|||||||
|
|
||||||
// Product identifier in the Ozon system, SKU
|
// Product identifier in the Ozon system, SKU
|
||||||
SKU int64 `json:"sku"`
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
// Product traceability attribute
|
|
||||||
IsBLRTraceable bool `json:"is_blr_traceable"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FBSCustomer struct {
|
type FBSCustomer struct {
|
||||||
@@ -777,7 +766,7 @@ type ValidateLabelingCodesExemplar struct {
|
|||||||
GTD string `json:"gtd"`
|
GTD string `json:"gtd"`
|
||||||
|
|
||||||
// Mandatory “Chestny ZNAK” labeling
|
// Mandatory “Chestny ZNAK” labeling
|
||||||
Marks []SetProductItemsDataProductMark `json:"marks"`
|
MandatoryMark string `json:"mandatory_mark"`
|
||||||
|
|
||||||
// Product batch registration number
|
// Product batch registration number
|
||||||
RNPT string `json:"rnpt"`
|
RNPT string `json:"rnpt"`
|
||||||
@@ -786,6 +775,11 @@ type ValidateLabelingCodesExemplar struct {
|
|||||||
type ValidateLabelingCodesResponse struct {
|
type ValidateLabelingCodesResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result ValidateLabelingCodesResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValidateLabelingCodesResult struct {
|
||||||
// Products list
|
// Products list
|
||||||
Products []ValidateLabelingCodesResultProduct `json:"products"`
|
Products []ValidateLabelingCodesResultProduct `json:"products"`
|
||||||
}
|
}
|
||||||
@@ -795,7 +789,7 @@ type ValidateLabelingCodesResultProduct struct {
|
|||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
|
|
||||||
// Product items data
|
// Product items data
|
||||||
Exemplars []ValidateLabelingCodesResultExemplar `json:"exemplars"`
|
Exemplars []FBSProductExemplar `json:"exemplars"`
|
||||||
|
|
||||||
// Product identifier
|
// Product identifier
|
||||||
ProductId int64 `json:"product_id"`
|
ProductId int64 `json:"product_id"`
|
||||||
@@ -804,43 +798,11 @@ type ValidateLabelingCodesResultProduct struct {
|
|||||||
Valid bool `json:"valid"`
|
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.
|
// 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
|
// 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) {
|
func (c FBS) ValidateLabelingCodes(ctx context.Context, params *ValidateLabelingCodesParams) (*ValidateLabelingCodesResponse, error) {
|
||||||
url := "/v5/fbs/posting/product/exemplar/validate"
|
url := "/v4/fbs/posting/product/exemplar/validate"
|
||||||
|
|
||||||
resp := &ValidateLabelingCodesResponse{}
|
resp := &ValidateLabelingCodesResponse{}
|
||||||
|
|
||||||
@@ -1051,15 +1013,9 @@ type GetShipmentDataByIdentifierResult struct {
|
|||||||
// Number of the parent shipment which split resulted in the current shipment
|
// Number of the parent shipment which split resulted in the current shipment
|
||||||
ParentPostingNumber string `json:"parent_posting_number"`
|
ParentPostingNumber string `json:"parent_posting_number"`
|
||||||
|
|
||||||
// List of products with additional characteristics
|
|
||||||
Optional GetShipmentDataByIdentifierOptional `json:"optional"`
|
|
||||||
|
|
||||||
// Shipment number
|
// Shipment number
|
||||||
PostingNumber string `json:"posting_number"`
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
// Date and time of successful verification of the courier code
|
|
||||||
PickupCodeVerifiedAt time.Time `json:"pickup_code_verified_at"`
|
|
||||||
|
|
||||||
// Information on products and their instances.
|
// Information on products and their instances.
|
||||||
//
|
//
|
||||||
// The response contains the field product_exemplars, if the attribute with.product_exemplars = true is passed in the request
|
// The response contains the field product_exemplars, if the attribute with.product_exemplars = true is passed in the request
|
||||||
@@ -1071,9 +1027,6 @@ type GetShipmentDataByIdentifierResult struct {
|
|||||||
// Delivery service status
|
// Delivery service status
|
||||||
ProviderStatus string `json:"provider_status"`
|
ProviderStatus string `json:"provider_status"`
|
||||||
|
|
||||||
// Previous sub-status of the shipment
|
|
||||||
PreviousSubstatus string `json:"previous_substatus"`
|
|
||||||
|
|
||||||
// Information on lifting service. Only relevant for bulky products
|
// Information on lifting service. Only relevant for bulky products
|
||||||
// with a delivery by a third-party or integrated service
|
// with a delivery by a third-party or integrated service
|
||||||
PRROption GetShipmentDataByIdentifierResultPRROption `json:"prr_option"`
|
PRROption GetShipmentDataByIdentifierResultPRROption `json:"prr_option"`
|
||||||
@@ -1105,11 +1058,6 @@ type GetShipmentDataByIdentifierResult struct {
|
|||||||
Tariffication []FBSPostingTariffication `json:"tariffication"`
|
Tariffication []FBSPostingTariffication `json:"tariffication"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetShipmentDataByIdentifierOptional struct {
|
|
||||||
// List of products with optional labeling
|
|
||||||
ProductsWithPossibleMandatoryMark []int `json:"products_with_possible_mandatory_mark"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetShipmentDataByIdentifierResultAdditionalData struct {
|
type GetShipmentDataByIdentifierResultAdditionalData struct {
|
||||||
// Key
|
// Key
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
@@ -1129,7 +1077,7 @@ type GetShipmentDataByIdentifierResultAddressee struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
||||||
// Delivery city. Only for rFBS shipments and sellers from CIS countries
|
// Delivery city. Only for rFBS shipments
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
|
|
||||||
// Delivery start date and time
|
// Delivery start date and time
|
||||||
@@ -1237,23 +1185,39 @@ type ProductDimension struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FBSProductExemplar struct {
|
type FBSProductExemplar struct {
|
||||||
// Item identifier
|
// Product item validation errors
|
||||||
ExemplarId int64 `json:"exemplar_id"`
|
Errors []string `json:"errors"`
|
||||||
|
|
||||||
// Mandatory “Chestny ZNAK” labeling
|
// Mandatory “Chestny ZNAK” labeling
|
||||||
MandatoryMark string `json:"mandatory_mark"`
|
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
|
// Сustoms cargo declaration (CCD) number
|
||||||
GTD string `json:"gtd"`
|
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
|
// Indication that a сustoms cargo declaration (CCD) number hasn't been specified
|
||||||
IsGTDAbsest bool `json:"is_gtd_absent"`
|
IsGTDAbsest bool `json:"is_gtd_absent"`
|
||||||
|
|
||||||
|
// Сustoms cargo declaration (CCD) check error codes
|
||||||
|
GTDErrorCodes []string `json:"gtd_error_codes"`
|
||||||
|
|
||||||
// Product batch registration number
|
// Product batch registration number
|
||||||
RNPT string `json:"rnpt"`
|
RNPT string `json:"rnpt"`
|
||||||
|
|
||||||
// Indication that a product batch registration number hasn't been specified
|
// Indication that a product batch registration number hasn't been specified
|
||||||
IsRNPTAbsent bool `json:"is_rnpt_absent"`
|
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
|
// Method for getting shipment details by identifier
|
||||||
@@ -1441,6 +1405,38 @@ func (c FBS) ListOfShipmentCertificates(ctx context.Context, params *ListOfShipm
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SignShipmentCertificateParams struct {
|
||||||
|
// Certificate identifier
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
|
// Type of shipment certificate:
|
||||||
|
// - act_of_mismatch — discrepancy certificate,
|
||||||
|
// - act_of_excess — surplus certificate
|
||||||
|
DocType string `json:"doc_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignShipmentCertificateResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Request processing
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signs shipment certificates electronically via the electronic documents (ED) system of Ozon logistics
|
||||||
|
func (c FBS) SignShipmentCertificate(ctx context.Context, params *SignShipmentCertificateParams) (*SignShipmentCertificateResponse, error) {
|
||||||
|
url := "/v2/posting/fbs/digital/act/document-sign"
|
||||||
|
|
||||||
|
resp := &SignShipmentCertificateResponse{}
|
||||||
|
|
||||||
|
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 ChangeStatusToParams struct {
|
type ChangeStatusToParams struct {
|
||||||
// Shipment identifier
|
// Shipment identifier
|
||||||
PostingNumber []string `json:"posting_number"`
|
PostingNumber []string `json:"posting_number"`
|
||||||
@@ -1525,7 +1521,7 @@ func (c FBS) ChangeStatusToSendBySeller(ctx context.Context, params *ChangeStatu
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PassShipmentToShippingParams struct {
|
type PassShipmentToShippingParams struct {
|
||||||
// Shipment identifier. The maximum number of values in one request is 100
|
// Shipment identifier
|
||||||
PostingNumber []string `json:"posting_number"`
|
PostingNumber []string `json:"posting_number"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1830,7 +1826,7 @@ func (c FBS) GetDropOffPointRestrictions(ctx context.Context, params *GetDropOff
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetProductItemsDataParams struct {
|
type CheckProductItemsDataParams struct {
|
||||||
// Quantity of boxes the product is packed in
|
// Quantity of boxes the product is packed in
|
||||||
MultiBoxQuantity int32 `json:"multi_box_qty"`
|
MultiBoxQuantity int32 `json:"multi_box_qty"`
|
||||||
|
|
||||||
@@ -1838,26 +1834,20 @@ type SetProductItemsDataParams struct {
|
|||||||
PostingNumber string `json:"posting_number"`
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
// Product list
|
// Product list
|
||||||
Products []SetProductItemsDataProduct `json:"products"`
|
Products []CheckProductItemsDataProduct `json:"products"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetProductItemsDataProduct struct {
|
type CheckProductItemsDataProduct struct {
|
||||||
// Product items data
|
// Product items data
|
||||||
Exemplars []SetProductItemsDataProductExemplar `json:"exemplars"`
|
Exemplars []CheckProductItemsDataProductExemplar `json:"exemplars"`
|
||||||
|
|
||||||
// Indication that you need to pass the сustoms cargo declaration
|
// Indication that you need to pass the сustoms cargo declaration
|
||||||
// (CCD) number for the product and shipment
|
// (CCD) number for the product and shipment
|
||||||
IsGTDNeeded bool `json:"is_gtd_needed"`
|
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
|
// Indication that you need to pass the "Chestny ZNAK" labeling
|
||||||
IsMandatoryMarkNeeded bool `json:"is_mandatory_mark_needed"`
|
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
|
// Indication that you need to pass the product batch registration number
|
||||||
IsRNPTNeeded bool `json:"is_rnpt_needed"`
|
IsRNPTNeeded bool `json:"is_rnpt_needed"`
|
||||||
|
|
||||||
@@ -1868,35 +1858,48 @@ type SetProductItemsDataProduct struct {
|
|||||||
Quantity int32 `json:"quantity"`
|
Quantity int32 `json:"quantity"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetProductItemsDataProductExemplar struct {
|
type CheckProductItemsDataProductExemplar struct {
|
||||||
// Item identifier
|
// Item identifier
|
||||||
ExemplarId int64 `json:"exemplar_id"`
|
ExemplarId int64 `json:"exemplar_id"`
|
||||||
|
|
||||||
// Customs cargo declaration (CCD) number
|
// Customs cargo declaration (CCD) number
|
||||||
GTD string `json:"gtd"`
|
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
|
// Indication that the customs cargo declaration (CCD) number isn't specified
|
||||||
IsGTDAbsent bool `json:"is_gtd_absent"`
|
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
|
// Indication that the product batch registration number isn't specified
|
||||||
IsRNPTAbsent bool `json:"is_rnpt_absent"`
|
IsRNPTAbsent bool `json:"is_rnpt_absent"`
|
||||||
|
|
||||||
|
// Mandatory "Chestny ZNAK" labeling
|
||||||
|
MandatoryMark string `json:"mandatory_mark"`
|
||||||
|
|
||||||
// Product batch registration number
|
// Product batch registration number
|
||||||
RNPT string `json:"rnpt"`
|
RNPT string `json:"rnpt"`
|
||||||
|
|
||||||
// Errors that appeared during verification of Control Identification Marks
|
// Product batch registration number check status
|
||||||
Marks []SetProductItemsDataProductMark `json:"marks"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetProductItemsDataProductMark struct {
|
type CheckProductItemsDataResponse struct {
|
||||||
// Labeling code meaning
|
|
||||||
Mark string `json:"mark"`
|
|
||||||
|
|
||||||
// Labeling code type
|
|
||||||
MarkType string `json:"mark_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SetProductItemsDataResponse struct {
|
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
// Method result. true if the request was processed successfully
|
// Method result. true if the request was processed successfully
|
||||||
@@ -1907,19 +1910,37 @@ type SetProductItemsDataResponse struct {
|
|||||||
//
|
//
|
||||||
// for checking the availability of product items in the “Chestny ZNAK” labeling system;
|
// for checking the availability of product items in the “Chestny ZNAK” labeling system;
|
||||||
// for saving product items data.
|
// 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.
|
|
||||||
//
|
//
|
||||||
// If you have multiple identical products in a shipment, specify one product_id and exemplars array for each product in the shipment.
|
// 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.
|
||||||
//
|
//
|
||||||
// Always pass a complete set of product items data.
|
// Always pass a complete set of product items data.
|
||||||
//
|
//
|
||||||
// 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.
|
// 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.
|
||||||
//
|
//
|
||||||
// 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.
|
// Unlike /v4/fbs/posting/product/exemplar/set,
|
||||||
func (c FBS) SetProductItemsData(ctx context.Context, params *SetProductItemsDataParams) (*SetProductItemsDataResponse, error) {
|
// you can pass more item information in the request.
|
||||||
url := "/v6/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 /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"
|
||||||
|
|
||||||
resp := &SetProductItemsDataResponse{}
|
resp := &CheckProductItemsDataResponse{}
|
||||||
|
|
||||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1942,71 +1963,19 @@ type GetProductItemsCheckStatusesResponse struct {
|
|||||||
PostingNumber string `json:"posting_number"`
|
PostingNumber string `json:"posting_number"`
|
||||||
|
|
||||||
// Products list
|
// Products list
|
||||||
Products []GetProductItemsCheckStatusProduct `json:"products"`
|
Products []CheckProductItemsDataProduct `json:"products"`
|
||||||
|
|
||||||
// Product items check statuses and order collection availability
|
// 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
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProductItemsCheckStatusProduct struct {
|
// Method for getting check statuses of product items that were passed in the `/fbs/posting/product/exemplar/set` method.
|
||||||
// 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.
|
// Also returns data on these product items.
|
||||||
func (c FBS) GetProductItemsCheckStatuses(ctx context.Context, params *GetProductItemsCheckStatusesParams) (*GetProductItemsCheckStatusesResponse, error) {
|
func (c FBS) GetProductItemsCheckStatuses(ctx context.Context, params *GetProductItemsCheckStatusesParams) (*GetProductItemsCheckStatusesResponse, error) {
|
||||||
url := "/v5/fbs/posting/product/exemplar/status"
|
url := "/v4/fbs/posting/product/exemplar/status"
|
||||||
|
|
||||||
resp := &GetProductItemsCheckStatusesResponse{}
|
resp := &GetProductItemsCheckStatusesResponse{}
|
||||||
|
|
||||||
@@ -2992,37 +2961,11 @@ type CreateOrGetProductExemplarResponse struct {
|
|||||||
Products []CheckProductItemsDataProduct `json:"products"`
|
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.
|
// Method returns the created items data passed in the `/v5/fbs/posting/product/exemplar/set` method.
|
||||||
//
|
//
|
||||||
// Use this method to get the `exemplar_id`
|
// Use this method to get the `exemplar_id`
|
||||||
func (c FBS) CreateOrGetProductExemplar(ctx context.Context, params *CreateOrGetProductExemplarParams) (*CreateOrGetProductExemplarResponse, error) {
|
func (c FBS) CreateOrGetProductExemplar(ctx context.Context, params *CreateOrGetProductExemplarParams) (*CreateOrGetProductExemplarResponse, error) {
|
||||||
url := "/v6/fbs/posting/product/exemplar/create-or-get"
|
url := "/v5/fbs/posting/product/exemplar/create-or-get"
|
||||||
|
|
||||||
resp := &CreateOrGetProductExemplarResponse{}
|
resp := &CreateOrGetProductExemplarResponse{}
|
||||||
|
|
||||||
@@ -3291,128 +3234,3 @@ func (c FBS) ListUnpaidProducts(ctx context.Context, params *ListUnpaidProductsP
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChangeShipmentCompositionParams struct {
|
|
||||||
// Freight identifier
|
|
||||||
CarriageId int64 `json:"carriage_id"`
|
|
||||||
|
|
||||||
// Current list of shipments
|
|
||||||
PostingNumbers []string `json:"posting_numbers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChangeShipmentCompositionResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
Result []ChangeShipmentCompositionResult `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChangeShipmentCompositionResult struct {
|
|
||||||
// Error message
|
|
||||||
Error string `json:"error"`
|
|
||||||
|
|
||||||
// Shipment number
|
|
||||||
PostingNumber string `json:"posting_number"`
|
|
||||||
|
|
||||||
// Request processing result. true if the request was executed successfully
|
|
||||||
Result bool `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method overwrites the list of orders in the shipment. Pass only orders in the awaiting_deliver status and ones which are ready for shipping.
|
|
||||||
func (c FBS) ChangeShipmentComposition(ctx context.Context, params *ChangeShipmentCompositionParams) (*ChangeShipmentCompositionResponse, error) {
|
|
||||||
url := "/v1/carriage/set-postings"
|
|
||||||
|
|
||||||
resp := &ChangeShipmentCompositionResponse{}
|
|
||||||
|
|
||||||
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 DeleteShipmentParams struct {
|
|
||||||
// Freight identifier
|
|
||||||
CarriageId int64 `json:"carriage_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteShipmentResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Error message
|
|
||||||
Error string `json:"error"`
|
|
||||||
|
|
||||||
// Carriage status
|
|
||||||
Status string `json:"carriage_status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c FBS) DeleteShipment(ctx context.Context, params *DeleteShipmentParams) (*DeleteShipmentResponse, error) {
|
|
||||||
url := "/v1/carriage/cancel"
|
|
||||||
|
|
||||||
resp := &DeleteShipmentResponse{}
|
|
||||||
|
|
||||||
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 VerifyCourierCodeParams struct {
|
|
||||||
// Courier code
|
|
||||||
PickupCode string `json:"pickup_code"`
|
|
||||||
|
|
||||||
// Shipment number
|
|
||||||
PostingNumber string `json:"posting_number"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerifyCourierCodeResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// true, if the code is correct
|
|
||||||
Valid bool `json:"valid"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use this method to verify the courier code when handing over realFBS Express shipments
|
|
||||||
func (c FBS) VerifyCourierCode(ctx context.Context, params *VerifyCourierCodeParams) (*VerifyCourierCodeResponse, error) {
|
|
||||||
url := "/v1/posting/fbs/pick-up-code/verify"
|
|
||||||
|
|
||||||
resp := &VerifyCourierCodeResponse{}
|
|
||||||
|
|
||||||
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 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
|
|
||||||
}
|
|
||||||
|
|||||||
428
ozon/fbs_test.go
428
ozon/fbs_test.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListUnprocessedShipments(t *testing.T) {
|
func TestListUnprocessedShipments(t *testing.T) {
|
||||||
@@ -43,7 +43,6 @@ func TestListUnprocessedShipments(t *testing.T) {
|
|||||||
"posting_number": "23713478-0018-3",
|
"posting_number": "23713478-0018-3",
|
||||||
"order_id": 559293114,
|
"order_id": 559293114,
|
||||||
"order_number": "33713378-0051",
|
"order_number": "33713378-0051",
|
||||||
"pickup_code_verified_at": "2025-01-17T11:03:00.124Z",
|
|
||||||
"status": "awaiting_packaging",
|
"status": "awaiting_packaging",
|
||||||
"delivery_method": {
|
"delivery_method": {
|
||||||
"id": 15110442724000,
|
"id": 15110442724000,
|
||||||
@@ -58,11 +57,6 @@ func TestListUnprocessedShipments(t *testing.T) {
|
|||||||
"in_process_at": "2021-08-25T10:48:38Z",
|
"in_process_at": "2021-08-25T10:48:38Z",
|
||||||
"shipment_date": "2021-08-26T10:00:00Z",
|
"shipment_date": "2021-08-26T10:00:00Z",
|
||||||
"delivering_date": null,
|
"delivering_date": null,
|
||||||
"optional": {
|
|
||||||
"products_with_possible_mandatory_mark": [
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cancellation": {
|
"cancellation": {
|
||||||
"cancel_reason_id": 0,
|
"cancel_reason_id": 0,
|
||||||
"cancel_reason": "",
|
"cancel_reason": "",
|
||||||
@@ -76,12 +70,12 @@ func TestListUnprocessedShipments(t *testing.T) {
|
|||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
"currency_code": "RUB",
|
"currency_code": "RUB",
|
||||||
"is_blr_traceable": true,
|
|
||||||
"price": "1259",
|
"price": "1259",
|
||||||
"offer_id": "УТ-0001365",
|
"offer_id": "УТ-0001365",
|
||||||
"name": "Мяч, цвет: черный, 5 кг",
|
"name": "Мяч, цвет: черный, 5 кг",
|
||||||
"sku": 140048123,
|
"sku": 140048123,
|
||||||
"quantity": 1
|
"quantity": 1,
|
||||||
|
"mandatory_mark": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"addressee": null,
|
"addressee": null,
|
||||||
@@ -244,7 +238,6 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
|||||||
"posting_number": "05708065-0029-1",
|
"posting_number": "05708065-0029-1",
|
||||||
"order_id": 680420041,
|
"order_id": 680420041,
|
||||||
"order_number": "05708065-0029",
|
"order_number": "05708065-0029",
|
||||||
"pickup_code_verified_at": "2025-01-17T10:59:26.614Z",
|
|
||||||
"status": "awaiting_deliver",
|
"status": "awaiting_deliver",
|
||||||
"substatus": "posting_awaiting_passport_data",
|
"substatus": "posting_awaiting_passport_data",
|
||||||
"delivery_method": {
|
"delivery_method": {
|
||||||
@@ -260,11 +253,6 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
|||||||
"in_process_at": "2022-05-13T07:07:32Z",
|
"in_process_at": "2022-05-13T07:07:32Z",
|
||||||
"shipment_date": "2022-05-13T10:00:00Z",
|
"shipment_date": "2022-05-13T10:00:00Z",
|
||||||
"delivering_date": null,
|
"delivering_date": null,
|
||||||
"optional": {
|
|
||||||
"products_with_possible_mandatory_mark": [
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cancellation": {
|
"cancellation": {
|
||||||
"cancel_reason_id": 0,
|
"cancel_reason_id": 0,
|
||||||
"cancel_reason": "",
|
"cancel_reason": "",
|
||||||
@@ -277,12 +265,12 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
|||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
"currency_code": "RUB",
|
"currency_code": "RUB",
|
||||||
"is_blr_traceable": true,
|
|
||||||
"price": "1390.000000",
|
"price": "1390.000000",
|
||||||
"offer_id": "205953",
|
"offer_id": "205953",
|
||||||
"name": " Электронный конструктор PinLab Позитроник",
|
"name": " Электронный конструктор PinLab Позитроник",
|
||||||
"sku": 358924380,
|
"sku": 358924380,
|
||||||
"quantity": 1
|
"quantity": 1,
|
||||||
|
"mandatory_mark": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"addressee": null,
|
"addressee": null,
|
||||||
@@ -434,6 +422,7 @@ func TestValidateLabelingCodes(t *testing.T) {
|
|||||||
Exemplars: []ValidateLabelingCodesExemplar{
|
Exemplars: []ValidateLabelingCodesExemplar{
|
||||||
{
|
{
|
||||||
GTD: "",
|
GTD: "",
|
||||||
|
MandatoryMark: "010290000151642731tVMohkbfFgunB",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProductId: 476925391,
|
ProductId: 476925391,
|
||||||
@@ -441,33 +430,23 @@ func TestValidateLabelingCodes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
|
"result": {
|
||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
"error": "string",
|
"product_id": 476925391,
|
||||||
"exemplars": [
|
"exemplars": [
|
||||||
{
|
{
|
||||||
"errors": [
|
"mandatory_mark": "010290000151642731tVMohkbfFgunB",
|
||||||
"string"
|
"gtd": "",
|
||||||
],
|
"valid": true,
|
||||||
"gtd": "string",
|
"errors": []
|
||||||
"marks": [
|
|
||||||
{
|
|
||||||
"errors": [
|
|
||||||
"string"
|
|
||||||
],
|
|
||||||
"mark": "string",
|
|
||||||
"mark_type": "string",
|
|
||||||
"valid": true
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rnpt": "string",
|
"valid": true,
|
||||||
"valid": true
|
"error": ""
|
||||||
}
|
|
||||||
],
|
|
||||||
"product_id": 476925391,
|
|
||||||
"valid": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
@@ -499,11 +478,11 @@ func TestValidateLabelingCodes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
if resp.StatusCode == http.StatusOK {
|
||||||
if len(resp.Products) != len(test.params.Products) {
|
if len(resp.Result.Products) != len(test.params.Products) {
|
||||||
t.Errorf("Length of products in request and response are not equal")
|
t.Errorf("Length of products in request and response are not equal")
|
||||||
}
|
}
|
||||||
if len(resp.Products) > 0 {
|
if len(resp.Result.Products) > 0 {
|
||||||
if resp.Products[0].ProductId != test.params.Products[0].ProductId {
|
if resp.Result.Products[0].ProductId != test.params.Products[0].ProductId {
|
||||||
t.Errorf("Product ids in request and response are not equal")
|
t.Errorf("Product ids in request and response are not equal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -541,7 +520,8 @@ func TestGetShipmentDataByBarcode(t *testing.T) {
|
|||||||
"offer_id": "250-7898-1",
|
"offer_id": "250-7898-1",
|
||||||
"name": "Кофе ароматизированный \"Шоколадный апельсин\" 250 гр",
|
"name": "Кофе ароматизированный \"Шоколадный апельсин\" 250 гр",
|
||||||
"sku": 180550365,
|
"sku": 180550365,
|
||||||
"quantity": 1
|
"quantity": 1,
|
||||||
|
"mandatory_mark": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"barcodes": null,
|
"barcodes": null,
|
||||||
@@ -611,10 +591,8 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
|||||||
"posting_number": "57195475-0050-3",
|
"posting_number": "57195475-0050-3",
|
||||||
"order_id": 438764970,
|
"order_id": 438764970,
|
||||||
"order_number": "57195475-0050",
|
"order_number": "57195475-0050",
|
||||||
"pickup_code_verified_at": "2025-01-17T11:04:59.958Z",
|
|
||||||
"status": "awaiting_packaging",
|
"status": "awaiting_packaging",
|
||||||
"substatus": "posting_awaiting_passport_data",
|
"substatus": "posting_awaiting_passport_data",
|
||||||
"previous_substatus": "posting_transferring_to_delivery",
|
|
||||||
"delivery_method": {
|
"delivery_method": {
|
||||||
"id": 18114520187000,
|
"id": 18114520187000,
|
||||||
"name": "Ozon Логистика самостоятельно, Москва",
|
"name": "Ozon Логистика самостоятельно, Москва",
|
||||||
@@ -630,11 +608,6 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
|||||||
"delivering_date": null,
|
"delivering_date": null,
|
||||||
"provider_status": "",
|
"provider_status": "",
|
||||||
"delivery_price": "",
|
"delivery_price": "",
|
||||||
"optional": {
|
|
||||||
"products_with_possible_mandatory_mark": [
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cancellation": {
|
"cancellation": {
|
||||||
"cancel_reason_id": 0,
|
"cancel_reason_id": 0,
|
||||||
"cancel_reason": "",
|
"cancel_reason": "",
|
||||||
@@ -648,12 +621,12 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
|||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
"currency_code": "RUB",
|
"currency_code": "RUB",
|
||||||
"is_blr_traceable": true,
|
|
||||||
"price": "279.0000",
|
"price": "279.0000",
|
||||||
"offer_id": "250-7898-1",
|
"offer_id": "250-7898-1",
|
||||||
"name": "Кофе ароматизированный \"Шоколадный апельсин\" 250 гр",
|
"name": "Кофе ароматизированный \"Шоколадный апельсин\" 250 гр",
|
||||||
"sku": 180550365,
|
"sku": 180550365,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
|
"mandatory_mark": [],
|
||||||
"dimensions": {
|
"dimensions": {
|
||||||
"height": "40.00",
|
"height": "40.00",
|
||||||
"length": "240.00",
|
"length": "240.00",
|
||||||
@@ -905,6 +878,62 @@ func TestListOfShipmentCertificates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSignShipmentCertificate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *SignShipmentCertificateParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&SignShipmentCertificateParams{
|
||||||
|
Id: 900000250859000,
|
||||||
|
DocType: "act_of_mismatch",
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": "string"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&SignShipmentCertificateParams{},
|
||||||
|
`{
|
||||||
|
"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().SignShipmentCertificate(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &SignShipmentCertificateResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
if resp.Result == "" {
|
||||||
|
t.Errorf("Result cannot be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestChangeStatusTo(t *testing.T) {
|
func TestChangeStatusTo(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1403,52 +1432,52 @@ func TestGetDropOffPointRestrictions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetProductItemsData(t *testing.T) {
|
func TestCheckProductItemsData(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
params *SetProductItemsDataParams
|
params *CheckProductItemsDataParams
|
||||||
response string
|
response string
|
||||||
}{
|
}{
|
||||||
// Test Ok
|
// Test Ok
|
||||||
{
|
{
|
||||||
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"},
|
||||||
&SetProductItemsDataParams{
|
&CheckProductItemsDataParams{
|
||||||
MultiBoxQuantity: 0,
|
MultiBoxQuantity: 0,
|
||||||
PostingNumber: "1234",
|
PostingNumber: "1234",
|
||||||
Products: []SetProductItemsDataProduct{
|
Products: []CheckProductItemsDataProduct{
|
||||||
{
|
{
|
||||||
Exemplars: []SetProductItemsDataProductExemplar{
|
Exemplars: []CheckProductItemsDataProductExemplar{
|
||||||
{
|
{
|
||||||
ExemplarId: 1,
|
ExemplarId: 1,
|
||||||
GTD: "string",
|
GTD: "string",
|
||||||
IsGTDAbsent: true,
|
IsGTDAbsent: true,
|
||||||
|
IsRNPTAbsent: true,
|
||||||
|
MandatoryMark: "string",
|
||||||
RNPT: "string",
|
RNPT: "string",
|
||||||
|
JWUIN: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
IsGTDNeeded: true,
|
||||||
|
IsMandatoryMarkNeeded: true,
|
||||||
|
IsRNPTNeeded: true,
|
||||||
ProductId: 22,
|
ProductId: 22,
|
||||||
|
Quantity: 11,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
"code": 0,
|
"result": true
|
||||||
"details": [
|
|
||||||
{
|
|
||||||
"typeUrl": "string",
|
|
||||||
"value": "string"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"message": "string"
|
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
{
|
{
|
||||||
http.StatusUnauthorized,
|
http.StatusUnauthorized,
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
&SetProductItemsDataParams{},
|
&CheckProductItemsDataParams{},
|
||||||
`{
|
`{
|
||||||
"code": 16,
|
"code": 16,
|
||||||
"message": "Client-Id and Api-Key headers are required"
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
@@ -1460,13 +1489,13 @@ func TestSetProductItemsData(t *testing.T) {
|
|||||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||||
resp, err := c.FBS().SetProductItemsData(ctx, test.params)
|
resp, err := c.FBS().CheckProductItemsData(ctx, test.params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &SetProductItemsDataResponse{})
|
compareJsonResponse(t, test.response, &CheckProductItemsDataResponse{})
|
||||||
|
|
||||||
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)
|
||||||
@@ -1494,37 +1523,21 @@ func TestGetProductItemsCheckStatuses(t *testing.T) {
|
|||||||
"posting_number": "23281294-0063-2",
|
"posting_number": "23281294-0063-2",
|
||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
|
"product_id": 476925391,
|
||||||
"exemplars": [
|
"exemplars": [
|
||||||
{
|
{
|
||||||
"exemplar_id": 0,
|
"mandatory_mark": "010290000151642731tVMohkbfFgunB",
|
||||||
"gtd": "string",
|
"gtd": "",
|
||||||
"gtd_check_status": "string",
|
|
||||||
"gtd_error_codes": [
|
|
||||||
"string"
|
|
||||||
],
|
|
||||||
"is_gtd_absent": true,
|
"is_gtd_absent": true,
|
||||||
"is_rnpt_absent": true,
|
"mandatory_mark_check_status": "passed",
|
||||||
"marks": [
|
"mandatory_mark_error_codes": [],
|
||||||
{
|
"gtd_check_status": "passed",
|
||||||
"check_status": "string",
|
"gtd_error_codes": []
|
||||||
"error_codes": [
|
|
||||||
"string"
|
|
||||||
],
|
|
||||||
"mark": "string",
|
|
||||||
"mark_type": "string"
|
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"rnpt": "string",
|
|
||||||
"rnpt_check_status": "string",
|
|
||||||
"rnpt_error_codes": [
|
|
||||||
"string"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"product_id": 123
|
"status": "ship_available"
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "string"
|
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
@@ -1565,8 +1578,8 @@ func TestGetProductItemsCheckStatuses(t *testing.T) {
|
|||||||
if resp.Products[0].ProductId == 0 {
|
if resp.Products[0].ProductId == 0 {
|
||||||
t.Errorf("Product id cannot be 0")
|
t.Errorf("Product id cannot be 0")
|
||||||
}
|
}
|
||||||
if len(resp.Products[0].Exemplars) > 0 && len(resp.Products[0].Exemplars[0].Marks) > 0 {
|
if len(resp.Products[0].Exemplars) > 0 {
|
||||||
if resp.Products[0].Exemplars[0].Marks[0].Mark == "" {
|
if resp.Products[0].Exemplars[0].MandatoryMark == "" {
|
||||||
t.Errorf("Mandatory mark cannot be empty")
|
t.Errorf("Mandatory mark cannot be empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2916,19 +2929,13 @@ func TestCreateOrGetProductExemplar(t *testing.T) {
|
|||||||
"gtd": "string",
|
"gtd": "string",
|
||||||
"is_gtd_absent": true,
|
"is_gtd_absent": true,
|
||||||
"is_rnpt_absent": true,
|
"is_rnpt_absent": true,
|
||||||
"marks": [
|
"mandatory_mark": "string",
|
||||||
{
|
"rnpt": "string",
|
||||||
"mark": "string",
|
"jw_uin": "string"
|
||||||
"mark_type": "string"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"rnpt": "string"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_gtd_needed": true,
|
"is_gtd_needed": true,
|
||||||
"is_jw_uin_needed": true,
|
|
||||||
"is_mandatory_mark_needed": true,
|
"is_mandatory_mark_needed": true,
|
||||||
"is_mandatory_mark_possible": true,
|
|
||||||
"is_rnpt_needed": true,
|
"is_rnpt_needed": true,
|
||||||
"product_id": 0,
|
"product_id": 0,
|
||||||
"quantity": 0
|
"quantity": 0
|
||||||
@@ -3305,226 +3312,3 @@ func TestListUnpaidProducts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChangeShipmentComposition(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *ChangeShipmentCompositionParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&ChangeShipmentCompositionParams{
|
|
||||||
CarriageId: 10,
|
|
||||||
PostingNumbers: []string{"something"},
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"result": [
|
|
||||||
{
|
|
||||||
"error": "string",
|
|
||||||
"posting_number": "something",
|
|
||||||
"result": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&ChangeShipmentCompositionParams{},
|
|
||||||
`{
|
|
||||||
"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().ChangeShipmentComposition(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &ChangeShipmentCompositionResponse{})
|
|
||||||
|
|
||||||
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) > 0 {
|
|
||||||
if resp.Result[0].PostingNumber != test.params.PostingNumbers[0] {
|
|
||||||
t.Errorf("posting numbers are different. Expected: %s, got: %s", test.params.PostingNumbers[0], resp.Result[0].PostingNumber)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteShipment(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *DeleteShipmentParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&DeleteShipmentParams{
|
|
||||||
CarriageId: 10,
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"error": "string",
|
|
||||||
"carriage_status": "string"
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&DeleteShipmentParams{},
|
|
||||||
`{
|
|
||||||
"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().DeleteShipment(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &DeleteShipmentResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyCourierCode(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *VerifyCourierCodeParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&VerifyCourierCodeParams{
|
|
||||||
PickupCode: "string",
|
|
||||||
PostingNumber: "string",
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"valid": true
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&VerifyCourierCodeParams{},
|
|
||||||
`{
|
|
||||||
"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().VerifyCourierCode(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &VerifyCourierCodeResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Finance struct {
|
type Finance struct {
|
||||||
@@ -152,7 +152,7 @@ type ReturnCommission struct {
|
|||||||
//
|
//
|
||||||
// Report is returned no later than the 5th day of the next month
|
// Report is returned no later than the 5th day of the next month
|
||||||
func (c Finance) ReportOnSoldProducts(ctx context.Context, params *ReportOnSoldProductsParams) (*ReportOnSoldProductsResponse, error) {
|
func (c Finance) ReportOnSoldProducts(ctx context.Context, params *ReportOnSoldProductsParams) (*ReportOnSoldProductsResponse, error) {
|
||||||
url := "/v2/finance/realization"
|
url := "/v1/finance/realization"
|
||||||
|
|
||||||
resp := &ReportOnSoldProductsResponse{}
|
resp := &ReportOnSoldProductsResponse{}
|
||||||
|
|
||||||
@@ -305,10 +305,10 @@ type ListTransactionsResult struct {
|
|||||||
// Transactions infromation
|
// Transactions infromation
|
||||||
Operations []ListTransactionsResultOperation `json:"operations"`
|
Operations []ListTransactionsResultOperation `json:"operations"`
|
||||||
|
|
||||||
// Number of pages. If 0, there are no more pages
|
// Number of pages
|
||||||
PageCount int64 `json:"page_count"`
|
PageCount int64 `json:"page_count"`
|
||||||
|
|
||||||
// Number of transactions on all pages. If 0, there are no more transactions
|
// Number of products
|
||||||
RowCount int64 `json:"row_count"`
|
RowCount int64 `json:"row_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +362,11 @@ type ListTransactionsResultOperationItem struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListTransactionsResultOperationPosting struct {
|
type ListTransactionsResultOperationPosting struct {
|
||||||
// Delivery scheme
|
// Delivery scheme:
|
||||||
|
// - FBO — delivery to Ozon warehouse
|
||||||
|
// - FBS — delivery from seller's warehouse
|
||||||
|
// - RFBS — delivery service of seller's choice
|
||||||
|
// - Crossborder — delivery from abroad
|
||||||
DeliverySchema string `json:"delivery_schema"`
|
DeliverySchema string `json:"delivery_schema"`
|
||||||
|
|
||||||
// Date the product was accepted for processing
|
// Date the product was accepted for processing
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReportOnSoldProducts(t *testing.T) {
|
func TestReportOnSoldProducts(t *testing.T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Invoices struct {
|
type Invoices struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateUpdateProformaLink(t *testing.T) {
|
func TestCreateUpdateProformaLink(t *testing.T) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testData struct {
|
type testData struct {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package ozon
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -44,7 +44,6 @@ type Client struct {
|
|||||||
passes *Passes
|
passes *Passes
|
||||||
clusters *Clusters
|
clusters *Clusters
|
||||||
quants *Quants
|
quants *Quants
|
||||||
reviews *Reviews
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) Analytics() *Analytics {
|
func (c Client) Analytics() *Analytics {
|
||||||
@@ -135,10 +134,6 @@ func (c Client) Quants() *Quants {
|
|||||||
return c.quants
|
return c.quants
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) Reviews() *Reviews {
|
|
||||||
return c.reviews
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientOption func(c *ClientOptions)
|
type ClientOption func(c *ClientOptions)
|
||||||
|
|
||||||
func WithHttpClient(httpClient core.HttpClient) ClientOption {
|
func WithHttpClient(httpClient core.HttpClient) ClientOption {
|
||||||
@@ -205,7 +200,6 @@ func NewClient(opts ...ClientOption) *Client {
|
|||||||
passes: &Passes{client: coreClient},
|
passes: &Passes{client: coreClient},
|
||||||
clusters: &Clusters{client: coreClient},
|
clusters: &Clusters{client: coreClient},
|
||||||
quants: &Quants{client: coreClient},
|
quants: &Quants{client: coreClient},
|
||||||
reviews: &Reviews{client: coreClient},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +230,5 @@ func NewMockClient(handler http.HandlerFunc) *Client {
|
|||||||
passes: &Passes{client: coreClient},
|
passes: &Passes{client: coreClient},
|
||||||
clusters: &Clusters{client: coreClient},
|
clusters: &Clusters{client: coreClient},
|
||||||
quants: &Quants{client: coreClient},
|
quants: &Quants{client: coreClient},
|
||||||
reviews: &Reviews{client: coreClient},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Passes struct {
|
type Passes struct {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListPasses(t *testing.T) {
|
func TestListPasses(t *testing.T) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Polygons struct {
|
type Polygons struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateDeliveryPolygon(t *testing.T) {
|
func TestCreateDeliveryPolygon(t *testing.T) {
|
||||||
|
|||||||
627
ozon/products.go
627
ozon/products.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Products struct {
|
type Products struct {
|
||||||
@@ -13,11 +13,13 @@ type Products struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GetStocksInfoParams struct {
|
type GetStocksInfoParams struct {
|
||||||
// Cursor for the next data sample
|
// Identifier of the last value on the page. Leave this field blank in the first request.
|
||||||
Cursor string `json:"cursor"`
|
//
|
||||||
|
// To get the next values, specify last_id from the response of the previous request.
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
// Limit on number of entries in a reply. Default value is 1000. Maximum value is 1000
|
// Number of values per page. Minimum is 1, maximum is 1000
|
||||||
Limit int32 `json:"limit"`
|
Limit int64 `json:"limit"`
|
||||||
|
|
||||||
// Filter by product
|
// Filter by product
|
||||||
Filter GetStocksInfoFilter `json:"filter"`
|
Filter GetStocksInfoFilter `json:"filter"`
|
||||||
@@ -32,24 +34,20 @@ type GetStocksInfoFilter struct {
|
|||||||
|
|
||||||
// Filter by product visibility
|
// Filter by product visibility
|
||||||
Visibility string `json:"visibility,omitempty"`
|
Visibility string `json:"visibility,omitempty"`
|
||||||
|
|
||||||
// Products at the “Economy” tariff
|
|
||||||
WithQuant GetStocksInfoFilterWithQuant `json:"with_quant"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetStocksInfoFilterWithQuant struct {
|
|
||||||
// Active economy products
|
|
||||||
Created bool `json:"created"`
|
|
||||||
|
|
||||||
// Economy products in all statuses
|
|
||||||
Exists bool `json:"exists"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetStocksInfoResponse struct {
|
type GetStocksInfoResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
// Cursor for the next data sample
|
// Method Result
|
||||||
Cursor string `json:"cursor"`
|
Result GetStocksInfoResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetStocksInfoResult struct {
|
||||||
|
// Identifier of the last value on the page
|
||||||
|
//
|
||||||
|
// To get the next values, specify the recieved value in the next request in the last_id parameter
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
// The number of unique products for which information about stocks is displayed
|
// The number of unique products for which information about stocks is displayed
|
||||||
Total int32 `json:"total"`
|
Total int32 `json:"total"`
|
||||||
@@ -78,12 +76,6 @@ type GetStocksInfoResultItemStock struct {
|
|||||||
|
|
||||||
// Warehouse type
|
// Warehouse type
|
||||||
Type string `json:"type" default:"ALL"`
|
Type string `json:"type" default:"ALL"`
|
||||||
|
|
||||||
// Packaging type
|
|
||||||
ShipmentType string `json:"shipment_type"`
|
|
||||||
|
|
||||||
// Product identifier in the Ozon system, SKU
|
|
||||||
SKU int64 `json:"sku"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns information about the quantity of products in stock:
|
// Returns information about the quantity of products in stock:
|
||||||
@@ -92,7 +84,7 @@ type GetStocksInfoResultItemStock struct {
|
|||||||
//
|
//
|
||||||
// * how many are reserved by customers.
|
// * how many are reserved by customers.
|
||||||
func (c Products) GetStocksInfo(ctx context.Context, params *GetStocksInfoParams) (*GetStocksInfoResponse, error) {
|
func (c Products) GetStocksInfo(ctx context.Context, params *GetStocksInfoParams) (*GetStocksInfoResponse, error) {
|
||||||
url := "/v4/product/info/stocks"
|
url := "/v3/product/info/stocks"
|
||||||
|
|
||||||
resp := &GetStocksInfoResponse{}
|
resp := &GetStocksInfoResponse{}
|
||||||
|
|
||||||
@@ -105,122 +97,175 @@ func (c Products) GetStocksInfo(ctx context.Context, params *GetStocksInfoParams
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetProductDetailsParams struct {
|
||||||
|
// Product identifier in the seller's system
|
||||||
|
OfferId string `json:"offer_id,omitempty"`
|
||||||
|
|
||||||
|
// Product identifier
|
||||||
|
ProductId int64 `json:"product_id,omitempty"`
|
||||||
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
|
SKU int64 `json:"sku,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductDetailsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Request results
|
||||||
|
Result ProductDetails `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
type ProductDetails struct {
|
type ProductDetails struct {
|
||||||
|
// Barcode
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
|
||||||
|
// All product barcodes
|
||||||
Barcodes []string `json:"barcodes"`
|
Barcodes []string `json:"barcodes"`
|
||||||
ColorImage []string `json:"color_image"`
|
|
||||||
Commissions []ProductDetailCommission `json:"commissions"`
|
// Main offer price on Ozon.
|
||||||
CreatedAt time.Time `json:"created_at"`
|
//
|
||||||
CurrencyCode string `json:"currency_code"`
|
// The field is deprecated. Returns an empty string ""
|
||||||
|
BuyboxPrice string `json:"buybox_price"`
|
||||||
|
|
||||||
|
// Category identifier
|
||||||
DescriptionCategoryId int64 `json:"description_category_id"`
|
DescriptionCategoryId int64 `json:"description_category_id"`
|
||||||
DiscountedFBOStocks int32 `json:"discounted_fbo_stocks"`
|
|
||||||
Errors []ProductDetailsError `json:"errors"`
|
// Product type identifier
|
||||||
HasDiscountedFBOItem bool `json:"has_discounted_fbo_item"`
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
Images []string `json:"images"`
|
|
||||||
Images360 []string `json:"images360"`
|
|
||||||
IsArchived bool `json:"is_archived"`
|
|
||||||
IsAutoarchived bool `json:"is_autoarchived"`
|
|
||||||
IsDiscounted bool `json:"is_discounted"`
|
|
||||||
IsKGT bool `json:"is_kgt"`
|
|
||||||
IsPrepaymentAllowed bool `json:"is_prepayment_allowed"`
|
|
||||||
IsSuper bool `json:"is_super"`
|
|
||||||
MarketingPrice string `json:"marketing_price"`
|
|
||||||
MinPrice string `json:"min_price"`
|
|
||||||
ModelInfo *ProductDetailsModelInfo `json:"model_info"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
OfferId string `json:"offer_id"`
|
|
||||||
OldPrice string `json:"old_price"`
|
|
||||||
Price string `json:"price"`
|
|
||||||
PriceIndexes ProductDetailPriceIndex `json:"price_indexes"`
|
|
||||||
PrimaryImage []string `json:"primary_image"`
|
|
||||||
Sources []ProductDetailSource `json:"sources"`
|
|
||||||
Statuses ProductDetailsStatus `json:"statuses"`
|
|
||||||
Stocks ProductDetailStock `json:"stocks"`
|
|
||||||
TypeId int64 `json:"type_id"`
|
TypeId int64 `json:"type_id"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
VAT string `json:"vat"`
|
|
||||||
VisibilityDetails ProductDetailVisibilityDetails `json:"visibility_details"`
|
|
||||||
VolumeWeight float64 `json:"volume_weight"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProductDetailsError struct {
|
// Marketing color
|
||||||
// Characteristic identifier
|
ColorImage string `json:"color_image"`
|
||||||
AttributeId int64 `json:"attribute_id"`
|
|
||||||
|
|
||||||
// Error code
|
// Commission fees details
|
||||||
Code string `json:"code"`
|
Commissions []ProductDetailCommission `json:"commissions"`
|
||||||
|
|
||||||
// Field in which the error occurred
|
// Date and time when the product was created
|
||||||
Field string `json:"field"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
||||||
// Error level description
|
// Product SKU
|
||||||
Level string `json:"level"`
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
// Status of the product with the error
|
// SKU of the product that is sold from the Ozon warehouse (FBO)
|
||||||
State string `json:"state"`
|
FBOSKU int64 `json:"fbo_sku,omitempty"`
|
||||||
|
|
||||||
// Error description
|
// SKU of the product that is sold from the seller's warehouse (FBS and rFBS)
|
||||||
Texts ProductDetailsErrorText `json:"texts"`
|
FBSSKU int64 `json:"fbs_sku,omitempty"`
|
||||||
}
|
|
||||||
|
|
||||||
type ProductDetailsErrorText struct {
|
// Product identifier
|
||||||
// Attribute name
|
Id int64 `json:"id"`
|
||||||
AttributeName string `json:"attribute_name"`
|
|
||||||
|
|
||||||
// Error description
|
// 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
|
||||||
Description string `json:"description"`
|
Images []string `json:"images"`
|
||||||
|
|
||||||
// Error code in the Ozon system
|
// Main product image
|
||||||
HintCode string `json:"hint_code"`
|
PrimaryImage string `json:"primary_image"`
|
||||||
|
|
||||||
// Error message
|
// Array of 360 images
|
||||||
Message string `json:"message"`
|
Images360 []string `json:"images360"`
|
||||||
|
|
||||||
// Short description of the error
|
// true if the product has markdown equivalents at the Ozon warehouse
|
||||||
ShortDescription string `json:"short_description"`
|
HasDiscountedItem bool `json:"has_discounted_item"`
|
||||||
|
|
||||||
// Parameters in which the error occurred
|
// Indication of a markdown product:
|
||||||
Params []NameValue `json:"params"`
|
//
|
||||||
}
|
// * 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"`
|
||||||
|
|
||||||
type NameValue struct {
|
// 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"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
Value string `json:"value"`
|
// Product identifier in the seller's system
|
||||||
}
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
type ProductDetailsStatus struct {
|
// Price before discounts. Displayed strikethrough on the product description page
|
||||||
IsCreated bool `json:"is_created"`
|
OldPrice string `json:"old_price"`
|
||||||
ModerateStatus string `json:"moderate_status"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
StatusDescription string `json:"status_description"`
|
|
||||||
StatusFailed string `json:"status_failed"`
|
|
||||||
StatusName string `json:"status_name"`
|
|
||||||
StatusTooltip string `json:"status_tooltip"`
|
|
||||||
StatusUpdatedAt time.Time `json:"status_updated_at"`
|
|
||||||
ValidationStatus string `json:"validation_status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProductDetailsModelInfo struct {
|
// Product price including discounts. This value is shown on the product description page
|
||||||
// Number of products in the response
|
Price string `json:"price"`
|
||||||
Count int64 `json:"count"`
|
|
||||||
|
|
||||||
// Identifier of the product model
|
// Product price indexes
|
||||||
ModelId int64 `json:"model_id"`
|
PriceIndexes ProductDetailPriceIndex `json:"price_indexes"`
|
||||||
|
|
||||||
|
// Deprecated: Price index. Learn more in Help Center
|
||||||
|
//
|
||||||
|
// Use PriceIndexes instead
|
||||||
|
PriceIndex string `json:"price_index"`
|
||||||
|
|
||||||
|
// Product state description
|
||||||
|
Status ProductDetailStatus `json:"status"`
|
||||||
|
|
||||||
|
// Details about the sources of similar offers. Learn more in Help Сenter
|
||||||
|
Sources []ProductDetailSource `json:"sources"`
|
||||||
|
|
||||||
|
// Details about product stocks
|
||||||
|
Stocks ProductDetailStock `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 ProductDetailVisibilityDetails `json:"visibility_details"`
|
||||||
|
|
||||||
|
// If the product is on sale, the value is true
|
||||||
|
Visible bool `json:"visible"`
|
||||||
|
|
||||||
|
// Product volume weight
|
||||||
|
VolumeWeight float64 `json:"volume_weight"`
|
||||||
|
|
||||||
|
// 'true' if the item is archived manually.
|
||||||
|
IsArchived bool `json:"is_archived"`
|
||||||
|
|
||||||
|
// 'true' if the item is archived automatically.
|
||||||
|
IsArchivedAuto bool `json:"is_autoarchived"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductDetailCommission struct {
|
type ProductDetailCommission struct {
|
||||||
// Delivery cost
|
// Delivery cost
|
||||||
DeliveryAmount float64 `json:"delivery_amount"`
|
DeliveryAmount float64 `json:"deliveryAmount"`
|
||||||
|
|
||||||
// Commission percentage
|
// Commission percentage
|
||||||
Percent float64 `json:"percent"`
|
Percent float64 `json:"percent"`
|
||||||
|
|
||||||
// Return cost
|
// Return cost
|
||||||
ReturnAmount float64 `json:"return_amount"`
|
ReturnAmount float64 `json:"returnAmount"`
|
||||||
|
|
||||||
// Sale scheme
|
// Sale scheme
|
||||||
SaleSchema string `json:"sale_schema"`
|
SaleSchema string `json:"saleSchema"`
|
||||||
|
|
||||||
// Commission fee amount
|
// Commission fee amount
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
@@ -233,8 +278,8 @@ type ProductDetailPriceIndex struct {
|
|||||||
// Competitors' product price on Ozon
|
// Competitors' product price on Ozon
|
||||||
OzonIndexData ProductDetailPriceIndexOzon `json:"ozon_index_data"`
|
OzonIndexData ProductDetailPriceIndexOzon `json:"ozon_index_data"`
|
||||||
|
|
||||||
// Types of price index
|
// Resulting price index of the product
|
||||||
ColorIndex string `json:"color_index"`
|
PriceIndex string `json:"price_index"`
|
||||||
|
|
||||||
// Price of your product on other marketplaces
|
// Price of your product on other marketplaces
|
||||||
SelfMarketplaceIndexData ProductDetailPriceIndexSelfMarketplace `json:"self_marketplaces_index_data"`
|
SelfMarketplaceIndexData ProductDetailPriceIndexSelfMarketplace `json:"self_marketplaces_index_data"`
|
||||||
@@ -312,28 +357,41 @@ type ProductDetailStatus struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProductDetailSource struct {
|
type ProductDetailSource struct {
|
||||||
CreatedAt time.Time `json:"created_at"`
|
// Indication that the source is taken into account when calculating the market value
|
||||||
QuantCode string `json:"quant_code"`
|
IsEnabled bool `json:"is_enabled"`
|
||||||
ShipmentType string `json:"shipment_type"`
|
|
||||||
|
// Product identifier in the Ozon system, SKU
|
||||||
SKU int64 `json:"sku"`
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
|
// Link to the source
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductDetailStock struct {
|
type ProductDetailStock struct {
|
||||||
HasStock bool `json:"has_stock"`
|
// Supply expected
|
||||||
Stocks []ProductDetailStockStock `json:"stocks"`
|
Coming int32 `json:"coming"`
|
||||||
}
|
|
||||||
|
|
||||||
type ProductDetailStockStock struct {
|
// Currently at the warehouse
|
||||||
Present int32 `json:"present"`
|
Present int32 `json:"present"`
|
||||||
|
|
||||||
|
// Reserved
|
||||||
Reserved int32 `json:"reserved"`
|
Reserved int32 `json:"reserved"`
|
||||||
SKU int64 `json:"sku"`
|
|
||||||
Source string `json:"source"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductDetailVisibilityDetails struct {
|
type ProductDetailVisibilityDetails struct {
|
||||||
|
// If the product is active, the value is true
|
||||||
|
//
|
||||||
|
// Deprecated: Use `visible` parameter of `ProductDetails`
|
||||||
|
ActiveProduct bool `json:"active_product"`
|
||||||
|
|
||||||
|
// If the price is set, the value is true
|
||||||
HasPrice bool `json:"has_price"`
|
HasPrice bool `json:"has_price"`
|
||||||
|
|
||||||
|
// If there is stock at the warehouses, the value is true
|
||||||
HasStock bool `json:"has_stock"`
|
HasStock bool `json:"has_stock"`
|
||||||
|
|
||||||
|
// Reason why the product is hidden
|
||||||
|
Reasons map[string]interface{} `json:"reasons"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductDiscountedStocks struct {
|
type ProductDiscountedStocks struct {
|
||||||
@@ -372,6 +430,24 @@ type GetProductDetailsResponseItemError struct {
|
|||||||
OptionalDescriptionElements map[string]string `json:"optional_description_elements"`
|
OptionalDescriptionElements map[string]string `json:"optional_description_elements"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get product details
|
||||||
|
//
|
||||||
|
// Check a minimum product price with all promotions applied in your personal account.
|
||||||
|
// The min_price parameter from the method response is in development and returns 0
|
||||||
|
func (c Products) GetProductDetails(ctx context.Context, params *GetProductDetailsParams) (*GetProductDetailsResponse, error) {
|
||||||
|
url := "/v2/product/info"
|
||||||
|
|
||||||
|
resp := &GetProductDetailsResponse{}
|
||||||
|
|
||||||
|
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 UpdateStocksParams struct {
|
type UpdateStocksParams struct {
|
||||||
// Stock details
|
// Stock details
|
||||||
Stocks []UpdateStocksStock `json:"stocks"`
|
Stocks []UpdateStocksStock `json:"stocks"`
|
||||||
@@ -586,18 +662,9 @@ type UpdatePricesPrice struct {
|
|||||||
// By default, the passed value is RUB, Russian ruble
|
// By default, the passed value is RUB, Russian ruble
|
||||||
CurrencyCode string `json:"currency_code"`
|
CurrencyCode string `json:"currency_code"`
|
||||||
|
|
||||||
// true, if Ozon takes into account
|
|
||||||
// the minimum price when creating promotions.
|
|
||||||
// If you don't pass anything,
|
|
||||||
// the status of the price accounting remains the same
|
|
||||||
MinPriceForAutoActionsEnabled bool `json:"min_price_for_auto_actions_enabled"`
|
|
||||||
|
|
||||||
// Minimum product price with all promotions applied
|
// Minimum product price with all promotions applied
|
||||||
MinPrice string `json:"min_price"`
|
MinPrice string `json:"min_price"`
|
||||||
|
|
||||||
// Product cost price
|
|
||||||
NetPrice string `json:"net_price"`
|
|
||||||
|
|
||||||
// Product identifier in the seller's system
|
// Product identifier in the seller's system
|
||||||
OfferId string `json:"offer_id"`
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
@@ -641,9 +708,6 @@ type UpdatePricesPrice struct {
|
|||||||
//
|
//
|
||||||
// If the regular and economy products have different article codes, don't specify the parameter.
|
// If the regular and economy products have different article codes, don't specify the parameter.
|
||||||
QuantSize int64 `json:"quant_size"`
|
QuantSize int64 `json:"quant_size"`
|
||||||
|
|
||||||
// VAT rate for the product
|
|
||||||
VAT VAT `json:"vat"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePricesResponse struct {
|
type UpdatePricesResponse struct {
|
||||||
@@ -706,7 +770,7 @@ type CreateOrUpdateProductItem struct {
|
|||||||
DescriptionCategoryId int64 `json:"description_category_id"`
|
DescriptionCategoryId int64 `json:"description_category_id"`
|
||||||
|
|
||||||
// New category identifier. Specify it if you want to change the current product category
|
// New category identifier. Specify it if you want to change the current product category
|
||||||
NewDescriptionCategoryId int64 `json:"new_description_category_id"`
|
NewDescriptinoCategoryId int64 `json:"new_description_category_id"`
|
||||||
|
|
||||||
// Marketing color.
|
// Marketing color.
|
||||||
//
|
//
|
||||||
@@ -786,8 +850,11 @@ type CreateOrUpdateProductItem struct {
|
|||||||
// you can leave out the attributes attribute if it has the `id:8229` parameter
|
// you can leave out the attributes attribute if it has the `id:8229` parameter
|
||||||
TypeId int64 `json:"type_id"`
|
TypeId int64 `json:"type_id"`
|
||||||
|
|
||||||
// VAT rate for the product
|
// VAT rate for the product:
|
||||||
VAT VAT `json:"vat"`
|
// - 0 — not subject to VAT,
|
||||||
|
// - 0.1 — 10%,
|
||||||
|
// - 0.2 — 20%
|
||||||
|
VAT string `json:"vat"`
|
||||||
|
|
||||||
// Product weight with the package. The limit value is 1000 kilograms or a corresponding converted value in other measurement units
|
// Product weight with the package. The limit value is 1000 kilograms or a corresponding converted value in other measurement units
|
||||||
Weight int32 `json:"weight"`
|
Weight int32 `json:"weight"`
|
||||||
@@ -836,7 +903,7 @@ type CreateOrUpdateProductPDF struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// File address
|
// File address
|
||||||
SrcUrl string `json:"src_url"`
|
URL string `json:"url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateOrUpdateProductResponse struct {
|
type CreateOrUpdateProductResponse struct {
|
||||||
@@ -867,14 +934,12 @@ func (c Products) CreateOrUpdateProduct(ctx context.Context, params *CreateOrUpd
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListOfProductsParams reflects the new /v3/product/list request body.
|
|
||||||
// Filter, LastId, and Limit are used the same way as in the v2 version, plus we keep
|
|
||||||
// offer_id and product_id for backward compatibility if needed.
|
|
||||||
type GetListOfProductsParams struct {
|
type GetListOfProductsParams struct {
|
||||||
// Filter by product
|
// Filter by product
|
||||||
Filter GetListOfProductsFilter `json:"filter"`
|
Filter GetListOfProductsFilter `json:"filter"`
|
||||||
|
|
||||||
// Identifier of the last value on the page. Leave this field blank in the first request.
|
// Identifier of the last value on the page. Leave this field blank in the first request.
|
||||||
|
//
|
||||||
// To get the next values, specify last_id from the response of the previous request
|
// To get the next values, specify last_id from the response of the previous request
|
||||||
LastId string `json:"last_id"`
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
@@ -882,82 +947,51 @@ type GetListOfProductsParams struct {
|
|||||||
Limit int64 `json:"limit"`
|
Limit int64 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListOfProductsFilter holds filtering options for /v3/product/list.
|
|
||||||
type GetListOfProductsFilter struct {
|
type GetListOfProductsFilter struct {
|
||||||
// Filter by the offer_id parameter. You can pass a list of values in this parameter
|
// Filter by the offer_id parameter. You can pass a list of values in this parameter
|
||||||
OfferId []string `json:"offer_id,omitempty"`
|
OfferId []string `json:"offer_id"`
|
||||||
|
|
||||||
// Filter by the product_id parameter. You can pass a list of values in this parameter
|
// Filter by the product_id parameter. You can pass a list of values in this parameter
|
||||||
ProductId []int64 `json:"product_id,omitempty"`
|
ProductId []int64 `json:"product_id"`
|
||||||
|
|
||||||
// Filter by product visibility
|
// Filter by product visibility
|
||||||
Visibility string `json:"visibility,omitempty"`
|
Visibility string `json:"visibility"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListOfProductsResponse describes the /v3/product/list response body.
|
|
||||||
type GetListOfProductsResponse struct {
|
type GetListOfProductsResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
// Result object containing list of products and pagination info
|
// Result
|
||||||
Result GetListOfProductsResult `json:"result"`
|
Result GetListOfProductsResult `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListOfProductsResult contains the products, total count, and last_id.
|
|
||||||
type GetListOfProductsResult struct {
|
type GetListOfProductsResult struct {
|
||||||
// Products list
|
// Products list
|
||||||
Items []GetListOfProductsResultItem `json:"items"`
|
Items []GetListOfProductsResultItem `json:"items"`
|
||||||
|
|
||||||
|
// Identifier of the last value on the page.
|
||||||
|
//
|
||||||
|
// To get the next values, specify the recieved value in the next request in the last_id parameter
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
// Total number of products
|
// Total number of products
|
||||||
Total int32 `json:"total"`
|
Total int32 `json:"total"`
|
||||||
|
|
||||||
// Identifier of the last value on the page.
|
|
||||||
// To get the next values, specify the received value in the next request in the last_id parameter
|
|
||||||
LastId string `json:"last_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListOfProductsResultItem describes a single product item in the /v3/product/list response.
|
|
||||||
type GetListOfProductsResultItem struct {
|
type GetListOfProductsResultItem struct {
|
||||||
// Product ID
|
|
||||||
ProductId int64 `json:"product_id"`
|
|
||||||
|
|
||||||
// Product identifier in the seller's system
|
// Product identifier in the seller's system
|
||||||
OfferId string `json:"offer_id"`
|
OfferId string `json:"offer_id"`
|
||||||
|
|
||||||
// Flag indicating presence of FBO stocks
|
// Product ID
|
||||||
HasFboStocks bool `json:"has_fbo_stocks"`
|
ProductId int64 `json:"product_id"`
|
||||||
|
|
||||||
// Flag indicating presence of FBS stocks
|
|
||||||
HasFbsStocks bool `json:"has_fbs_stocks"`
|
|
||||||
|
|
||||||
// Product archive status
|
|
||||||
Archived bool `json:"archived"`
|
|
||||||
|
|
||||||
// Whether the product has an active discount
|
|
||||||
IsDiscounted bool `json:"is_discounted"`
|
|
||||||
|
|
||||||
// List of quants with detailed stock information
|
|
||||||
Quants []ProductQuant `json:"quants"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProductQuant describes a single quant entry with warehouse, available quantity, and reserved.
|
|
||||||
type ProductQuant struct {
|
|
||||||
// Warehouse ID where the stock is located
|
|
||||||
WarehouseId int64 `json:"warehouse_id"`
|
|
||||||
|
|
||||||
// Quantity available in the warehouse
|
|
||||||
Quantity int64 `json:"quantity"`
|
|
||||||
|
|
||||||
// Quantity reserved in the warehouse
|
|
||||||
Reserved int64 `json:"reserved"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetListOfProducts calls the new /v3/product/list endpoint.
|
|
||||||
// When using the filter by offer_id or product_id identifier, other parameters are not required.
|
// 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.
|
// 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.
|
// 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 := "/v3/product/list"
|
url := "/v2/product/list"
|
||||||
|
|
||||||
resp := &GetListOfProductsResponse{}
|
resp := &GetListOfProductsResponse{}
|
||||||
|
|
||||||
@@ -1145,8 +1179,11 @@ type CreateProductsByOzonIDItem struct {
|
|||||||
// Product identifier in the Ozon system, SKU
|
// Product identifier in the Ozon system, SKU
|
||||||
SKU int64 `json:"sku"`
|
SKU int64 `json:"sku"`
|
||||||
|
|
||||||
// VAT rate for the product
|
// VAT rate for the product:
|
||||||
VAT VAT `json:"vat"`
|
// - 0 — not subject to VAT,
|
||||||
|
// - 0.1 — 10%,
|
||||||
|
// - 0.2 — 20%
|
||||||
|
VAT string `json:"vat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateProductByOzonIDResponse struct {
|
type CreateProductByOzonIDResponse struct {
|
||||||
@@ -1227,9 +1264,9 @@ type ProductInfoResultPicture struct {
|
|||||||
// Image uploading status.
|
// Image uploading status.
|
||||||
//
|
//
|
||||||
// If the `/v1/product/pictures/import` method was called, the response will always be imported—image not processed.
|
// 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 `/v2/product/pictures/info` method after about 10 seconds.
|
// To see the final status, call the `/v1/product/pictures/info` method after about 10 seconds.
|
||||||
//
|
//
|
||||||
// If you called the `/v2/product/pictures/info` method, one of the statuses will appear:
|
// If you called the `/v1/product/pictures/info` method, one of the statuses will appear:
|
||||||
// - uploaded — image uploaded;
|
// - uploaded — image uploaded;
|
||||||
// - pending — image was not uploaded
|
// - pending — image was not uploaded
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
@@ -1276,35 +1313,11 @@ type CheckImageUploadingStatusParams struct {
|
|||||||
ProductId []int64 `json:"product_id"`
|
ProductId []int64 `json:"product_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckImageUploadingStatusResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Product images
|
|
||||||
Items []CheckImageUploadingStatusItem `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CheckImageUploadingStatusItem struct {
|
|
||||||
// Product identifier
|
|
||||||
ProductId int64 `json:"product_id"`
|
|
||||||
|
|
||||||
// Main image link
|
|
||||||
PrimaryPhoto []string `json:"primary_photo"`
|
|
||||||
|
|
||||||
// Links to product photos
|
|
||||||
Photo []string `json:"photo"`
|
|
||||||
|
|
||||||
// Links to uploaded color samples
|
|
||||||
ColorPhoto []string `json:"color_photo"`
|
|
||||||
|
|
||||||
// 360 images links
|
|
||||||
Photo360 []string `json:"photo_360"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check products images uploading status
|
// Check products images uploading status
|
||||||
func (c Products) CheckImageUploadingStatus(ctx context.Context, params *CheckImageUploadingStatusParams) (*CheckImageUploadingStatusResponse, error) {
|
func (c Products) CheckImageUploadingStatus(ctx context.Context, params *CheckImageUploadingStatusParams) (*ProductInfoResponse, error) {
|
||||||
url := "/v2/product/pictures/info"
|
url := "/v1/product/pictures/info"
|
||||||
|
|
||||||
resp := &CheckImageUploadingStatusResponse{}
|
resp := &ProductInfoResponse{}
|
||||||
|
|
||||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1329,6 +1342,11 @@ type ListProductsByIDsParams struct {
|
|||||||
type ListProductsByIDsResponse struct {
|
type ListProductsByIDsResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Request results
|
||||||
|
Result ListProductsByIDsResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListProductsByIDsResult struct {
|
||||||
// Data array
|
// Data array
|
||||||
Items []ProductDetails `json:"items"`
|
Items []ProductDetails `json:"items"`
|
||||||
}
|
}
|
||||||
@@ -1339,7 +1357,7 @@ type ListProductsByIDsResponse struct {
|
|||||||
//
|
//
|
||||||
// For each shipment in the items array the fields match the ones recieved in the /v2/product/info method
|
// For each shipment in the items array the fields match the ones recieved in the /v2/product/info method
|
||||||
func (c Products) ListProductsByIDs(ctx context.Context, params *ListProductsByIDsParams) (*ListProductsByIDsResponse, error) {
|
func (c Products) ListProductsByIDs(ctx context.Context, params *ListProductsByIDsParams) (*ListProductsByIDsResponse, error) {
|
||||||
url := "/v3/product/info/list"
|
url := "/v2/product/info/list"
|
||||||
|
|
||||||
resp := &ListProductsByIDsResponse{}
|
resp := &ListProductsByIDsResponse{}
|
||||||
|
|
||||||
@@ -1404,9 +1422,6 @@ type GetDescriptionOfProductResult struct {
|
|||||||
// Barcode
|
// Barcode
|
||||||
Barcode string `json:"barcode"`
|
Barcode string `json:"barcode"`
|
||||||
|
|
||||||
// All product's barcodes
|
|
||||||
Barcodes []string `json:"barcodes"`
|
|
||||||
|
|
||||||
// Category identifier
|
// Category identifier
|
||||||
DescriptionCategoryId int64 `json:"description_category_id"`
|
DescriptionCategoryId int64 `json:"description_category_id"`
|
||||||
|
|
||||||
@@ -1432,10 +1447,7 @@ type GetDescriptionOfProductResult struct {
|
|||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
|
|
||||||
// Array of links to product images
|
// Array of links to product images
|
||||||
Images []string `json:"images"`
|
Images []GetDescriptionOfProductResultImage `json:"images"`
|
||||||
|
|
||||||
// Model Information
|
|
||||||
ModelInfo GetDescriptionOfProductModelInfo `json:"model_info"`
|
|
||||||
|
|
||||||
// Array of 360 images
|
// Array of 360 images
|
||||||
Images360 []GetDescriptionOfProductResultImage360 `json:"images360"`
|
Images360 []GetDescriptionOfProductResultImage360 `json:"images360"`
|
||||||
@@ -1449,12 +1461,6 @@ type GetDescriptionOfProductResult struct {
|
|||||||
// Array of PDF files
|
// Array of PDF files
|
||||||
PDFList []GetDescriptionOfProductResultPDF `json:"pdf_list"`
|
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
|
// Product type identifier
|
||||||
TypeId int64 `json:"type_id"`
|
TypeId int64 `json:"type_id"`
|
||||||
|
|
||||||
@@ -1468,17 +1474,9 @@ type GetDescriptionOfProductResult struct {
|
|||||||
Width int32 `json:"width"`
|
Width int32 `json:"width"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetDescriptionOfProductModelInfo struct {
|
|
||||||
// Model Identifier
|
|
||||||
ModelId int64 `json:"model_id"`
|
|
||||||
|
|
||||||
// Quantity of combined model products
|
|
||||||
Count int64 `json:"count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetDescriptionOfProductResultAttr struct {
|
type GetDescriptionOfProductResultAttr struct {
|
||||||
// Characteristic identifier
|
// Characteristic identifier
|
||||||
AttributeId int64 `json:"id"`
|
AttributeId int64 `json:"attribute_id"`
|
||||||
|
|
||||||
// Identifier of the characteristic that supports nested properties.
|
// Identifier of the characteristic that supports nested properties.
|
||||||
// For example, the "Processor" characteristic has nested characteristics "Manufacturer" and "L2 Cache".
|
// For example, the "Processor" characteristic has nested characteristics "Manufacturer" and "L2 Cache".
|
||||||
@@ -1523,6 +1521,12 @@ type GetDescriptionOfProductResultComplexAttrValue struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetDescriptionOfProductResultImage struct {
|
||||||
|
Default bool `json:"default"`
|
||||||
|
FileName string `json:"file_name"`
|
||||||
|
Index int64 `json:"index"`
|
||||||
|
}
|
||||||
|
|
||||||
type GetDescriptionOfProductResultImage360 struct {
|
type GetDescriptionOfProductResultImage360 struct {
|
||||||
FileName string `json:"file_name"`
|
FileName string `json:"file_name"`
|
||||||
Index int64 `json:"index"`
|
Index int64 `json:"index"`
|
||||||
@@ -1541,7 +1545,7 @@ type GetDescriptionOfProductResultPDF struct {
|
|||||||
|
|
||||||
// Returns a product characteristics description by product identifier. You can search for the product by `offer_id` or `product_id`
|
// 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) {
|
func (c Products) GetDescriptionOfProduct(ctx context.Context, params *GetDescriptionOfProductParams) (*GetDescriptionOfProductResponse, error) {
|
||||||
url := "/v4/product/info/attributes"
|
url := "/v3/products/info/attributes"
|
||||||
|
|
||||||
resp := &GetDescriptionOfProductResponse{}
|
resp := &GetDescriptionOfProductResponse{}
|
||||||
|
|
||||||
@@ -1596,6 +1600,7 @@ type GetDescriptionOfProductsResult struct {
|
|||||||
ModelInfo *ModelInfo `json:"model_info,omitempty"`
|
ModelInfo *ModelInfo `json:"model_info,omitempty"`
|
||||||
|
|
||||||
Images []string `json:"images"`
|
Images []string `json:"images"`
|
||||||
|
PDFList []string `json:"pdf_list"`
|
||||||
|
|
||||||
Attributes []GetDescriptionOfProductsAttribute `json:"attributes"`
|
Attributes []GetDescriptionOfProductsAttribute `json:"attributes"`
|
||||||
ComplexAttributes []GetDescriptionOfProductsComplexAttribute `json:"complex_attributes"`
|
ComplexAttributes []GetDescriptionOfProductsComplexAttribute `json:"complex_attributes"`
|
||||||
@@ -1945,8 +1950,10 @@ type GetProductPriceInfoParams struct {
|
|||||||
// Filter by product
|
// Filter by product
|
||||||
Filter GetProductPriceInfoFilter `json:"filter"`
|
Filter GetProductPriceInfoFilter `json:"filter"`
|
||||||
|
|
||||||
// Cursor for the next data sample
|
// Identifier of the last value on page.
|
||||||
Cursor string `json:"cursor"`
|
//
|
||||||
|
// To get the next values, specify the recieved value in the next request in the `last_id` parameter
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
// Number of values per page. Minimum is 1, maximum is 1000
|
// Number of values per page. Minimum is 1, maximum is 1000
|
||||||
Limit int32 `json:"limit"`
|
Limit int32 `json:"limit"`
|
||||||
@@ -1966,11 +1973,18 @@ type GetProductPriceInfoFilter struct {
|
|||||||
type GetProductPriceInfoResponse struct {
|
type GetProductPriceInfoResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Result
|
||||||
|
Result GetProductPriceInfoResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductPriceInfoResult struct {
|
||||||
// Products list
|
// Products list
|
||||||
Items []GetProductPriceInfoResultItem `json:"items"`
|
Items []GetProductPriceInfoResultItem `json:"items"`
|
||||||
|
|
||||||
// Cursor for the next data sample
|
// Identifier of the last value on page. Leave this field blank in the first request.
|
||||||
Cursor string `json:"cursor"`
|
//
|
||||||
|
// To get the next values, specify last_id from the response of the previous request
|
||||||
|
LastId string `json:"last_id"`
|
||||||
|
|
||||||
// Products number in the list
|
// Products number in the list
|
||||||
Total int32 `json:"total"`
|
Total int32 `json:"total"`
|
||||||
@@ -1978,7 +1992,7 @@ type GetProductPriceInfoResponse struct {
|
|||||||
|
|
||||||
type GetProductPriceInfoResultItem struct {
|
type GetProductPriceInfoResultItem struct {
|
||||||
// Maximum acquiring fee
|
// Maximum acquiring fee
|
||||||
Acquiring float64 `json:"acquiring"`
|
Acquiring int32 `json:"acquiring"`
|
||||||
|
|
||||||
// Commissions information
|
// Commissions information
|
||||||
Commissions GetProductPriceInfoResultItemCommission `json:"commissions"`
|
Commissions GetProductPriceInfoResultItemCommission `json:"commissions"`
|
||||||
@@ -1992,6 +2006,11 @@ type GetProductPriceInfoResultItem struct {
|
|||||||
// Product price
|
// Product price
|
||||||
Price GetProductPriceInfoResultItemPrice `json:"price"`
|
Price GetProductPriceInfoResultItemPrice `json:"price"`
|
||||||
|
|
||||||
|
// Deprected: price index
|
||||||
|
//
|
||||||
|
// Use PriceIndexes instead
|
||||||
|
PriceIndex string `json:"price_index"`
|
||||||
|
|
||||||
// Product price indexes
|
// Product price indexes
|
||||||
PriceIndexes GetProductPriceInfoResultItemPriceIndexes `json:"price_indexes"`
|
PriceIndexes GetProductPriceInfoResultItemPriceIndexes `json:"price_indexes"`
|
||||||
|
|
||||||
@@ -2079,11 +2098,11 @@ type GetProductPriceInfoResultItemMarketingActionsAction struct {
|
|||||||
// Date and time when the seller's promotion ends
|
// Date and time when the seller's promotion ends
|
||||||
DateTo time.Time `json:"date_to"`
|
DateTo time.Time `json:"date_to"`
|
||||||
|
|
||||||
|
// Discount on the seller's promotion
|
||||||
|
DiscountValue string `json:"discount_value"`
|
||||||
|
|
||||||
// Promotion name
|
// Promotion name
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
|
||||||
// Discount on the seller's promotion
|
|
||||||
Value float64 `json:"value"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProductPriceInfoResultItemPrice struct {
|
type GetProductPriceInfoResultItemPrice struct {
|
||||||
@@ -2094,50 +2113,72 @@ type GetProductPriceInfoResultItemPrice struct {
|
|||||||
CurrencyCode string `json:"currency_code"`
|
CurrencyCode string `json:"currency_code"`
|
||||||
|
|
||||||
// Product price including all promotion discounts. This value will be indicated on the Ozon storefront
|
// Product price including all promotion discounts. This value will be indicated on the Ozon storefront
|
||||||
MarketingPrice float64 `json:"marketing_price"`
|
MarketingPrice string `json:"marketing_price"`
|
||||||
|
|
||||||
// Product price with seller's promotions applied
|
// Product price with seller's promotions applied
|
||||||
MarketingSellerPrice float64 `json:"marketing_seller_price"`
|
MarketingSellerPrice string `json:"marketing_seller_price"`
|
||||||
|
|
||||||
// Minimum price for similar products on Ozon
|
// Minimum price for similar products on Ozon
|
||||||
MinOzonPrice float64 `json:"min_ozon_price"`
|
MinOzonPrice string `json:"min_ozon_price"`
|
||||||
|
|
||||||
// Minimum product price with all promotions applied
|
// Minimum product price with all promotions applied
|
||||||
MinPrice float64 `json:"min_price"`
|
MinPrice string `json:"min_price"`
|
||||||
|
|
||||||
// Price before discounts. Displayed strikethrough on the product description page
|
// Price before discounts. Displayed strikethrough on the product description page
|
||||||
OldPrice float64 `json:"old_price"`
|
OldPrice string `json:"old_price"`
|
||||||
|
|
||||||
// Product price including discounts. This value is shown on the product description page
|
// Product price including discounts. This value is shown on the product description page
|
||||||
Price float64 `json:"price"`
|
Price string `json:"price"`
|
||||||
|
|
||||||
// Retailer price
|
// Retailer price
|
||||||
RetailPrice float64 `json:"retail_price"`
|
RetailPrice string `json:"retail_price"`
|
||||||
|
|
||||||
// Product VAT rate
|
// Product VAT rate
|
||||||
VAT float64 `json:"vat"`
|
VAT string `json:"vat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProductPriceInfoResultItemPriceIndexes struct {
|
type GetProductPriceInfoResultItemPriceIndexes struct {
|
||||||
// Resulting price index of the product
|
|
||||||
ColorIndex string `json:"color_index"`
|
|
||||||
|
|
||||||
// Competitors' product price on other marketplaces
|
// Competitors' product price on other marketplaces
|
||||||
ExternalIndexData GetProductPriceInfoResultItemPriceIndexesValue `json:"external_index_data"`
|
ExternalIndexData GetProductPriceInfoResultItemPriceIndexesExternal `json:"external_index_data"`
|
||||||
|
|
||||||
// Competitors' product price on Ozon
|
// Competitors' product price on Ozon
|
||||||
OzonIndexData GetProductPriceInfoResultItemPriceIndexesValue `json:"ozon_index_data"`
|
OzonIndexData GetProductPriceInfoResultItemPriceIndexesOzon `json:"ozon_index_data"`
|
||||||
|
|
||||||
|
// Resulting price index of the product
|
||||||
|
PriceIndex string `json:"price_index"`
|
||||||
|
|
||||||
// Price of your product on other marketplaces
|
// Price of your product on other marketplaces
|
||||||
SelfMarketplaceIndexData GetProductPriceInfoResultItemPriceIndexesValue `json:"self_marketplaces_index_data"`
|
SelfMarketplaceIndexData GetProductPriceInfoResultItemPriceIndexesSelfMarketplace `json:"self_marketplaces_index_data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProductPriceInfoResultItemPriceIndexesValue struct {
|
type GetProductPriceInfoResultItemPriceIndexesExternal struct {
|
||||||
// Minimum price of your product on other marketplaces
|
// Minimum competitors' product price on other marketplaces
|
||||||
MinimalPrice float64 `json:"min_price"`
|
MinimalPrice string `json:"minimal_price"`
|
||||||
|
|
||||||
// Price currency
|
// Price currency
|
||||||
MinimalPriceCurrency string `json:"min_price_currency"`
|
MinimalPriceCurrency string `json:"minimal_price_currency"`
|
||||||
|
|
||||||
|
// Price index value
|
||||||
|
PriceIndexValue float64 `json:"price_index_value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductPriceInfoResultItemPriceIndexesOzon struct {
|
||||||
|
// Minimum competitors' product price on Ozon
|
||||||
|
MinimalPrice string `json:"minimal_price"`
|
||||||
|
|
||||||
|
// Price currency
|
||||||
|
MinimalPriceCurrency string `json:"minimal_price_currency"`
|
||||||
|
|
||||||
|
// Price index value
|
||||||
|
PriceIndexValue float64 `json:"price_index_value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductPriceInfoResultItemPriceIndexesSelfMarketplace struct {
|
||||||
|
// Minimum price of your product on other marketplaces
|
||||||
|
MinimalPrice string `json:"minimal_price"`
|
||||||
|
|
||||||
|
// Price currency
|
||||||
|
MinimalPriceCurrency string `json:"minimal_price_currency"`
|
||||||
|
|
||||||
// Price index value
|
// Price index value
|
||||||
PriceIndexValue float64 `json:"price_index_value"`
|
PriceIndexValue float64 `json:"price_index_value"`
|
||||||
@@ -2149,7 +2190,7 @@ type GetProductPriceInfoResultItemPriceIndexesValue struct {
|
|||||||
// The `fbo_direct_flow_trans_max_amount` and `fbo_direct_flow_trans_min_amount` parameters
|
// The `fbo_direct_flow_trans_max_amount` and `fbo_direct_flow_trans_min_amount` parameters
|
||||||
// from the method response are in development and return 0
|
// from the method response are in development and return 0
|
||||||
func (c Products) GetProductPriceInfo(ctx context.Context, params *GetProductPriceInfoParams) (*GetProductPriceInfoResponse, error) {
|
func (c Products) GetProductPriceInfo(ctx context.Context, params *GetProductPriceInfoParams) (*GetProductPriceInfoResponse, error) {
|
||||||
url := "/v5/product/info/prices"
|
url := "/v4/product/info/prices"
|
||||||
|
|
||||||
resp := &GetProductPriceInfoResponse{}
|
resp := &GetProductPriceInfoResponse{}
|
||||||
|
|
||||||
@@ -2601,63 +2642,3 @@ func (c Products) ListEconomy(ctx context.Context, params *ListEconomyProductsPa
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePriceRelevanceTimerParams struct {
|
|
||||||
// List of product identifiers
|
|
||||||
ProductIds []string `json:"product_ids"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdatePriceRelevanceTimerResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Products) UpdatePriceRelevanceTimer(ctx context.Context, params *UpdatePriceRelevanceTimerParams) (*UpdatePriceRelevanceTimerResponse, error) {
|
|
||||||
url := "/v1/product/action/timer/update"
|
|
||||||
|
|
||||||
resp := &UpdatePriceRelevanceTimerResponse{}
|
|
||||||
|
|
||||||
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 StatusPriceRelevanceTimerParams struct {
|
|
||||||
// List of product identifiers
|
|
||||||
ProductIds []string `json:"product_ids"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type StatusPriceRelevanceTimerResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
Statuses []PriceRelevanceTimerStatus `json:"statuses"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PriceRelevanceTimerStatus struct {
|
|
||||||
// Timer end time
|
|
||||||
ExpiredAt time.Time `json:"expired_at"`
|
|
||||||
|
|
||||||
// true, if Ozon takes into account the minimum price when creating promotions
|
|
||||||
MinPriceForAutoActionsEnabled bool `json:"min_price_for_auto_actions_enabled"`
|
|
||||||
|
|
||||||
// Product identifier
|
|
||||||
ProductId int64 `json:"product_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get status of timer you've set
|
|
||||||
func (c Products) StatusPriceRelevanceTimer(ctx context.Context, params *StatusPriceRelevanceTimerParams) (*StatusPriceRelevanceTimerResponse, error) {
|
|
||||||
url := "/v1/product/action/timer/update"
|
|
||||||
|
|
||||||
resp := &StatusPriceRelevanceTimerResponse{}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Promotions struct {
|
type Promotions struct {
|
||||||
@@ -297,6 +297,196 @@ func (c Promotions) RemoveProduct(ctx context.Context, params *RemoveProductFrom
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ListHotSalePromotionsResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result []ListHotSalePromotionsResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListHotSalePromotionsResult struct {
|
||||||
|
// Promotion end date
|
||||||
|
DateEnd string `json:"date_end"`
|
||||||
|
|
||||||
|
// Promotion start date
|
||||||
|
DateStart string `json:"date_start"`
|
||||||
|
|
||||||
|
// Promotion description
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
|
// Promotion freeze date.
|
||||||
|
//
|
||||||
|
// If the field is filled, the seller can't increase prices, change the list of products,
|
||||||
|
// or decrease the number of product units in the promotion.
|
||||||
|
//
|
||||||
|
// The seller can lower prices and increase the product units number in the promotion
|
||||||
|
FreezeDate string `json:"freeze_date"`
|
||||||
|
|
||||||
|
// Hot Sale promotion identifier
|
||||||
|
HotsaleId float64 `json:"hotsale_id"`
|
||||||
|
|
||||||
|
// Indication that you participate in this promotion
|
||||||
|
IsParticipating bool `json:"is_participating"`
|
||||||
|
|
||||||
|
// Promotion name
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of available Hot Sale promotions
|
||||||
|
func (c Promotions) ListHotSalePromotions(ctx context.Context) (*ListHotSalePromotionsResponse, error) {
|
||||||
|
url := "/v1/actions/hotsales/list"
|
||||||
|
|
||||||
|
resp := &ListHotSalePromotionsResponse{}
|
||||||
|
|
||||||
|
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 ProductsAvailableForHotSalePromotionParams struct {
|
||||||
|
// Hot Sale promotion identifier
|
||||||
|
HotSaleId float64 `json:"hotsale_id"`
|
||||||
|
|
||||||
|
// Number of elements in the response. Default value is 100
|
||||||
|
Limit float64 `json:"limit"`
|
||||||
|
|
||||||
|
// Number of elements that will be skipped in the response. For example, if offset=10, the response will start with the 11th element found
|
||||||
|
Offset float64 `json:"offset,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsAvailableForHotSalePromotionResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result ProductsAvailableForHotSalePromotionResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsAvailableForHotSalePromotionResult struct {
|
||||||
|
// Products list
|
||||||
|
Products []ProductsAvailableForHotSalePromotionResultProduct `json:"products"`
|
||||||
|
|
||||||
|
// Total number of products that are available for the promotion
|
||||||
|
Total float64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsAvailableForHotSalePromotionResultProduct struct {
|
||||||
|
// Promotional product price
|
||||||
|
ActionPrice float64 `json:"action_price"`
|
||||||
|
|
||||||
|
// Date when the product participates in the promotion in the YYYY-MM-DD format
|
||||||
|
DateDayPromo string `json:"date_day_promo"`
|
||||||
|
|
||||||
|
// Product identifier
|
||||||
|
Id float64 `json:"id"`
|
||||||
|
|
||||||
|
// Indication that product participates in the promotion
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
|
|
||||||
|
// Maximum possible promotional price of the product
|
||||||
|
MaxActionPrice float64 `json:"max_action_price"`
|
||||||
|
|
||||||
|
// Minimum number of product units in a stock discount type promotion
|
||||||
|
MinStock float64 `json:"min_stock"`
|
||||||
|
|
||||||
|
// Number of product units in a stock discount type promotion
|
||||||
|
Stock float64 `json:"stock"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method for getting a list of products that can participate or are already participating in the Hot Sale promotion
|
||||||
|
func (c Promotions) ProductsAvailableForHotSalePromotion(ctx context.Context, params *ProductsAvailableForHotSalePromotionParams) (*ProductsAvailableForHotSalePromotionResponse, error) {
|
||||||
|
url := "/v1/actions/hotsales/products"
|
||||||
|
|
||||||
|
resp := &ProductsAvailableForHotSalePromotionResponse{}
|
||||||
|
|
||||||
|
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 AddProductsToHotSaleParams struct {
|
||||||
|
// Hot Sale promotion identifier
|
||||||
|
HotSaleId float64 `json:"hotsale_id"`
|
||||||
|
|
||||||
|
// Products to be added to the promotion. The maximum number in one request is 100
|
||||||
|
Products []AddProductsToHotSaleProduct `json:"products"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddProductsToHotSaleProduct struct {
|
||||||
|
// Promotional product price
|
||||||
|
ActionPrice float64 `json:"action_price"`
|
||||||
|
|
||||||
|
// Product identifier
|
||||||
|
ProductId float64 `json:"product_id"`
|
||||||
|
|
||||||
|
// Number of product units in a stock discount type promotion
|
||||||
|
Stock float64 `json:"stock"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsToHotSaleResponse struct {
|
||||||
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Method result
|
||||||
|
Result ProductsToHotSaleResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsToHotSaleResult struct {
|
||||||
|
// List of products that haven't been added to the promotion
|
||||||
|
Rejected []ProductsToHotSaleResultRejected `json:"rejected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductsToHotSaleResultRejected struct {
|
||||||
|
//Product identifier
|
||||||
|
ProductId float64 `json:"product_id"`
|
||||||
|
|
||||||
|
// Reason why the product hasn't been added to the promotion
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Promotions) AddProductsToHotSale(ctx context.Context, params *AddProductsToHotSaleParams) (*ProductsToHotSaleResponse, error) {
|
||||||
|
url := "/v1/actions/hotsales/activate"
|
||||||
|
|
||||||
|
resp := &ProductsToHotSaleResponse{}
|
||||||
|
|
||||||
|
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 RemoveProductsToHotSaleParams struct {
|
||||||
|
// Hot Sale promotion identifier
|
||||||
|
HotSaleId float64 `json:"hotsale_id"`
|
||||||
|
|
||||||
|
// List of products identifiers. Maximum number of values in one request is 100
|
||||||
|
ProductIds []float64 `json:"product_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove product from the Hot Sale promotion
|
||||||
|
func (c Promotions) RemoveProductsToHotSale(ctx context.Context, params *RemoveProductsToHotSaleParams) (*ProductsToHotSaleResponse, error) {
|
||||||
|
url := "/v1/actions/hotsales/activate"
|
||||||
|
|
||||||
|
resp := &ProductsToHotSaleResponse{}
|
||||||
|
|
||||||
|
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 ListDiscountRequestsParams struct {
|
type ListDiscountRequestsParams struct {
|
||||||
// Discount request status
|
// Discount request status
|
||||||
Status ListDiscountRequestsStatus `json:"status" default:"UNKNOWN"`
|
Status ListDiscountRequestsStatus `json:"status" default:"UNKNOWN"`
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetAvailablePromotions(t *testing.T) {
|
func TestGetAvailablePromotions(t *testing.T) {
|
||||||
@@ -354,6 +354,248 @@ func TestRemoveProduct(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListHotSalePromotions(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": [
|
||||||
|
{
|
||||||
|
"date_end": "string",
|
||||||
|
"date_start": "string",
|
||||||
|
"description": "string",
|
||||||
|
"freeze_date": "string",
|
||||||
|
"hotsale_id": 0,
|
||||||
|
"is_participating": true,
|
||||||
|
"title": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
`{
|
||||||
|
"code": 16,
|
||||||
|
"message": "Client-Id and Api-Key headers are required"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||||
|
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||||
|
resp, err := c.Promotions().ListHotSalePromotions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ListHotSalePromotionsResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProductsAvailableForHotSalePromotion(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *ProductsAvailableForHotSalePromotionParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&ProductsAvailableForHotSalePromotionParams{
|
||||||
|
HotSaleId: 0,
|
||||||
|
Limit: 0,
|
||||||
|
Offset: 0,
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": {
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"action_price": 0,
|
||||||
|
"date_day_promo": "string",
|
||||||
|
"id": 0,
|
||||||
|
"is_active": true,
|
||||||
|
"max_action_price": 0,
|
||||||
|
"min_stock": 0,
|
||||||
|
"stock": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 0
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&ProductsAvailableForHotSalePromotionParams{},
|
||||||
|
`{
|
||||||
|
"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.Promotions().ProductsAvailableForHotSalePromotion(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ProductsAvailableForHotSalePromotionResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddProductsToHotSale(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *AddProductsToHotSaleParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&AddProductsToHotSaleParams{
|
||||||
|
HotSaleId: 1234,
|
||||||
|
Products: []AddProductsToHotSaleProduct{
|
||||||
|
{
|
||||||
|
ActionPrice: 12,
|
||||||
|
ProductId: 111,
|
||||||
|
Stock: 45,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": {
|
||||||
|
"rejected": [
|
||||||
|
{
|
||||||
|
"product_id": 0,
|
||||||
|
"reason": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&AddProductsToHotSaleParams{},
|
||||||
|
`{
|
||||||
|
"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.Promotions().AddProductsToHotSale(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ProductsToHotSaleResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveProductsToHotSale(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
statusCode int
|
||||||
|
headers map[string]string
|
||||||
|
params *RemoveProductsToHotSaleParams
|
||||||
|
response string
|
||||||
|
}{
|
||||||
|
// Test Ok
|
||||||
|
{
|
||||||
|
http.StatusOK,
|
||||||
|
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||||
|
&RemoveProductsToHotSaleParams{
|
||||||
|
HotSaleId: 12345,
|
||||||
|
ProductIds: []float64{111},
|
||||||
|
},
|
||||||
|
`{
|
||||||
|
"result": {
|
||||||
|
"rejected": [
|
||||||
|
{
|
||||||
|
"product_id": 0,
|
||||||
|
"reason": "string"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
// Test No Client-Id or Api-Key
|
||||||
|
{
|
||||||
|
http.StatusUnauthorized,
|
||||||
|
map[string]string{},
|
||||||
|
&RemoveProductsToHotSaleParams{},
|
||||||
|
`{
|
||||||
|
"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.Promotions().RemoveProductsToHotSale(ctx, test.params)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJsonResponse(t, test.response, &ProductsToHotSaleResponse{})
|
||||||
|
|
||||||
|
if resp.StatusCode != test.statusCode {
|
||||||
|
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestListDiscountRequests(t *testing.T) {
|
func TestListDiscountRequests(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Quants struct {
|
type Quants struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListQuants(t *testing.T) {
|
func TestListQuants(t *testing.T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rating struct {
|
type Rating struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetCurrentRatingInfo(t *testing.T) {
|
func TestGetCurrentRatingInfo(t *testing.T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reports struct {
|
type Reports struct {
|
||||||
@@ -480,10 +480,6 @@ type GetReturnsReportsFilter struct {
|
|||||||
type GetReturnsReportResponse struct {
|
type GetReturnsReportResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
Result GetReturnsReportResult `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetReturnsReportResult 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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetList(t *testing.T) {
|
func TestGetList(t *testing.T) {
|
||||||
@@ -380,9 +380,7 @@ func TestGetReturnsReport(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
"result": {
|
|
||||||
"code": "REPORT_seller_products_924336_1720170405_a9ea2f27-a473-4b13-99f9-d0cfcb5b1a69"
|
"code": "REPORT_seller_products_924336_1720170405_a9ea2f27-a473-4b13-99f9-d0cfcb5b1a69"
|
||||||
}
|
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
// Test No Client-Id or Api-Key
|
// Test No Client-Id or Api-Key
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Returns struct {
|
type Returns struct {
|
||||||
@@ -667,6 +667,9 @@ type GetFBSQuantityReturnsPagination struct {
|
|||||||
type GetFBSQuantityReturnsResponse struct {
|
type GetFBSQuantityReturnsResponse struct {
|
||||||
core.CommonResponse
|
core.CommonResponse
|
||||||
|
|
||||||
|
// Seller identifier
|
||||||
|
CompanyId int64 `json:"company_id"`
|
||||||
|
|
||||||
DropoffPoints []GetFBSQuantityDropoffPoint `json:"drop_off_points"`
|
DropoffPoints []GetFBSQuantityDropoffPoint `json:"drop_off_points"`
|
||||||
|
|
||||||
// true if there are any other points where sellers have orders waiting
|
// true if there are any other points where sellers have orders waiting
|
||||||
@@ -694,12 +697,6 @@ type GetFBSQuantityDropoffPoint struct {
|
|||||||
|
|
||||||
// Seller's warehouses identifiers
|
// Seller's warehouses identifiers
|
||||||
WarehouseIds []string `json:"warehouses_ids"`
|
WarehouseIds []string `json:"warehouses_ids"`
|
||||||
|
|
||||||
// Number of boxes in drop-off point
|
|
||||||
BoxCount int32 `json:"box_count"`
|
|
||||||
|
|
||||||
// Time zone offset of the shipping time from UTC-0
|
|
||||||
UTCOffset string `json:"utc_offset"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetFBSQuantityDropoffPointPassInfo struct {
|
type GetFBSQuantityDropoffPointPassInfo struct {
|
||||||
@@ -725,7 +722,7 @@ func (c Returns) FBSQuantity(ctx context.Context, params *GetFBSQuantityReturnsP
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListReturnsParams struct {
|
type ListReturnsParams struct {
|
||||||
// Filters. Use only one filter per request. Otherwise it returns an error
|
// Filter
|
||||||
Filter *ListReturnsFilter `json:"filter,omitempty"`
|
Filter *ListReturnsFilter `json:"filter,omitempty"`
|
||||||
|
|
||||||
// Number of loaded returns. The maximum value is 500
|
// Number of loaded returns. The maximum value is 500
|
||||||
@@ -748,7 +745,7 @@ type ListReturnsFilter struct {
|
|||||||
// Filter by order identifier
|
// Filter by order identifier
|
||||||
OrderId int64 `json:"order_id,omitempty"`
|
OrderId int64 `json:"order_id,omitempty"`
|
||||||
|
|
||||||
// Filter by shipment number. Pass no more than 50 postings
|
// Filter by shipment number
|
||||||
PostingNumbers []string `json:"posting_numbers,omitempty"`
|
PostingNumbers []string `json:"posting_numbers,omitempty"`
|
||||||
|
|
||||||
// Filter by product name
|
// Filter by product name
|
||||||
@@ -907,9 +904,6 @@ type ReturnProduct struct {
|
|||||||
|
|
||||||
// Commission details
|
// Commission details
|
||||||
Commission ReturnSum `json:"commission"`
|
Commission ReturnSum `json:"commission"`
|
||||||
|
|
||||||
// Product quantity
|
|
||||||
Quantity int32 `json:"quantity"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReturnLogistic struct {
|
type ReturnLogistic struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetRFBSReturns(t *testing.T) {
|
func TestGetRFBSReturns(t *testing.T) {
|
||||||
@@ -823,10 +823,10 @@ func TestFBSQuantity(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
`{
|
`{
|
||||||
|
"company_id": 0,
|
||||||
"drop_off_points": [
|
"drop_off_points": [
|
||||||
{
|
{
|
||||||
"address": "string",
|
"address": "string",
|
||||||
"box_count": 0,
|
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "string",
|
"name": "string",
|
||||||
"pass_info": {
|
"pass_info": {
|
||||||
@@ -835,7 +835,6 @@ func TestFBSQuantity(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"place_id": 0,
|
"place_id": 0,
|
||||||
"returns_count": 0,
|
"returns_count": 0,
|
||||||
"utc_offset": "string",
|
|
||||||
"warehouses_ids": [
|
"warehouses_ids": [
|
||||||
"string"
|
"string"
|
||||||
]
|
]
|
||||||
@@ -964,8 +963,7 @@ func TestListReturns(t *testing.T) {
|
|||||||
"commission": {
|
"commission": {
|
||||||
"currency_code": "RUB",
|
"currency_code": "RUB",
|
||||||
"price": 2312
|
"price": 2312
|
||||||
},
|
}
|
||||||
"quantity": 1
|
|
||||||
},
|
},
|
||||||
"logistic": {
|
"logistic": {
|
||||||
"technical_return_moment": "2024-07-29T06:15:48.998146Z",
|
"technical_return_moment": "2024-07-29T06:15:48.998146Z",
|
||||||
|
|||||||
334
ozon/reviews.go
334
ozon/reviews.go
@@ -1,334 +0,0 @@
|
|||||||
package ozon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Reviews struct {
|
|
||||||
client *core.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type LeaveCommentParams struct {
|
|
||||||
// Review status update
|
|
||||||
MarkReviewAsProcesses bool `json:"mark_review_as_processed"`
|
|
||||||
|
|
||||||
// Identifier of the parent comment you're replying to
|
|
||||||
ParentCommentId string `json:"parent_comment_id"`
|
|
||||||
|
|
||||||
// Review identifier
|
|
||||||
ReviewId string `json:"review_id"`
|
|
||||||
|
|
||||||
// Comment text
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LeaveCommentResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Comment identifier
|
|
||||||
CommentId string `json:"comment_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
func (c Reviews) LeaveComment(ctx context.Context, params *LeaveCommentParams) (*LeaveCommentResponse, error) {
|
|
||||||
url := "/v1/review/comment/create"
|
|
||||||
|
|
||||||
resp := &LeaveCommentResponse{}
|
|
||||||
|
|
||||||
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 DeleteCommentParams struct {
|
|
||||||
// Comment identifier
|
|
||||||
CommentId string `json:"comment_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteCommentResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
func (c Reviews) DeleteComment(ctx context.Context, params *DeleteCommentParams) (*DeleteCommentResponse, error) {
|
|
||||||
url := "/v1/review/comment/delete"
|
|
||||||
|
|
||||||
resp := &DeleteCommentResponse{}
|
|
||||||
|
|
||||||
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 ListCommentsParams struct {
|
|
||||||
// Limit of values in the response. Minimum is 20. Maximum is 100
|
|
||||||
Limit int32 `json:"limit"`
|
|
||||||
|
|
||||||
// Number of elements that is skipped in the response.
|
|
||||||
// For example, if offset = 10, the response starts with the 11th element found
|
|
||||||
Offset int32 `json:"offset"`
|
|
||||||
|
|
||||||
// Review identifier
|
|
||||||
ReviewId string `json:"review_id"`
|
|
||||||
|
|
||||||
// Sorting direction
|
|
||||||
SortDir Order `json:"sort_dir"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListCommentsResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Number of elements in the response
|
|
||||||
Offset int32 `json:"offset"`
|
|
||||||
|
|
||||||
// Comment details
|
|
||||||
Comments []Comment `json:"comments"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Comment struct {
|
|
||||||
// Comment identifier
|
|
||||||
Id string `json:"id"`
|
|
||||||
|
|
||||||
// true, if the comment was left by an official, false if a customer left it
|
|
||||||
IsOfficial bool `json:"is_official"`
|
|
||||||
|
|
||||||
// true, if the comment was left by a seller, false if a customer left it
|
|
||||||
IsOwner bool `json:"is_owner"`
|
|
||||||
|
|
||||||
// Identifier of the parent comment to reply to
|
|
||||||
ParentCommentId string `json:"parent_comment_id"`
|
|
||||||
|
|
||||||
// Date the comment was published
|
|
||||||
PublishedAt time.Time `json:"published_at"`
|
|
||||||
|
|
||||||
// Comment text
|
|
||||||
Text string `json:"text"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
//
|
|
||||||
// Method returns information about comments on reviews that have passed moderation
|
|
||||||
func (c Reviews) ListComments(ctx context.Context, params *ListCommentsParams) (*ListCommentsResponse, error) {
|
|
||||||
url := "/v1/review/comment/list"
|
|
||||||
|
|
||||||
resp := &ListCommentsResponse{}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
type ChangeStatusParams struct {
|
|
||||||
// Array with review identifiers from 1 to 100
|
|
||||||
ReviewIds []string `json:"review_ids"`
|
|
||||||
|
|
||||||
// Review status
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChangeStatusResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
func (c Reviews) ChangeStatus(ctx context.Context, params *ChangeStatusParams) (*ChangeStatusResponse, error) {
|
|
||||||
url := "/v1/review/change-status"
|
|
||||||
|
|
||||||
resp := &ChangeStatusResponse{}
|
|
||||||
|
|
||||||
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 CountReviewsResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// Number of processed review
|
|
||||||
Processed int32 `json:"processed"`
|
|
||||||
|
|
||||||
// Number of all reviews
|
|
||||||
Total int32 `json:"total"`
|
|
||||||
|
|
||||||
// Number of unprocessed reviews
|
|
||||||
Unprocessed int32 `json:"unprocessed"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
func (c Reviews) Count(ctx context.Context) (*CountReviewsResponse, error) {
|
|
||||||
url := "/v1/review/count"
|
|
||||||
|
|
||||||
resp := &CountReviewsResponse{}
|
|
||||||
|
|
||||||
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 GetReviewParams struct {
|
|
||||||
// Review identifier
|
|
||||||
ReviewId string `json:"review_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetReviewResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
ReviewDetails
|
|
||||||
|
|
||||||
// Number of dislikes on the review
|
|
||||||
DislikesAmount int32 `json:"dislikes_amount"`
|
|
||||||
|
|
||||||
// Number of likes on the review
|
|
||||||
LikesAmount int32 `json:"likes_amount"`
|
|
||||||
|
|
||||||
// Image details
|
|
||||||
Photos []ReviewPhoto `json:"photos"`
|
|
||||||
|
|
||||||
// Video details
|
|
||||||
Videos []ReviewVideo `json:"videos"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReviewDetails struct {
|
|
||||||
// Number of comments on the review
|
|
||||||
CommentsAmount int32 `json:"comments_amount"`
|
|
||||||
|
|
||||||
// Review identifier
|
|
||||||
Id string `json:"id"`
|
|
||||||
|
|
||||||
// true, if the review affects the rating calculation
|
|
||||||
IsRatingParticipant bool `json:"is_rating_participant"`
|
|
||||||
|
|
||||||
// Status of the order for which the customer left a review
|
|
||||||
OrderStatus string `json:"order_status"`
|
|
||||||
|
|
||||||
// Number of images in the review
|
|
||||||
PhotosAmount int32 `json:"photos_amount"`
|
|
||||||
|
|
||||||
// Review publication date
|
|
||||||
PublishedAt time.Time `json:"published_at"`
|
|
||||||
|
|
||||||
// Review rating
|
|
||||||
Rating int32 `json:"rating"`
|
|
||||||
|
|
||||||
// Product identifier in the Ozon system, SKU
|
|
||||||
SKU int64 `json:"sku"`
|
|
||||||
|
|
||||||
// Review status
|
|
||||||
Status string `json:"status"`
|
|
||||||
|
|
||||||
// Review text
|
|
||||||
Text string `json:"text"`
|
|
||||||
|
|
||||||
// Number of videos for the review
|
|
||||||
VideosAmount int32 `json:"videos_amount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReviewPhoto struct {
|
|
||||||
// Height
|
|
||||||
Height int32 `json:"height"`
|
|
||||||
|
|
||||||
// Link to image
|
|
||||||
URL string `json:"url"`
|
|
||||||
|
|
||||||
// Width
|
|
||||||
Width int32 `json:"width"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReviewVideo struct {
|
|
||||||
// Height
|
|
||||||
Height int64 `json:"height"`
|
|
||||||
|
|
||||||
// Link to video preview
|
|
||||||
PreviewURL string `json:"preview_url"`
|
|
||||||
|
|
||||||
// Link to short video
|
|
||||||
ShortVideoPreviewURL string `json:"short_video_preview_url"`
|
|
||||||
|
|
||||||
// Video link
|
|
||||||
URL string `json:"url"`
|
|
||||||
|
|
||||||
// Width
|
|
||||||
Width int64 `json:"width"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
func (c Reviews) Get(ctx context.Context, params *GetReviewParams) (*GetReviewResponse, error) {
|
|
||||||
url := "/v1/review/info"
|
|
||||||
|
|
||||||
resp := &GetReviewResponse{}
|
|
||||||
|
|
||||||
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 ListReviewsParams struct {
|
|
||||||
// Identifier of the last review on the page
|
|
||||||
LastId string `json:"last_id"`
|
|
||||||
|
|
||||||
// Number of reviews in the response. Minimum is 20, maximum is 100
|
|
||||||
Limit int32 `json:"limit"`
|
|
||||||
|
|
||||||
// Sorting direction
|
|
||||||
SortDir Order `json:"sort_dir"`
|
|
||||||
|
|
||||||
// Review statuses
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListReviewsResponse struct {
|
|
||||||
core.CommonResponse
|
|
||||||
|
|
||||||
// true, if not all reviews were returned in the response
|
|
||||||
HasNext bool `json:"has_next"`
|
|
||||||
|
|
||||||
// Identifier of the last review on the page
|
|
||||||
LastId string `json:"last_id"`
|
|
||||||
|
|
||||||
// Review details
|
|
||||||
Reviews []ReviewDetails `json:"reviews"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only available to sellers with the Premium Plus subscription
|
|
||||||
func (c Reviews) List(ctx context.Context, params *ListReviewsParams) (*ListReviewsResponse, error) {
|
|
||||||
url := "/v1/review/list"
|
|
||||||
|
|
||||||
resp := &ListReviewsResponse{}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,416 +0,0 @@
|
|||||||
package ozon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLeaveComment(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *LeaveCommentParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&LeaveCommentParams{
|
|
||||||
MarkReviewAsProcesses: true,
|
|
||||||
ParentCommentId: "string",
|
|
||||||
ReviewId: "string1",
|
|
||||||
Text: "some string",
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"comment_id": "string"
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&LeaveCommentParams{},
|
|
||||||
`{
|
|
||||||
"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.Reviews().LeaveComment(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &LeaveCommentResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteComment(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *DeleteCommentParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&DeleteCommentParams{
|
|
||||||
CommentId: "string",
|
|
||||||
},
|
|
||||||
`{}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&DeleteCommentParams{},
|
|
||||||
`{
|
|
||||||
"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.Reviews().DeleteComment(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &DeleteCommentResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListComments(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *ListCommentsParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&ListCommentsParams{
|
|
||||||
Limit: 0,
|
|
||||||
Offset: 0,
|
|
||||||
ReviewId: "string",
|
|
||||||
SortDir: Ascending,
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"comments": [
|
|
||||||
{
|
|
||||||
"id": "string",
|
|
||||||
"is_official": true,
|
|
||||||
"is_owner": true,
|
|
||||||
"parent_comment_id": "string",
|
|
||||||
"published_at": "2019-08-24T14:15:22Z",
|
|
||||||
"text": "string"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"offset": 0
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&ListCommentsParams{},
|
|
||||||
`{
|
|
||||||
"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.Reviews().ListComments(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &ListCommentsResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChangeStatus(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *ChangeStatusParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&ChangeStatusParams{
|
|
||||||
ReviewIds: []string{"string"},
|
|
||||||
Status: "PROCESSED",
|
|
||||||
},
|
|
||||||
`{}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&ChangeStatusParams{},
|
|
||||||
`{
|
|
||||||
"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.Reviews().ChangeStatus(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &ChangeStatusResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCountReviews(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"},
|
|
||||||
`{
|
|
||||||
"processed": 2,
|
|
||||||
"total": 3,
|
|
||||||
"unprocessed": 1
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// 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.Reviews().Count(ctx)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &CountReviewsResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetReview(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *GetReviewParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&GetReviewParams{
|
|
||||||
ReviewId: "string",
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"comments_amount": 0,
|
|
||||||
"dislikes_amount": 0,
|
|
||||||
"id": "string",
|
|
||||||
"is_rating_participant": true,
|
|
||||||
"likes_amount": 0,
|
|
||||||
"order_status": "string",
|
|
||||||
"photos": [
|
|
||||||
{
|
|
||||||
"height": 0,
|
|
||||||
"url": "string",
|
|
||||||
"width": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"photos_amount": 0,
|
|
||||||
"published_at": "2019-08-24T14:15:22Z",
|
|
||||||
"rating": 0,
|
|
||||||
"sku": 0,
|
|
||||||
"status": "string",
|
|
||||||
"text": "string",
|
|
||||||
"videos": [
|
|
||||||
{
|
|
||||||
"height": 0,
|
|
||||||
"preview_url": "string",
|
|
||||||
"short_video_preview_url": "string",
|
|
||||||
"url": "string",
|
|
||||||
"width": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"videos_amount": 0
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&GetReviewParams{},
|
|
||||||
`{
|
|
||||||
"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.Reviews().Get(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &GetReviewResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListReviews(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
statusCode int
|
|
||||||
headers map[string]string
|
|
||||||
params *ListReviewsParams
|
|
||||||
response string
|
|
||||||
}{
|
|
||||||
// Test Ok
|
|
||||||
{
|
|
||||||
http.StatusOK,
|
|
||||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
|
||||||
&ListReviewsParams{
|
|
||||||
LastId: "string",
|
|
||||||
Limit: 0,
|
|
||||||
SortDir: Ascending,
|
|
||||||
Status: "ALL",
|
|
||||||
},
|
|
||||||
`{
|
|
||||||
"has_next": true,
|
|
||||||
"last_id": "string",
|
|
||||||
"reviews": [
|
|
||||||
{
|
|
||||||
"comments_amount": 0,
|
|
||||||
"id": "string",
|
|
||||||
"is_rating_participant": true,
|
|
||||||
"order_status": "string",
|
|
||||||
"photos_amount": 0,
|
|
||||||
"published_at": "2019-08-24T14:15:22Z",
|
|
||||||
"rating": 0,
|
|
||||||
"sku": 0,
|
|
||||||
"status": "string",
|
|
||||||
"text": "string",
|
|
||||||
"videos_amount": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
// Test No Client-Id or Api-Key
|
|
||||||
{
|
|
||||||
http.StatusUnauthorized,
|
|
||||||
map[string]string{},
|
|
||||||
&ListReviewsParams{},
|
|
||||||
`{
|
|
||||||
"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.Reviews().List(ctx, test.params)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
compareJsonResponse(t, test.response, &ListReviewsResponse{})
|
|
||||||
|
|
||||||
if resp.StatusCode != test.statusCode {
|
|
||||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Strategies struct {
|
type Strategies struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListCompetitors(t *testing.T) {
|
func TestListCompetitors(t *testing.T) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Warehouses struct {
|
type Warehouses struct {
|
||||||
@@ -88,8 +88,7 @@ type GetListOfWarehousesResultFirstMile struct {
|
|||||||
FirstMileType string `json:"first_mile_type"`
|
FirstMileType string `json:"first_mile_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method returns the list of FBS and rFBS warehouses.
|
// You do not need to specify any parameters in the request. Your company will be identified by the Warehouses ID
|
||||||
// To get the list of FBO warehouses, use the /v1/cluster/list method.
|
|
||||||
func (c Warehouses) GetListOfWarehouses(ctx context.Context) (*GetListOfWarehousesResponse, error) {
|
func (c Warehouses) GetListOfWarehouses(ctx context.Context) (*GetListOfWarehousesResponse, error) {
|
||||||
url := "/v1/warehouse/list"
|
url := "/v1/warehouse/list"
|
||||||
|
|
||||||
@@ -162,9 +161,6 @@ type GetListOfDeliveryMethodsResult struct {
|
|||||||
// Delivery service identifier
|
// Delivery service identifier
|
||||||
ProviderId int64 `json:"provider_id"`
|
ProviderId int64 `json:"provider_id"`
|
||||||
|
|
||||||
// Minimum time to package an order in minutes according to warehouse settings
|
|
||||||
SLACutIn int64 `json:"sla_cut_in"`
|
|
||||||
|
|
||||||
// Delivery method status:
|
// Delivery method status:
|
||||||
// - NEW—created,
|
// - NEW—created,
|
||||||
// - EDITED—being edited,
|
// - EDITED—being edited,
|
||||||
@@ -237,9 +233,7 @@ type Coordinates struct {
|
|||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the method to find sorting centres, pick-up points, and drop-off points available for cross-docking and direct supplies.
|
// Get a list of warehouses, sorting centers and pick-up points available for cross-docking, and direct supplies.
|
||||||
//
|
|
||||||
// You can view the addresses of all points on the map and in a table in the Knowledge Base.
|
|
||||||
func (c Warehouses) ListForShipping(ctx context.Context, params *ListForShippingParams) (*ListForShippingResponse, error) {
|
func (c Warehouses) ListForShipping(ctx context.Context, params *ListForShippingParams) (*ListForShippingResponse, error) {
|
||||||
url := "/v1/warehouse/fbo/list"
|
url := "/v1/warehouse/fbo/list"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
core "git.denco.store/fakz9/ozon-api-client"
|
core "github.com/diphantxm/ozon-api-client"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetListOfWarehouses(t *testing.T) {
|
func TestGetListOfWarehouses(t *testing.T) {
|
||||||
@@ -127,8 +127,7 @@ func TestGetListOfDeliveryMethods(t *testing.T) {
|
|||||||
"template_id": 0,
|
"template_id": 0,
|
||||||
"warehouse_id": 15588127982000,
|
"warehouse_id": 15588127982000,
|
||||||
"created_at": "2019-04-04T15:22:31.048202Z",
|
"created_at": "2019-04-04T15:22:31.048202Z",
|
||||||
"updated_at": "2021-08-15T10:21:44.854209Z",
|
"updated_at": "2021-08-15T10:21:44.854209Z"
|
||||||
"sla_cut_in": 1440
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_next": false
|
"has_next": false
|
||||||
|
|||||||
Reference in New Issue
Block a user