Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89aeba12d4 | |||
| 1e2c591a70 | |||
| 3e6c8fbc05 | |||
| c6c3030925 | |||
| c9ca89e364 | |||
| dcff4ba1e3 | |||
|
|
25e5e568b5 | ||
|
|
a9d7a5c6d3 | ||
|
|
bac3c04f59 | ||
|
|
851e7a2819 | ||
|
|
94119c0f39 | ||
|
|
1c42f6b3a6 | ||
|
|
dc69b67f0f | ||
|
|
bd280b54f4 | ||
|
|
1c0c203097 | ||
|
|
b08f17f3f1 | ||
|
|
4831ad70d6 | ||
|
|
739f672caf | ||
|
|
0fa0986178 | ||
|
|
76e54922fa | ||
|
|
24cc2cbe93 | ||
|
|
38e8446187 | ||
|
|
77c3cf5462 | ||
|
|
bc228dd6e1 | ||
|
|
3a67391d71 | ||
|
|
040bc23ebc | ||
|
|
804a4f3c2b | ||
|
|
c38e9f19a9 | ||
|
|
7654f5b7c5 | ||
|
|
7f705a4eb5 | ||
|
|
8173450413 | ||
|
|
32bd7748ec | ||
|
|
14986eb627 | ||
|
|
3c17a365a3 | ||
|
|
7f71ed6545 | ||
|
|
2f4d207726 | ||
|
|
7b5f44ee44 | ||
|
|
bc5f0e52a5 | ||
|
|
8b8b3bc974 | ||
|
|
45b0dffe39 | ||
|
|
c7697863db | ||
|
|
8a585d086a | ||
|
|
d1fd698368 | ||
|
|
6b8b22180a | ||
|
|
f4a09903c7 | ||
|
|
23ca98fedd | ||
|
|
eae6f54e71 | ||
|
|
8a6cd20b95 | ||
|
|
1706575a34 | ||
|
|
3430ead143 | ||
|
|
2164eff0a6 | ||
|
|
b6af642636 | ||
|
|
67898a4738 | ||
|
|
8c07540d28 | ||
|
|
7c0e18681b | ||
|
|
895ef8be52 | ||
|
|
ccd3610c76 | ||
|
|
090b2afb63 | ||
|
|
823386edf2 | ||
|
|
5ecf131061 |
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
177
ENDPOINTS.md
177
ENDPOINTS.md
@@ -1,177 +0,0 @@
|
||||
# Supported Endpoints
|
||||
|
||||
## Ozon attributes and characteristics
|
||||
- [x] Product category tree
|
||||
- [x] Category characteristics list
|
||||
- [x] 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,21 +2,19 @@
|
||||
A Ozon Seller API client written in Golang
|
||||
|
||||
[](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.
|
||||
|
||||
Read full [documentation](https://docs.ozon.ru/api/seller/en/#tag/Introduction)
|
||||
|
||||
You can check [list of supported endpoints](ENDPOINTS.md)
|
||||
|
||||
## How to start
|
||||
### API
|
||||
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.
|
||||
```bash
|
||||
go get github.com/diphantxm/ozon-api-client
|
||||
go get git.denco.store/fakz9/ozon-api-client
|
||||
```
|
||||
A simple example on how to use this library:
|
||||
```Golang
|
||||
@@ -28,7 +26,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/diphantxm/ozon-api-client/ozon"
|
||||
"git.denco.store/fakz9/ozon-api-client/ozon"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -67,7 +65,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/diphantxm/ozon-api-client/ozon/notifications"
|
||||
"git.denco.store/fakz9/ozon-api-client/ozon/notifications"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -91,8 +89,3 @@ 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 github.com/diphantxm/ozon-api-client
|
||||
module git.denco.store/fakz9/ozon-api-client
|
||||
|
||||
go 1.20
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Analytics struct {
|
||||
@@ -202,7 +202,7 @@ type GetStocksOnWarehousesResultRow struct {
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
// Report on stocks and products movement at Ozon warehouses
|
||||
// Method for getting a report on leftover stocks and products movement at Ozon warehouses
|
||||
func (c Analytics) GetStocksOnWarehouses(ctx context.Context, params *GetStocksOnWarehousesParams) (*GetStocksOnWarehousesResponse, error) {
|
||||
url := "/v2/analytics/stock_on_warehouses"
|
||||
|
||||
@@ -216,3 +216,235 @@ func (c Analytics) GetStocksOnWarehouses(ctx context.Context, params *GetStocksO
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetProductTurnoverParams struct {
|
||||
// Number of values in the response
|
||||
Limit int64 `json:"limit"`
|
||||
|
||||
// Number of elements to skip in the response.
|
||||
//
|
||||
// For example, if offset = 10, the response starts with the 11th element found
|
||||
Offset int32 `json:"offset"`
|
||||
|
||||
// Product identifiers in the Ozon system, SKU
|
||||
SKU []string `json:"sku"`
|
||||
}
|
||||
|
||||
type GetProductTurnoverResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Products
|
||||
Items []ProductTurnoverItem `json:"items"`
|
||||
}
|
||||
|
||||
type ProductTurnoverItem struct {
|
||||
// Average daily number of product items sold over the last 60 days
|
||||
Ads float64 `json:"ads"`
|
||||
|
||||
// Product stock, pcs
|
||||
CurrentStock int64 `json:"current_stock"`
|
||||
|
||||
// Number of days the stock will last based on your average daily sales
|
||||
IDC float64 `json:"idc"`
|
||||
|
||||
// Product stock level
|
||||
IDCGrade string `json:"idc_grade"`
|
||||
}
|
||||
|
||||
// Use the method to get the product turnover rate and the number of days the current stock will last.
|
||||
//
|
||||
// If you request a list of products by sku, the limit and offset parameters are optional.
|
||||
func (c Analytics) GetProductTurnover(ctx context.Context, params *GetProductTurnoverParams) (*GetProductTurnoverResponse, error) {
|
||||
url := "/v1/analytics/turnover/stocks"
|
||||
|
||||
resp := &GetProductTurnoverResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetStockManagementParams struct {
|
||||
// GetStockManagementFilter
|
||||
Filter GetStockManagementFilter `json:"filter"`
|
||||
|
||||
// Number of values in the response
|
||||
Limit int32 `json:"limit,omitempty"`
|
||||
|
||||
// Number of elements to skip in the response
|
||||
Offset int32 `json:"offset,omitempty"`
|
||||
}
|
||||
|
||||
type GetStockManagementFilter struct {
|
||||
// Product identifiers in the Ozon system, SKU
|
||||
SKUs []string `json:"skus"`
|
||||
|
||||
// The type of item in stock
|
||||
StockTypes string `json:"stock_types"`
|
||||
|
||||
// Warehouse identifiers
|
||||
WarehouseIds []string `json:"warehouse_ids"`
|
||||
}
|
||||
|
||||
type GetStockManagementResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Products
|
||||
Items []StockItem `json:"items"`
|
||||
}
|
||||
|
||||
type StockItem struct {
|
||||
// Stock of defective products, pcs
|
||||
DefectCount int64 `json:"defect_stock_count"`
|
||||
|
||||
// Stock of near-expired products, pcs
|
||||
ExpiringCount int64 `json:"expiring_stock_count"`
|
||||
|
||||
// Product name
|
||||
ProductName string `json:"name"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Stock of valid products
|
||||
ValidCount int64 `json:"valid_stock_count"`
|
||||
|
||||
// Stock of products that waiting for documents
|
||||
WaitingDocsCount int64 `json:"waitingdocs_stock_count"`
|
||||
|
||||
// Warehouse name
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
// Use the method to find out how many product items are left in stock
|
||||
func (c Analytics) Stock(ctx context.Context, params *GetStockManagementParams) (*GetStockManagementResponse, error) {
|
||||
url := "/v1/analytics/manage/stocks"
|
||||
|
||||
resp := &GetStockManagementResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
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"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetAnalyticsData(t *testing.T) {
|
||||
@@ -145,3 +145,206 @@ func TestGetStocksOnWarehouses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProductTurnover(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetProductTurnoverParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetProductTurnoverParams{
|
||||
Limit: 1,
|
||||
SKU: []string{"string"},
|
||||
},
|
||||
`{
|
||||
"items": [
|
||||
{
|
||||
"ads": 0,
|
||||
"current_stock": 0,
|
||||
"idc": 0,
|
||||
"idc_grade": "GRADES_NONE"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetProductTurnoverParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Analytics().GetProductTurnover(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetProductTurnoverResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Items) > int(test.params.Limit) {
|
||||
t.Errorf("Length of items is bigger than limit")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetStockManagementParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetStockManagementParams{
|
||||
Limit: 1,
|
||||
Offset: 0,
|
||||
Filter: GetStockManagementFilter{
|
||||
StockTypes: "STOCK_TYPE_VALID",
|
||||
SKUs: []string{
|
||||
"string",
|
||||
},
|
||||
},
|
||||
},
|
||||
`{
|
||||
"items": [
|
||||
{
|
||||
"defect_stock_count": 0,
|
||||
"expiring_stock_count": 0,
|
||||
"name": "string",
|
||||
"offer_id": "string",
|
||||
"sku": 0,
|
||||
"valid_stock_count": 0,
|
||||
"waitingdocs_stock_count": 0,
|
||||
"warehouse_name": "string"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetStockManagementParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Analytics().Stock(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetStockManagementResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Barcodes struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGenerateBarcodes(t *testing.T) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Brands struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListCertifiedBrands(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Cancellations struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetCancellationInfo(t *testing.T) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Categories struct {
|
||||
@@ -224,3 +224,63 @@ func (c *Categories) AttributesDictionary(ctx context.Context, params *GetAttrib
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type SearchAttributeDictionaryParams struct {
|
||||
// Characteristics identifier
|
||||
AttributeId int64 `json:"attribute_id"`
|
||||
|
||||
// Category identifier
|
||||
DescriptionCategoryId int64 `json:"description_category_id"`
|
||||
|
||||
// The value to be searched for
|
||||
// - minimum—2 characters
|
||||
Value string `json:"value"`
|
||||
|
||||
// Number of values in the response:
|
||||
//
|
||||
// - maximum—100,
|
||||
// - minimum—1.
|
||||
Limit int64 `json:"limit,omitempty"`
|
||||
|
||||
// Product type identifier
|
||||
TypeId int64 `json:"type_id"`
|
||||
}
|
||||
|
||||
type SearchAttributeDictionaryResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Characteristic values
|
||||
Result []SearchAttributeDictionaryResult `json:"result"`
|
||||
}
|
||||
|
||||
type SearchAttributeDictionaryResult struct {
|
||||
// Characteristic value identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Additional description
|
||||
Info string `json:"info"`
|
||||
|
||||
// Image link
|
||||
Picture string `json:"picture"`
|
||||
|
||||
// Product characteristic value
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// Returns found characteristics value directory.
|
||||
//
|
||||
// To check if an attribute has a nested directory,
|
||||
// use the `/v1/description-category/attribute` method.
|
||||
func (c *Categories) SearchAttributesDictionary(ctx context.Context, params *SearchAttributeDictionaryParams) (*SearchAttributeDictionaryResponse, error) {
|
||||
url := "/v1/description-category/attribute/values/search"
|
||||
|
||||
resp := &SearchAttributeDictionaryResponse{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetProductTree(t *testing.T) {
|
||||
@@ -203,3 +203,71 @@ func TestGetAttributeDictionary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchAttributeDictionary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *SearchAttributeDictionaryParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&SearchAttributeDictionaryParams{
|
||||
AttributeId: 123456,
|
||||
DescriptionCategoryId: 12,
|
||||
Value: "34",
|
||||
Limit: 5,
|
||||
TypeId: 6,
|
||||
},
|
||||
`{
|
||||
"has_next": true,
|
||||
"result": [
|
||||
{
|
||||
"id": 0,
|
||||
"info": "string",
|
||||
"picture": "string",
|
||||
"value": "string"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&SearchAttributeDictionaryParams{},
|
||||
`{
|
||||
"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.Categories().SearchAttributesDictionary(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetAttributeDictionaryResponse{})
|
||||
|
||||
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) > int(test.params.Limit) {
|
||||
t.Errorf("Length of response result is bigger than limit")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Certificates struct {
|
||||
@@ -99,11 +99,6 @@ type ListOfCertifiedCategoriesParams struct {
|
||||
type ListOfCertifiedCategoriesResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result ListOfCertifiedCategoriesResult `json:"result"`
|
||||
}
|
||||
|
||||
type ListOfCertifiedCategoriesResult struct {
|
||||
// Certified categories details
|
||||
Certification []ListOfCertifiedCategoriesResultCert `json:"certification"`
|
||||
|
||||
@@ -112,16 +107,25 @@ type ListOfCertifiedCategoriesResult struct {
|
||||
}
|
||||
|
||||
type ListOfCertifiedCategoriesResultCert struct {
|
||||
// Identifier of the certified category
|
||||
CategoryId int64 `json:"category_id"`
|
||||
|
||||
// Category name
|
||||
CategoryName string `json:"category_name"`
|
||||
|
||||
// Indication of a mandatory category
|
||||
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
|
||||
func (c Certificates) ListOfCertifiedCategories(ctx context.Context, params *ListOfCertifiedCategoriesParams) (*ListOfCertifiedCategoriesResponse, error) {
|
||||
url := "/v1/product/certification/list"
|
||||
url := "/v2/product/certification/list"
|
||||
|
||||
resp := &ListOfCertifiedCategoriesResponse{}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListOfAccordanceTypes(t *testing.T) {
|
||||
@@ -151,15 +151,16 @@ func TestListOfCertifiedCategories(t *testing.T) {
|
||||
PageSize: 100,
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"certification": [
|
||||
{
|
||||
"is_required": true,
|
||||
"category_name": "Витаминно-минеральные комплексы для взрослых"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
"certification": [
|
||||
{
|
||||
"category_id": 0,
|
||||
"category_name": "string",
|
||||
"is_required": true,
|
||||
"type_id": 0,
|
||||
"type_name": "string"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Chats struct {
|
||||
@@ -39,7 +39,7 @@ type ListChatsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Chats data
|
||||
Chats []ListChatsChat `json:"chats"`
|
||||
Chats []ListChatsChatData `json:"chats"`
|
||||
|
||||
// Total number of chats
|
||||
TotalChatsCount int64 `json:"total_chats_count"`
|
||||
@@ -48,20 +48,6 @@ type ListChatsResponse struct {
|
||||
TotalUnreadCount int64 `json:"total_unread_count"`
|
||||
}
|
||||
|
||||
type ListChatsChat struct {
|
||||
// Chat data
|
||||
Chat ListChatsChatData `json:"chat"`
|
||||
|
||||
// Identifier of the first unread chat message
|
||||
FirstUnreadMessageId string `json:"first_unread_message_id"`
|
||||
|
||||
// Identifier of the last message in the chat
|
||||
LastMessageId string `json:"last_message_id"`
|
||||
|
||||
// Number of unread messages in the chat
|
||||
UnreadCount int64 `json:"unread_count"`
|
||||
}
|
||||
|
||||
type ListChatsChatData struct {
|
||||
// Chat identifier
|
||||
ChatId string `json:"chat_id"`
|
||||
@@ -79,6 +65,15 @@ type ListChatsChatData struct {
|
||||
|
||||
// Chat creation date
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// Identifier of the first unread chat message
|
||||
FirstUnreadMessageId uint64 `json:"first_unread_message_id"`
|
||||
|
||||
// Identifier of the last message in the chat
|
||||
LastMessageId uint64 `json:"last_message_id"`
|
||||
|
||||
// Number of unread messages in the chat
|
||||
UnreadCount int64 `json:"unread_count"`
|
||||
}
|
||||
|
||||
// Returns information about chats by specified filters
|
||||
@@ -169,6 +164,8 @@ type ChatHistoryParams struct {
|
||||
// The default value is `Backward`. You can set the number of messages in the limit parameter
|
||||
Direction string `json:"direction" default:"Backward"`
|
||||
|
||||
Filter *ChatHistoryFilter `json:"filter,omitempty"`
|
||||
|
||||
// Identifier of the message from which the chat history will be displayed.
|
||||
// Default value is the last visible message
|
||||
FromMessageId string `json:"from_message_id"`
|
||||
@@ -177,6 +174,10 @@ type ChatHistoryParams struct {
|
||||
Limit int64 `json:"limit" default:"50"`
|
||||
}
|
||||
|
||||
type ChatHistoryFilter struct {
|
||||
MessageIds []string `json:"message_ids"`
|
||||
}
|
||||
|
||||
type ChatHistoryResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
@@ -188,22 +189,33 @@ type ChatHistoryResponse struct {
|
||||
}
|
||||
|
||||
type ChatHistoryMessage struct {
|
||||
Context *ChatHistoryContext `json:"context,omitempty"`
|
||||
|
||||
// Message creation date
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// Array with message content in Markdown format
|
||||
Data []string `json:"data"`
|
||||
|
||||
IsImage bool `json:"is_image"`
|
||||
|
||||
// Indication of the read message
|
||||
IsRead bool `json:"is_read"`
|
||||
|
||||
// Message identifier
|
||||
MessageId string `json:"message_id"`
|
||||
|
||||
ModarateImageStatus string `json:"moderate_image_status"`
|
||||
|
||||
// Chat participant identifier
|
||||
User ChatHistoryMessageUser `json:"user"`
|
||||
}
|
||||
|
||||
type ChatHistoryContext struct {
|
||||
OrderNumber string `json:"order_number"`
|
||||
SKU string `json:"sku"`
|
||||
}
|
||||
|
||||
type ChatHistoryMessageUser struct {
|
||||
// Chat participant identifier
|
||||
Id string `json:"id"`
|
||||
@@ -219,7 +231,7 @@ type ChatHistoryMessageUser struct {
|
||||
|
||||
// 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) {
|
||||
url := "/v2/chat/history"
|
||||
url := "/v3/chat/history"
|
||||
|
||||
resp := &ChatHistoryResponse{}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListChats(t *testing.T) {
|
||||
@@ -32,15 +32,13 @@ func TestListChats(t *testing.T) {
|
||||
`{
|
||||
"chats": [
|
||||
{
|
||||
"chat": {
|
||||
"created_at": "2022-07-22T08:07:19.581Z",
|
||||
"chat_id": "5e767w03-b400-4y1b-a841-75319ca8a5c8",
|
||||
"chat_status": "Opened",
|
||||
"chat_type": "Seller_Support"
|
||||
},
|
||||
"first_unread_message_id": "3000000000118021931",
|
||||
"last_message_id": "30000000001280042740",
|
||||
"unread_count": 1
|
||||
"chat_id": "5e767w03-b400-4y1b-a841-75319ca8a5c8",
|
||||
"chat_status": "Opened",
|
||||
"chat_type": "Seller_Support",
|
||||
"created_at": "2022-07-22T08:07:19.581Z",
|
||||
"unread_count": 1,
|
||||
"last_message_id": 3000000000128004274,
|
||||
"first_unread_message_id": 3000000000118021931
|
||||
}
|
||||
],
|
||||
"total_chats_count": 25,
|
||||
@@ -77,10 +75,10 @@ func TestListChats(t *testing.T) {
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Chats) > 0 {
|
||||
if resp.Chats[0].Chat.ChatStatus == "" {
|
||||
if resp.Chats[0].ChatStatus == "" {
|
||||
t.Errorf("Chat status cannot be empty")
|
||||
}
|
||||
if resp.Chats[0].Chat.ChatType == "" {
|
||||
if resp.Chats[0].ChatType == "" {
|
||||
t.Errorf("Chat type cannot be empty")
|
||||
}
|
||||
}
|
||||
@@ -214,16 +212,22 @@ func TestChatHistory(t *testing.T) {
|
||||
"has_next": true,
|
||||
"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",
|
||||
"moderate_image_status": "SUCCESS",
|
||||
"user": {
|
||||
"id": "115568",
|
||||
"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) телефона?"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
|
||||
74
ozon/clusters.go
Normal file
74
ozon/clusters.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Clusters struct {
|
||||
client *core.Client
|
||||
}
|
||||
|
||||
type ListClustersParams struct {
|
||||
// Clusters identifiers
|
||||
ClusterIds []string `json:"cluster_ids"`
|
||||
|
||||
// Cluster type
|
||||
ClusterType string `json:"cluster_type"`
|
||||
}
|
||||
|
||||
type ListClustersResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Cluster details
|
||||
Clusters []Cluster `json:"clusters"`
|
||||
}
|
||||
|
||||
type Cluster struct {
|
||||
// Cluster identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Cluster warehouse details
|
||||
LogisticClusters []LogisticCluster `json:"logistic_clusters"`
|
||||
|
||||
// Cluster name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Cluster type
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LogisticCluster struct {
|
||||
// Warehouse status
|
||||
IsArchived bool `json:"is_archived"`
|
||||
|
||||
// Warehouses
|
||||
Warehouses []LogisticClusterWarehouse `json:"warehouses"`
|
||||
}
|
||||
|
||||
type LogisticClusterWarehouse struct {
|
||||
// Warehouse name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Warehouse type
|
||||
Type string `json:"type"`
|
||||
|
||||
// Warehouse identifier
|
||||
Id int64 `json:"warehouse_id"`
|
||||
}
|
||||
|
||||
func (c Clusters) List(ctx context.Context, params *ListClustersParams) (*ListClustersResponse, error) {
|
||||
url := "/v1/cluster/list"
|
||||
|
||||
resp := &ListClustersResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
78
ozon/clusters_test.go
Normal file
78
ozon/clusters_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListClusters(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListClustersParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListClustersParams{
|
||||
ClusterIds: []string{"string"},
|
||||
ClusterType: "CLUSTER_TYPE_UNKNOWN",
|
||||
},
|
||||
`{
|
||||
"clusters": [
|
||||
{
|
||||
"id": 0,
|
||||
"logistic_clusters": [
|
||||
{
|
||||
"is_archived": true,
|
||||
"warehouses": [
|
||||
{
|
||||
"name": "string",
|
||||
"type": "FULL_FILLMENT",
|
||||
"warehouse_id": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "string",
|
||||
"type": "CLUSTER_TYPE_UNKNOWN"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListClustersParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Clusters().List(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListClustersResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
149
ozon/common.go
149
ozon/common.go
@@ -126,24 +126,6 @@ const (
|
||||
type SupplyRequestState string
|
||||
|
||||
const (
|
||||
// request draft. Only for supplies via vDC
|
||||
Draft SupplyRequestState = "DRAFT"
|
||||
|
||||
// selecting supply options. Only for supplies via vDC
|
||||
SupplyVariantsArranging SupplyRequestState = "SUPPLY_VARIANTS_ARRANGING"
|
||||
|
||||
// no supply options, the request is archived. Only for supplies via vDC
|
||||
HasNoSupplyVariantsArchive SupplyRequestState = "HAS_NO_SUPPLY_VARIANTS_ARCHIVE"
|
||||
|
||||
// no supply options. Only for supplies via vDC
|
||||
HasNoSupplyVariantsNew SupplyRequestState = "HAS_NO_SUPPLY_VARIANTS_NEW"
|
||||
|
||||
// supply being approved. Only for supplies via vDC
|
||||
SupplyVariantsConfirmation SupplyRequestState = "SUPPLY_VARIANTS_CONFIRMATION"
|
||||
|
||||
// time reservation
|
||||
TimeslotBooking SupplyRequestState = "TIMESLOT_BOOKING"
|
||||
|
||||
// filling in the data
|
||||
DATA_FILLING SupplyRequestState = "DATA_FILLING"
|
||||
|
||||
@@ -351,7 +333,7 @@ const (
|
||||
ServiceProcessingIdentifiedSurplus DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedSurplus"
|
||||
ServiceProcessingIdentifiedDiscrepancies DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedDiscrepancies"
|
||||
ServiceItemInternetSiteAdvertising DetailsServiceItemName = "MarketplaceServiceItemInternetSiteAdvertising"
|
||||
ServiceItemPremiumSubscribtion DetailsServiceItemName = "MarketplaceServiceItemPremiumSubscribtion"
|
||||
ServiceItemPremiumSubscribtion DetailsServiceItemName = "MarketplaceServiceItemSubscribtionPremium"
|
||||
AgencyFeeAggregator3PLGlobalItem DetailsServiceItemName = "MarketplaceAgencyFeeAggregator3PLGlobalItem"
|
||||
)
|
||||
|
||||
@@ -471,6 +453,8 @@ const (
|
||||
ReadyForShipment GetFBSReturnsFilterStatus = "ready_for_shipment"
|
||||
Disposing GetFBSReturnsFilterStatus = "disposing"
|
||||
Disposed GetFBSReturnsFilterStatus = "disposed"
|
||||
ArrivedForResale GetFBSReturnsFilterStatus = "arrived_for_resale"
|
||||
MovingToResale GetFBSReturnsFilterStatus = "moving_to_resale"
|
||||
)
|
||||
|
||||
type GetFBOReturnsFilterStatus string
|
||||
@@ -577,6 +561,12 @@ const (
|
||||
|
||||
// financial report
|
||||
ReportTypeSellerFinance ReportType = "SELLER_FINANCE"
|
||||
|
||||
// report on sales to legal entities
|
||||
ReportTypeDocB2BSales ReportType = "DOCUMENT_B2B_SALES"
|
||||
|
||||
// settlement report
|
||||
ReportTypeMutualSettlement ReportType = "MUTUAL_SETTLEMENT"
|
||||
)
|
||||
|
||||
type ReportInfoStatus string
|
||||
@@ -740,6 +730,8 @@ const (
|
||||
// picking up products for removal by the seller
|
||||
TransactionServiceReturnFromStock TransactionOperationService = "MarketplaceServiceItemReturnFromStock"
|
||||
|
||||
TransactionServiceStarsMembership TransactionOperationService = "ItemAgentServiceStarsMembership"
|
||||
|
||||
// forwarding trade
|
||||
TransactionItemAdForSupplierLogisticSeller TransactionOperationService = "ItemAdvertisementForSupplierLogisticSeller"
|
||||
|
||||
@@ -808,6 +800,9 @@ const (
|
||||
|
||||
// reissue of returns at the pick-up point
|
||||
TransactionServiceRedistributionReturnsPVZ TransactionOperationService = "MarketplaceServiceItemRedistributionReturnsPVZ"
|
||||
|
||||
// Agregator 3PL Globalagency service tariffication
|
||||
TransactionServiceAgencyFeeAggregator3PLGlobal TransactionOperationService = "OperationMarketplaceAgencyFeeAggregator3PLGlobal"
|
||||
)
|
||||
|
||||
type PaymentTypeGroupName string
|
||||
@@ -822,3 +817,119 @@ const (
|
||||
PaymentTypeGroupPaymentToCurrentAccount PaymentTypeGroupName = "payment to current account"
|
||||
PaymentTypeGroupSberpay PaymentTypeGroupName = "Sberpay"
|
||||
)
|
||||
|
||||
type VisualStatus string
|
||||
|
||||
const (
|
||||
// dispute with the customer has been opened
|
||||
VisualStatusDisputeOpened VisualStatus = "DisputeOpened"
|
||||
|
||||
// pending with the seller
|
||||
VisualStatusOnSellerApproval VisualStatus = "OnSellerApproval"
|
||||
|
||||
// at the pick-up point
|
||||
VisualStatusArrivedAtReturnPlace VisualStatus = "ArrivedAtReturnPlace"
|
||||
|
||||
// pending clarification by the seller
|
||||
VisualStatusOnSellerClarification VisualStatus = "OnSellerClarification"
|
||||
|
||||
// pending clarification by the seller after partial compensation
|
||||
VisualStatusOnSellerClarificationPartial VisualStatus = "OnSellerClarificationAfterPartialCompensation"
|
||||
|
||||
// partial compensation offered
|
||||
VisualStatusOfferedPartial VisualStatus = "OfferedPartialCompensation"
|
||||
|
||||
// refund approved
|
||||
VisualStatusReturnMoneyApproved VisualStatus = "ReturnMoneyApproved"
|
||||
|
||||
// partial compensation provided
|
||||
VisualStatusPartialReturned VisualStatus = "PartialCompensationReturned"
|
||||
|
||||
// refund rejected, dispute isn't opened
|
||||
VisualStatusCancelledDisputeNotOpen VisualStatus = "CancelledDisputeNotOpen"
|
||||
|
||||
// request rejected
|
||||
VisualStatusRejected VisualStatus = "Rejected"
|
||||
|
||||
// request rejected by Ozon
|
||||
VisualStatusCrmRejected VisualStatus = "CrmRejected"
|
||||
|
||||
// request canceled
|
||||
VisualStatusCancelled VisualStatus = "Cancelled"
|
||||
|
||||
// request approved by the seller
|
||||
VisualStatusApproved VisualStatus = "Approved"
|
||||
|
||||
// request approved by Ozon
|
||||
VisualStatusApprovedByOzon VisualStatus = "ApprovedByOzon"
|
||||
|
||||
// seller received the return
|
||||
VisualStatusReceivedBySeller VisualStatus = "ReceivedBySeller"
|
||||
|
||||
// return is on its way to the seller
|
||||
VisualStatusMovingToSeller VisualStatus = "MovingToSeller"
|
||||
|
||||
// seller received the refund
|
||||
VisualStatusReturnCompensated VisualStatus = "ReturnCompensated"
|
||||
|
||||
// courier is taking the return to the seller
|
||||
VisualStatusReturningByCourier VisualStatus = "ReturningByCourier"
|
||||
|
||||
// on disposal
|
||||
VisualStatusUtilizing VisualStatus = "Utilizing"
|
||||
|
||||
// disposed of
|
||||
VisualStatusUtilized VisualStatus = "Utilized"
|
||||
|
||||
// customer received full refund
|
||||
VisualStatusMoneyReturned VisualStatus = "MoneyReturned"
|
||||
|
||||
// partial refund has been approved
|
||||
VisualStatusPartialInProcess VisualStatus = "PartialCompensationInProcess"
|
||||
|
||||
// seller opened a dispute
|
||||
VisualStatusDisputeYouOpened VisualStatus = "DisputeYouOpened"
|
||||
|
||||
// compensation rejected
|
||||
VisualStatusCompensationRejected VisualStatus = "CompensationRejected"
|
||||
|
||||
// support request sent
|
||||
VisualStatusDisputeOpening VisualStatus = "DisputeOpening"
|
||||
|
||||
// awaiting your decision on compensation
|
||||
VisualStatusCompensationOffered VisualStatus = "CompensationOffered"
|
||||
|
||||
// awaiting compensation
|
||||
VisualStatusWaitingCompensation VisualStatus = "WaitingCompensation"
|
||||
|
||||
// an error occurred when sending the support request
|
||||
VisualStatusSendingError VisualStatus = "SendingError"
|
||||
|
||||
// decision period has expired
|
||||
VisualStatusCompensationRejectedBySla VisualStatus = "CompensationRejectedBySla"
|
||||
|
||||
// seller has refused compensation
|
||||
VisualStatusCompensationRejectedBySeller VisualStatus = "CompensationRejectedBySeller"
|
||||
|
||||
// on the way to the Ozon warehouse
|
||||
VisualStatusMovingToOzon VisualStatus = "MovingToOzon"
|
||||
|
||||
// arrived at the Ozon warehouse
|
||||
VisualStatusReturnedToOzon VisualStatus = "ReturnedToOzon"
|
||||
|
||||
// quick refund
|
||||
VisualStatusMoneyReturnedBySystem VisualStatus = "MoneyReturnedBySystem"
|
||||
|
||||
// awaiting shipping
|
||||
VisualStatusWaitingShipment VisualStatus = "WaitingShipment"
|
||||
)
|
||||
|
||||
type VAT string
|
||||
|
||||
const (
|
||||
VAT0 VAT = "0"
|
||||
VAT005 VAT = "0.05"
|
||||
VAT007 VAT = "0.07"
|
||||
VAT01 VAT = "0.1"
|
||||
VAT02 VAT = "0.2"
|
||||
)
|
||||
|
||||
965
ozon/fbo.go
965
ozon/fbo.go
File diff suppressed because it is too large
Load Diff
1024
ozon/fbo_test.go
1024
ozon/fbo_test.go
File diff suppressed because it is too large
Load Diff
686
ozon/fbs.go
686
ozon/fbs.go
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type FBS struct {
|
||||
@@ -53,6 +53,11 @@ type ListUnprocessedShipmentsFilter struct {
|
||||
// Delivery method identifier
|
||||
DeliveryMethodId []int64 `json:"delivery_method_id"`
|
||||
|
||||
// Specify true to get only MOQ shipments.
|
||||
//
|
||||
// The default value is false, the response contains all shipments
|
||||
IsQuantum bool `json:"is_quantum"`
|
||||
|
||||
// Filter for shipments delivered from partner warehouse (FBP). You can pass one of the following values:
|
||||
//
|
||||
// Default value is all.
|
||||
@@ -106,6 +111,9 @@ type FBSPosting struct {
|
||||
// Analytics data
|
||||
AnalyticsData FBSPostingAnalyticsData `json:"analytics_data"`
|
||||
|
||||
// Available actions and shipment information
|
||||
AvailableActions []string `json:"available_actions"`
|
||||
|
||||
// Shipment barcodes
|
||||
Barcodes FBSBarcode `json:"barcodes"`
|
||||
|
||||
@@ -146,6 +154,9 @@ type FBSPosting struct {
|
||||
// Number of the parent shipment which split resulted in the current shipment
|
||||
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
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
@@ -178,6 +189,52 @@ type FBSPosting struct {
|
||||
|
||||
// Shipment tracking number
|
||||
TrackingNumber string `json:"tracking_number"`
|
||||
|
||||
// Details on shipping rate
|
||||
Tariffication []FBSPostingTariffication `json:"tariffication"`
|
||||
|
||||
// Economy product identifier
|
||||
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 {
|
||||
// Current shipping rate as a percentage
|
||||
CurrentTariffRate float64 `json:"current_tariff_rate"`
|
||||
|
||||
// Type of shipping rate adjustment: discount or surcharge
|
||||
CurrentTariffType string `json:"current_tariff_type"`
|
||||
|
||||
// Current amount of discount or surcharge
|
||||
CurrentTariffCharge string `json:"current_tariff_charge"`
|
||||
|
||||
// Currency of the amount
|
||||
CurrencyTariffCurrencyCode string `json:"current_tariff_charge_currency_code"`
|
||||
|
||||
// Percentage by which the shipping rate is adjusted
|
||||
// after the time specified in the next_tariff_starts_at parameter
|
||||
NextTariffRate float64 `json:"next_tariff_rate"`
|
||||
|
||||
// The adjustment type applied to the shipping rate
|
||||
// after the time specified in the next_tariff_starts_at parameter:
|
||||
// discount or surcharge
|
||||
NextTariffType string `json:"next_tariff_type"`
|
||||
|
||||
// Discount or surcharge amount applied during the next shipping rate adjustment step
|
||||
NextTariffCharge string `json:"next_tariff_charge"`
|
||||
|
||||
// Date and time when the new shipping rate is applied
|
||||
NextTariffStartsAt time.Time `json:"next_tariff_starts_at"`
|
||||
|
||||
// New shipping rate currency
|
||||
NextTariffCurrencyCode string `json:"next_tariff_charge_currency_code"`
|
||||
}
|
||||
|
||||
type FBSPostingAddressee struct {
|
||||
@@ -191,7 +248,7 @@ type FBSPostingAddressee struct {
|
||||
}
|
||||
|
||||
type FBSPostingAnalyticsData struct {
|
||||
// Delivery city
|
||||
// Delivery city. Only for rFBS shipments and sellers from CIS countries
|
||||
City string `json:"city"`
|
||||
|
||||
// Delivery start date and time
|
||||
@@ -215,7 +272,7 @@ type FBSPostingAnalyticsData struct {
|
||||
// Payment method
|
||||
PaymentTypeGroupName PaymentTypeGroupName `json:"payment_type_group_name"`
|
||||
|
||||
// Delivery region
|
||||
// Delivery region. Only for rFBS shipments
|
||||
Region string `json:"region"`
|
||||
|
||||
// Delivery service
|
||||
@@ -324,9 +381,6 @@ type FBSRequirements struct {
|
||||
}
|
||||
|
||||
type PostingProduct struct {
|
||||
// Mandatory product labeling
|
||||
MandatoryMark []string `json:"mandatory_mark"`
|
||||
|
||||
// Product name
|
||||
Name string `json:"name"`
|
||||
|
||||
@@ -344,6 +398,9 @@ type PostingProduct struct {
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Product traceability attribute
|
||||
IsBLRTraceable bool `json:"is_blr_traceable"`
|
||||
}
|
||||
|
||||
type FBSCustomer struct {
|
||||
@@ -541,6 +598,11 @@ type GetFBSShipmentsListFilter struct {
|
||||
// Order identifier
|
||||
OrderId int64 `json:"order_id"`
|
||||
|
||||
// Specify true to get only MOQ shipments.
|
||||
//
|
||||
// The default value is false, the response contains all shipments
|
||||
IsQuantum bool `json:"is_quantum"`
|
||||
|
||||
// Delivery service identifier
|
||||
ProviderId []int64 `json:"provider_id"`
|
||||
|
||||
@@ -715,7 +777,7 @@ type ValidateLabelingCodesExemplar struct {
|
||||
GTD string `json:"gtd"`
|
||||
|
||||
// Mandatory “Chestny ZNAK” labeling
|
||||
MandatoryMark string `json:"mandatory_mark"`
|
||||
Marks []SetProductItemsDataProductMark `json:"marks"`
|
||||
|
||||
// Product batch registration number
|
||||
RNPT string `json:"rnpt"`
|
||||
@@ -724,11 +786,6 @@ type ValidateLabelingCodesExemplar struct {
|
||||
type ValidateLabelingCodesResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result ValidateLabelingCodesResult `json:"result"`
|
||||
}
|
||||
|
||||
type ValidateLabelingCodesResult struct {
|
||||
// Products list
|
||||
Products []ValidateLabelingCodesResultProduct `json:"products"`
|
||||
}
|
||||
@@ -738,7 +795,7 @@ type ValidateLabelingCodesResultProduct struct {
|
||||
Error string `json:"error"`
|
||||
|
||||
// Product items data
|
||||
Exemplars []FBSProductExemplar `json:"exemplars"`
|
||||
Exemplars []ValidateLabelingCodesResultExemplar `json:"exemplars"`
|
||||
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
@@ -747,11 +804,43 @@ type ValidateLabelingCodesResultProduct struct {
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
type ValidateLabelingCodesResultExemplar struct {
|
||||
// Product item validation errors
|
||||
Errors []string `json:"errors"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) number
|
||||
GTD string `json:"gtd"`
|
||||
|
||||
// List of Control Identification Marks in one copy
|
||||
Marks []ValidateLabelingCodesMark `json:"marks"`
|
||||
|
||||
// Product batch registration number
|
||||
RNPT string `json:"rnpt"`
|
||||
|
||||
// Check result. true if the labeling codes of all product items meet the requirements
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
type ValidateLabelingCodesMark struct {
|
||||
// Errors that appeared during verification of Control Identification Marks
|
||||
Errors []string `json:"errors"`
|
||||
|
||||
// Labeling code meaning
|
||||
Mark string `json:"mark"`
|
||||
|
||||
// Labeling code type
|
||||
MarkType string `json:"mark_type"`
|
||||
|
||||
// Check result. true if the labeling
|
||||
// codes of all product items meet the requirements
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
// Method for checking whether labeling codes meet the "Chestny ZNAK" system requirements on length and symbols.
|
||||
//
|
||||
// If you don't have the customs cargo declaration (CCD) number, you don't have to specify it
|
||||
func (c FBS) ValidateLabelingCodes(ctx context.Context, params *ValidateLabelingCodesParams) (*ValidateLabelingCodesResponse, error) {
|
||||
url := "/v4/fbs/posting/product/exemplar/validate"
|
||||
url := "/v5/fbs/posting/product/exemplar/validate"
|
||||
|
||||
resp := &ValidateLabelingCodesResponse{}
|
||||
|
||||
@@ -912,6 +1001,9 @@ type GetShipmentDataByIdentifierResult struct {
|
||||
// Analytics data
|
||||
AnalyticsData GetShipmentDataByIdentifierResultAnalyticsData `json:"analytics_data"`
|
||||
|
||||
// Available actions and shipment information
|
||||
AvailableActions []string `json:"available_actions"`
|
||||
|
||||
// Shipment barcodes
|
||||
Barcodes FBSBarcode `json:"barcodes"`
|
||||
|
||||
@@ -959,9 +1051,15 @@ type GetShipmentDataByIdentifierResult struct {
|
||||
// Number of the parent shipment which split resulted in the current shipment
|
||||
ParentPostingNumber string `json:"parent_posting_number"`
|
||||
|
||||
// List of products with additional characteristics
|
||||
Optional GetShipmentDataByIdentifierOptional `json:"optional"`
|
||||
|
||||
// Shipment 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.
|
||||
//
|
||||
// The response contains the field product_exemplars, if the attribute with.product_exemplars = true is passed in the request
|
||||
@@ -973,6 +1071,9 @@ type GetShipmentDataByIdentifierResult struct {
|
||||
// Delivery service 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
|
||||
// with a delivery by a third-party or integrated service
|
||||
PRROption GetShipmentDataByIdentifierResultPRROption `json:"prr_option"`
|
||||
@@ -999,6 +1100,14 @@ type GetShipmentDataByIdentifierResult struct {
|
||||
|
||||
// Shipment tracking number
|
||||
TrackingNumber string `json:"tracking_number"`
|
||||
|
||||
// Details on shipping rate
|
||||
Tariffication []FBSPostingTariffication `json:"tariffication"`
|
||||
}
|
||||
|
||||
type GetShipmentDataByIdentifierOptional struct {
|
||||
// List of products with optional labeling
|
||||
ProductsWithPossibleMandatoryMark []int `json:"products_with_possible_mandatory_mark"`
|
||||
}
|
||||
|
||||
type GetShipmentDataByIdentifierResultAdditionalData struct {
|
||||
@@ -1020,7 +1129,7 @@ type GetShipmentDataByIdentifierResultAddressee struct {
|
||||
}
|
||||
|
||||
type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
||||
// Delivery city
|
||||
// Delivery city. Only for rFBS shipments and sellers from CIS countries
|
||||
City string `json:"city"`
|
||||
|
||||
// Delivery start date and time
|
||||
@@ -1043,7 +1152,7 @@ type GetShipmentDataByIdentifierResultAnalyticsData struct {
|
||||
// Payment method
|
||||
PaymentTypeGroupName string `json:"payment_type_group_name"`
|
||||
|
||||
// Delivery region
|
||||
// Delivery region. Only for rFBS shipments
|
||||
Region string `json:"region"`
|
||||
|
||||
// Delivery service
|
||||
@@ -1128,39 +1237,23 @@ type ProductDimension struct {
|
||||
}
|
||||
|
||||
type FBSProductExemplar struct {
|
||||
// Product item validation errors
|
||||
Errors []string `json:"errors"`
|
||||
// Item identifier
|
||||
ExemplarId int64 `json:"exemplar_id"`
|
||||
|
||||
// Mandatory “Chestny ZNAK” labeling
|
||||
MandatoryMark string `json:"mandatory_mark"`
|
||||
|
||||
// "Chestny ZNAK" labeling check status
|
||||
MandatoryMarkCheckStatus MandatoryMarkStatus `json:"mandatory_mark_check_status"`
|
||||
|
||||
// "Chestny ZNAK" labeling check error codes
|
||||
MandatoryMarkErrorCodes []string `json:"mandatory_mark_error_codes"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) number
|
||||
GTD string `json:"gtd"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) check status
|
||||
GTDCheckStatus string `json:"gtd_check_status"`
|
||||
|
||||
// Indication that a сustoms cargo declaration (CCD) number hasn't been specified
|
||||
IsGTDAbsest bool `json:"is_gtd_absent"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) check error codes
|
||||
GTDErrorCodes []string `json:"gtd_error_codes"`
|
||||
|
||||
// Product batch registration number
|
||||
RNPT string `json:"rnpt"`
|
||||
|
||||
// Indication that a product batch registration number hasn't been specified
|
||||
IsRNPTAbsent bool `json:"is_rnpt_absent"`
|
||||
|
||||
// Check result.
|
||||
// `true` if the labeling code of product item meets the requirements
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
// Method for getting shipment details by identifier
|
||||
@@ -1348,38 +1441,6 @@ func (c FBS) ListOfShipmentCertificates(ctx context.Context, params *ListOfShipm
|
||||
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 {
|
||||
// Shipment identifier
|
||||
PostingNumber []string `json:"posting_number"`
|
||||
@@ -1464,7 +1525,7 @@ func (c FBS) ChangeStatusToSendBySeller(ctx context.Context, params *ChangeStatu
|
||||
}
|
||||
|
||||
type PassShipmentToShippingParams struct {
|
||||
// Shipment identifier
|
||||
// Shipment identifier. The maximum number of values in one request is 100
|
||||
PostingNumber []string `json:"posting_number"`
|
||||
}
|
||||
|
||||
@@ -1769,7 +1830,7 @@ func (c FBS) GetDropOffPointRestrictions(ctx context.Context, params *GetDropOff
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type CheckProductItemsDataParams struct {
|
||||
type SetProductItemsDataParams struct {
|
||||
// Quantity of boxes the product is packed in
|
||||
MultiBoxQuantity int32 `json:"multi_box_qty"`
|
||||
|
||||
@@ -1777,20 +1838,26 @@ type CheckProductItemsDataParams struct {
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Product list
|
||||
Products []CheckProductItemsDataProduct `json:"products"`
|
||||
Products []SetProductItemsDataProduct `json:"products"`
|
||||
}
|
||||
|
||||
type CheckProductItemsDataProduct struct {
|
||||
type SetProductItemsDataProduct struct {
|
||||
// Product items data
|
||||
Exemplars []CheckProductItemsDataProductExemplar `json:"exemplars"`
|
||||
Exemplars []SetProductItemsDataProductExemplar `json:"exemplars"`
|
||||
|
||||
// Indication that you need to pass the сustoms cargo declaration
|
||||
// (CCD) number for the product and shipment
|
||||
IsGTDNeeded bool `json:"is_gtd_needed"`
|
||||
|
||||
// Indication that you need to pass the unique identifier of charges of the jewelry
|
||||
IsJwUINNeeded bool `json:"is_jw_uin_needed"`
|
||||
|
||||
// Indication that you need to pass the "Chestny ZNAK" labeling
|
||||
IsMandatoryMarkNeeded bool `json:"is_mandatory_mark_needed"`
|
||||
|
||||
// Indication that you can pass the "Chestny ZNAK" labeling, but it's not mandatory
|
||||
IsMandatoryMarkPossible bool `json:"is_mandatory_mark_possible"`
|
||||
|
||||
// Indication that you need to pass the product batch registration number
|
||||
IsRNPTNeeded bool `json:"is_rnpt_needed"`
|
||||
|
||||
@@ -1801,48 +1868,35 @@ type CheckProductItemsDataProduct struct {
|
||||
Quantity int32 `json:"quantity"`
|
||||
}
|
||||
|
||||
type CheckProductItemsDataProductExemplar struct {
|
||||
type SetProductItemsDataProductExemplar struct {
|
||||
// Item identifier
|
||||
ExemplarId int64 `json:"exemplar_id"`
|
||||
|
||||
// Customs cargo declaration (CCD) number
|
||||
GTD string `json:"gtd"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) check status
|
||||
GTDCheckStatus string `json:"gtd_check_status"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) check error codes
|
||||
GTDErrorCodes []string `json:"gtd_error_codes"`
|
||||
|
||||
// Indication that the customs cargo declaration (CCD) number isn't specified
|
||||
IsGTDAbsent bool `json:"is_gtd_absent"`
|
||||
|
||||
// "Chestny ZNAK" labeling check status
|
||||
MandatoryMarkCheckStatus MandatoryMarkStatus `json:"mandatory_mark_check_status"`
|
||||
|
||||
// "Chestny ZNAK" labeling check error codes
|
||||
MandatoryMarkErrorCodes []string `json:"mandatory_mark_error_codes"`
|
||||
|
||||
// Indication that the product batch registration number isn't specified
|
||||
IsRNPTAbsent bool `json:"is_rnpt_absent"`
|
||||
|
||||
// Mandatory "Chestny ZNAK" labeling
|
||||
MandatoryMark string `json:"mandatory_mark"`
|
||||
|
||||
// Product batch registration number
|
||||
RNPT string `json:"rnpt"`
|
||||
|
||||
// Product batch registration number check status
|
||||
RNPTCheckStatus string `json:"rnpt_check_status"`
|
||||
|
||||
// Product batch registration number check error codes
|
||||
RNPTErrorCodes []string `json:"rnpt_error_codes"`
|
||||
|
||||
// Unique identifier of charges of the jewelry
|
||||
JWUIN string `json:"jw_uin"`
|
||||
// Errors that appeared during verification of Control Identification Marks
|
||||
Marks []SetProductItemsDataProductMark `json:"marks"`
|
||||
}
|
||||
|
||||
type CheckProductItemsDataResponse struct {
|
||||
type SetProductItemsDataProductMark struct {
|
||||
// Labeling code meaning
|
||||
Mark string `json:"mark"`
|
||||
|
||||
// Labeling code type
|
||||
MarkType string `json:"mark_type"`
|
||||
}
|
||||
|
||||
type SetProductItemsDataResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result. true if the request was processed successfully
|
||||
@@ -1851,39 +1905,21 @@ type CheckProductItemsDataResponse struct {
|
||||
|
||||
// Asynchronous method:
|
||||
//
|
||||
// for checking the availability of product items in the “Chestny ZNAK” labeling system;
|
||||
// for saving product items data.
|
||||
// for checking the availability of product items in the “Chestny ZNAK” labeling system;
|
||||
// for saving product items data.
|
||||
// To get the checks results, use the /v5/fbs/posting/product/exemplar/status method. To get data about created items, use the /v6/fbs/posting/product/exemplar/create-or-get method.
|
||||
//
|
||||
// To get the checks results,
|
||||
// use the /v4/fbs/posting/product/exemplar/status method.
|
||||
// To get data about created items,
|
||||
// use the /v5/fbs/fbs/posting/product/exemplar/create-or-get method.
|
||||
//
|
||||
// If necessary, specify the number of the cargo customs declaration
|
||||
// in the gtd parameter. If it is missing,
|
||||
// pass the value is_gtd_absent = true.
|
||||
//
|
||||
// If you have multiple identical products in a shipment,
|
||||
// specify one product_id and exemplars array for each product in the shipment.
|
||||
// If you have multiple identical products in a shipment, specify one product_id and exemplars array for each product in the shipment.
|
||||
//
|
||||
// Always pass a complete set of product items data.
|
||||
//
|
||||
// For example, you have 10 product items in your system.
|
||||
// You've passed them for checking and saving.
|
||||
// Then you added another 60 product items to your system.
|
||||
// When you pass product items for checking and saving again,
|
||||
// pass all of them: both old and newly added.
|
||||
// For example, you have 10 product items in your system. You pass them for checking and saving. Then you add another 60 product items to your system. When you pass product items for checking and saving again, pass all of them: both old and newly added.
|
||||
//
|
||||
// Unlike /v4/fbs/posting/product/exemplar/set,
|
||||
// you can pass more item information in the request.
|
||||
//
|
||||
// The 200 response code doesn't guarantee that instance data has been received.
|
||||
// It indicates that a task for adding the information has been created.
|
||||
// To check the task status, use the /v4/fbs/posting/product/exemplar/status method.
|
||||
func (c FBS) CheckProductItemsData(ctx context.Context, params *CheckProductItemsDataParams) (*CheckProductItemsDataResponse, error) {
|
||||
url := "/v5/fbs/posting/product/exemplar/set"
|
||||
// The 200 response code doesn't guarantee that instance data has been received. It indicates that a task for adding the information has been created. To check the task status, use the /v5/fbs/posting/product/exemplar/status method.
|
||||
func (c FBS) SetProductItemsData(ctx context.Context, params *SetProductItemsDataParams) (*SetProductItemsDataResponse, error) {
|
||||
url := "/v6/fbs/posting/product/exemplar/set"
|
||||
|
||||
resp := &CheckProductItemsDataResponse{}
|
||||
resp := &SetProductItemsDataResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
@@ -1906,19 +1942,71 @@ type GetProductItemsCheckStatusesResponse struct {
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Products list
|
||||
Products []CheckProductItemsDataProduct `json:"products"`
|
||||
Products []GetProductItemsCheckStatusProduct `json:"products"`
|
||||
|
||||
// Product items check statuses and order collection availability:
|
||||
// - ship_available — order collection is available,
|
||||
// - ship_not_available — order collection is unavailable,
|
||||
// - validation_in_process — product items validation is in progress
|
||||
// Product items check statuses and order collection availability
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// Method for getting check statuses of product items that were passed in the `/fbs/posting/product/exemplar/set` method.
|
||||
type GetProductItemsCheckStatusProduct struct {
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Product items data
|
||||
Exemplars []GetProductItemsCheckStatusExemplar `json:"exemplars"`
|
||||
}
|
||||
|
||||
type GetProductItemsCheckStatusExemplar struct {
|
||||
// Item identifier
|
||||
ExemplarId int64 `json:"exemplar_id"`
|
||||
|
||||
// Customs cargo declaration (CCD) number
|
||||
GTD string `json:"gtd"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) check status
|
||||
GTDCheckStatus string `json:"gtd_check_status"`
|
||||
|
||||
// Сustoms cargo declaration (CCD) check error codes
|
||||
GTDErrorCodes []string `json:"gtd_error_codes"`
|
||||
|
||||
// Indication that the customs cargo declaration (CCD) number isn't specified
|
||||
IsGTDAbsent bool `json:"is_gtd_absent"`
|
||||
|
||||
// Indication that the product batch registration number isn't specified
|
||||
IsRNPTAbsent bool `json:"is_rnpt_absent"`
|
||||
|
||||
// List of Control Identification Marks in one copy
|
||||
Marks []GetProductItemsCheckStatusMark `json:"marks"`
|
||||
|
||||
// Product batch registration number
|
||||
RNPT string `json:"rnpt"`
|
||||
|
||||
// Product batch registration number check status
|
||||
RNPTCheckStatus string `json:"rnpt_check_status"`
|
||||
|
||||
// Product batch registration number check error codes
|
||||
RNPTErrorCodes []string `json:"rnpt_error_codes"`
|
||||
}
|
||||
|
||||
type GetProductItemsCheckStatusMark struct {
|
||||
// Check status
|
||||
CheckStatus string `json:"check_status"`
|
||||
|
||||
// Errors that appeared during verification of Control Identification Marks
|
||||
ErrorCodes []string `json:"error_codes"`
|
||||
|
||||
// Labeling code meaning
|
||||
Mark string `json:"mark"`
|
||||
|
||||
// Labeling code type
|
||||
MarkType string `json:"mark_type"`
|
||||
}
|
||||
|
||||
// Method for getting product items addition statuses
|
||||
// that were passed in the /v6/fbs/posting/product/exemplar/set method.
|
||||
// Also returns data on these product items.
|
||||
func (c FBS) GetProductItemsCheckStatuses(ctx context.Context, params *GetProductItemsCheckStatusesParams) (*GetProductItemsCheckStatusesResponse, error) {
|
||||
url := "/v4/fbs/posting/product/exemplar/status"
|
||||
url := "/v5/fbs/posting/product/exemplar/status"
|
||||
|
||||
resp := &GetProductItemsCheckStatusesResponse{}
|
||||
|
||||
@@ -2188,6 +2276,12 @@ type AvailableFreightsListResult struct {
|
||||
// Number of already packaged shipments
|
||||
MandatoryPackagedCount int32 `json:"mandatory_packaged_count"`
|
||||
|
||||
// Recommended local time of shipping to the pick-up point
|
||||
RecommendedTimeLocal string `json:"recommended_time_local"`
|
||||
|
||||
// Time zone offset of the recommended shipping time from UTC-0 in minutes
|
||||
RecommendedTimeUTCOffset int32 `json:"recommended_time_utc_offset_in_minutes"`
|
||||
|
||||
// Delivery service icon link
|
||||
TPLProviderIconURL string `json:"tpl_provider_icon_url"`
|
||||
|
||||
@@ -2525,20 +2619,11 @@ type CancelSendingResponse struct {
|
||||
|
||||
// Use this method if you cannot send some of the products from the shipment.
|
||||
//
|
||||
// If you are using the rFBS scheme, you have the following cancellation reason identifiers (`cancel_reason_id`) available:
|
||||
//
|
||||
// 352—product is out of stock;
|
||||
// 400—only defective products left;
|
||||
// 401—cancellation from arbitration;
|
||||
// 402—other reason;
|
||||
// 665—the customer did not pick the order;
|
||||
// 666—delivery is not available in the region;
|
||||
// 667—order was lost by the delivery service.
|
||||
// The last 4 reasons are available for shipments in the "Delivering" and "Courier on the way" statuses.
|
||||
// To get the cancel_reason_id cancellation reason identifiers
|
||||
// when working with the FBS or rFBS schemes,
|
||||
// use the /v2/posting/fbs/cancel-reason/list method.
|
||||
//
|
||||
// You can't cancel presumably delivered orders.
|
||||
//
|
||||
// If `cancel_reason_id` parameter value is 402, fill the `cancel_reason_message` field.
|
||||
func (c FBS) CancelSending(ctx context.Context, params *CancelSendingParams) (*CancelSendingResponse, error) {
|
||||
url := "/v2/posting/fbs/product/cancel"
|
||||
|
||||
@@ -2907,11 +2992,37 @@ type CreateOrGetProductExemplarResponse struct {
|
||||
Products []CheckProductItemsDataProduct `json:"products"`
|
||||
}
|
||||
|
||||
type CheckProductItemsDataProduct struct {
|
||||
// Data about items
|
||||
Exemplars []SetProductItemsDataProductExemplar `json:"exemplars"`
|
||||
|
||||
// Indication that you need to pass the сustoms cargo declaration (CCD) number for the product and shipment
|
||||
IsGTDNeeded bool `json:"is_gtd_needed"`
|
||||
|
||||
// Indication that you need to pass the unique identifier of charges of the jewelry
|
||||
IsJwUINNeeded bool `json:"is_jw_uin_needed"`
|
||||
|
||||
// Indication that you need to pass the "Chestny ZNAK" labeling
|
||||
IsMandatoryMarkNeeded bool `json:"is_mandatory_mark_needed"`
|
||||
|
||||
// Indication that you can pass the "Chestny ZNAK" labeling, but it's not mandatory
|
||||
IsMandatoryMarkPossible bool `json:"is_mandatory_mark_possible"`
|
||||
|
||||
// Indication that you need to pass the product batch registration number
|
||||
IsRNPTNeeded bool `json:"is_rnpt_needed"`
|
||||
|
||||
// Product ID
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Items quantity
|
||||
Quantity int32 `json:"quantity"`
|
||||
}
|
||||
|
||||
// Method returns the created items data passed in the `/v5/fbs/posting/product/exemplar/set` method.
|
||||
//
|
||||
// Use this method to get the `exemplar_id`
|
||||
func (c FBS) CreateOrGetProductExemplar(ctx context.Context, params *CreateOrGetProductExemplarParams) (*CreateOrGetProductExemplarResponse, error) {
|
||||
url := "/v5/fbs/posting/product/exemplar/create-or-get"
|
||||
url := "/v6/fbs/posting/product/exemplar/create-or-get"
|
||||
|
||||
resp := &CreateOrGetProductExemplarResponse{}
|
||||
|
||||
@@ -3016,3 +3127,292 @@ func (c FBS) GetCarriage(ctx context.Context, params *GetCarriageParams) (*GetCa
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetCancellationReasonsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result []GetCancellationReasonsResult `json:"result"`
|
||||
}
|
||||
|
||||
type GetCancellationReasonsResult struct {
|
||||
// Cancellation reasons identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Shipment cancellation result. true if the request is available for cancellation
|
||||
IsAvailableForCancellation bool `json:"is_available_for_cancellation"`
|
||||
|
||||
// Category name
|
||||
Title string `json:"title"`
|
||||
|
||||
// Shipment cancellation initiator
|
||||
TypeId string `json:"type_id"`
|
||||
}
|
||||
|
||||
func (c FBS) GetCancellationReasons(ctx context.Context) (*GetCancellationReasonsResponse, error) {
|
||||
url := "/v1/posting/fbo/cancel-reason/list"
|
||||
|
||||
resp := &GetCancellationReasonsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, nil, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type SetShippingDateParams struct {
|
||||
// New shipping date
|
||||
NewCutoffDate time.Time `json:"new_cutoff_date"`
|
||||
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
}
|
||||
|
||||
type SetShippingDateResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// true if the new date is set
|
||||
Result bool `json:"result"`
|
||||
}
|
||||
|
||||
func (c FBS) SetShippingDate(ctx context.Context, params *SetShippingDateParams) (*SetShippingDateResponse, error) {
|
||||
url := "/v1/posting/cutoff/set"
|
||||
|
||||
resp := &SetShippingDateResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type SplitOrderParams struct {
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Shipments list the order will be split into. You can split one order per one request
|
||||
Postings []SplitOrderParamPosting `json:"postings"`
|
||||
}
|
||||
|
||||
type SplitOrderParamPosting struct {
|
||||
Products []SplitOrderPostingProduct `json:"products"`
|
||||
}
|
||||
|
||||
type SplitOrderResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Original shipment details
|
||||
ParentPosting SplitOrderPosting `json:"parent_posting"`
|
||||
|
||||
// List of shipments the order was split into
|
||||
Postings []SplitOrderPosting `json:"postings"`
|
||||
}
|
||||
|
||||
type SplitOrderPosting struct {
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// List of products in the shipment
|
||||
Products []SplitOrderPostingProduct `json:"products"`
|
||||
}
|
||||
|
||||
type SplitOrderPostingProduct struct {
|
||||
// FBS product identifier in the Ozon system, SKU
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Product quantity
|
||||
Quantity int64 `json:"quantity"`
|
||||
}
|
||||
|
||||
func (c FBS) SplitOrder(ctx context.Context, params *SplitOrderParams) (*SplitOrderResponse, error) {
|
||||
url := "/v1/posting/fbs/split"
|
||||
|
||||
resp := &SplitOrderResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ListUnpaidProductsParams struct {
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Number of values in the response
|
||||
Limit int32 `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type ListUnpaidProductsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
Products []UnpaidProduct `json:"products"`
|
||||
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
}
|
||||
|
||||
type UnpaidProduct struct {
|
||||
// Product identifier
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product quantity, pcs
|
||||
Quantity int32 `json:"quantity"`
|
||||
|
||||
// Product name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Link to product image
|
||||
ImageURL string `json:"image_url"`
|
||||
}
|
||||
|
||||
func (c FBS) ListUnpaidProducts(ctx context.Context, params *ListUnpaidProductsParams) (*ListUnpaidProductsResponse, error) {
|
||||
url := "/v1/posting/unpaid-legal/product/list"
|
||||
|
||||
resp := &ListUnpaidProductsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
766
ozon/fbs_test.go
766
ozon/fbs_test.go
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListUnprocessedShipments(t *testing.T) {
|
||||
@@ -43,6 +43,7 @@ func TestListUnprocessedShipments(t *testing.T) {
|
||||
"posting_number": "23713478-0018-3",
|
||||
"order_id": 559293114,
|
||||
"order_number": "33713378-0051",
|
||||
"pickup_code_verified_at": "2025-01-17T11:03:00.124Z",
|
||||
"status": "awaiting_packaging",
|
||||
"delivery_method": {
|
||||
"id": 15110442724000,
|
||||
@@ -57,6 +58,11 @@ func TestListUnprocessedShipments(t *testing.T) {
|
||||
"in_process_at": "2021-08-25T10:48:38Z",
|
||||
"shipment_date": "2021-08-26T10:00:00Z",
|
||||
"delivering_date": null,
|
||||
"optional": {
|
||||
"products_with_possible_mandatory_mark": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"cancellation": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason": "",
|
||||
@@ -66,15 +72,16 @@ func TestListUnprocessedShipments(t *testing.T) {
|
||||
"cancellation_initiator": ""
|
||||
},
|
||||
"customer": null,
|
||||
"quantum_id": 0,
|
||||
"products": [
|
||||
{
|
||||
"currency_code": "RUB",
|
||||
"is_blr_traceable": true,
|
||||
"price": "1259",
|
||||
"offer_id": "УТ-0001365",
|
||||
"name": "Мяч, цвет: черный, 5 кг",
|
||||
"sku": 140048123,
|
||||
"quantity": 1,
|
||||
"mandatory_mark": []
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"addressee": null,
|
||||
@@ -146,7 +153,20 @@ func TestListUnprocessedShipments(t *testing.T) {
|
||||
"requirements": {
|
||||
"products_requiring_gtd": [],
|
||||
"products_requiring_country": []
|
||||
}
|
||||
},
|
||||
"tariffication": [
|
||||
{
|
||||
"current_tariff_rate": 0,
|
||||
"current_tariff_type": "",
|
||||
"current_tariff_charge": "",
|
||||
"current_tariff_charge_currency_code": "",
|
||||
"next_tariff_rate": 0,
|
||||
"next_tariff_type": "",
|
||||
"next_tariff_charge": "",
|
||||
"next_tariff_starts_at": "2023-11-13T08:05:57.657Z",
|
||||
"next_tariff_charge_currency_code": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"count": 55
|
||||
@@ -224,6 +244,7 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
||||
"posting_number": "05708065-0029-1",
|
||||
"order_id": 680420041,
|
||||
"order_number": "05708065-0029",
|
||||
"pickup_code_verified_at": "2025-01-17T10:59:26.614Z",
|
||||
"status": "awaiting_deliver",
|
||||
"substatus": "posting_awaiting_passport_data",
|
||||
"delivery_method": {
|
||||
@@ -239,6 +260,11 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
||||
"in_process_at": "2022-05-13T07:07:32Z",
|
||||
"shipment_date": "2022-05-13T10:00:00Z",
|
||||
"delivering_date": null,
|
||||
"optional": {
|
||||
"products_with_possible_mandatory_mark": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"cancellation": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason": "",
|
||||
@@ -251,12 +277,12 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
||||
"products": [
|
||||
{
|
||||
"currency_code": "RUB",
|
||||
"is_blr_traceable": true,
|
||||
"price": "1390.000000",
|
||||
"offer_id": "205953",
|
||||
"name": " Электронный конструктор PinLab Позитроник",
|
||||
"sku": 358924380,
|
||||
"quantity": 1,
|
||||
"mandatory_mark": []
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"addressee": null,
|
||||
@@ -264,11 +290,25 @@ func TestGetFBSShipmentsList(t *testing.T) {
|
||||
"analytics_data": null,
|
||||
"financial_data": null,
|
||||
"is_express": false,
|
||||
"quantum_id": 0,
|
||||
"requirements": {
|
||||
"products_requiring_gtd": [],
|
||||
"products_requiring_country": [],
|
||||
"products_requiring_mandatory_mark": []
|
||||
}
|
||||
},
|
||||
"tariffication": [
|
||||
{
|
||||
"current_tariff_rate": 0,
|
||||
"current_tariff_type": "",
|
||||
"current_tariff_charge": "",
|
||||
"current_tariff_charge_currency_code": "",
|
||||
"next_tariff_rate": 0,
|
||||
"next_tariff_type": "",
|
||||
"next_tariff_charge": "",
|
||||
"next_tariff_starts_at": "2023-11-13T08:05:57.657Z",
|
||||
"next_tariff_charge_currency_code": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"has_next": true
|
||||
@@ -393,8 +433,7 @@ func TestValidateLabelingCodes(t *testing.T) {
|
||||
{
|
||||
Exemplars: []ValidateLabelingCodesExemplar{
|
||||
{
|
||||
GTD: "",
|
||||
MandatoryMark: "010290000151642731tVMohkbfFgunB",
|
||||
GTD: "",
|
||||
},
|
||||
},
|
||||
ProductId: 476925391,
|
||||
@@ -402,23 +441,33 @@ func TestValidateLabelingCodes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"products": [
|
||||
{
|
||||
"product_id": 476925391,
|
||||
"exemplars": [
|
||||
{
|
||||
"mandatory_mark": "010290000151642731tVMohkbfFgunB",
|
||||
"gtd": "",
|
||||
"valid": true,
|
||||
"errors": []
|
||||
}
|
||||
],
|
||||
"valid": true,
|
||||
"error": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
"products": [
|
||||
{
|
||||
"error": "string",
|
||||
"exemplars": [
|
||||
{
|
||||
"errors": [
|
||||
"string"
|
||||
],
|
||||
"gtd": "string",
|
||||
"marks": [
|
||||
{
|
||||
"errors": [
|
||||
"string"
|
||||
],
|
||||
"mark": "string",
|
||||
"mark_type": "string",
|
||||
"valid": true
|
||||
}
|
||||
],
|
||||
"rnpt": "string",
|
||||
"valid": true
|
||||
}
|
||||
],
|
||||
"product_id": 476925391,
|
||||
"valid": true
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
@@ -450,11 +499,11 @@ func TestValidateLabelingCodes(t *testing.T) {
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Result.Products) != len(test.params.Products) {
|
||||
if len(resp.Products) != len(test.params.Products) {
|
||||
t.Errorf("Length of products in request and response are not equal")
|
||||
}
|
||||
if len(resp.Result.Products) > 0 {
|
||||
if resp.Result.Products[0].ProductId != test.params.Products[0].ProductId {
|
||||
if len(resp.Products) > 0 {
|
||||
if resp.Products[0].ProductId != test.params.Products[0].ProductId {
|
||||
t.Errorf("Product ids in request and response are not equal")
|
||||
}
|
||||
}
|
||||
@@ -492,8 +541,7 @@ func TestGetShipmentDataByBarcode(t *testing.T) {
|
||||
"offer_id": "250-7898-1",
|
||||
"name": "Кофе ароматизированный \"Шоколадный апельсин\" 250 гр",
|
||||
"sku": 180550365,
|
||||
"quantity": 1,
|
||||
"mandatory_mark": []
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"barcodes": null,
|
||||
@@ -563,8 +611,10 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
||||
"posting_number": "57195475-0050-3",
|
||||
"order_id": 438764970,
|
||||
"order_number": "57195475-0050",
|
||||
"pickup_code_verified_at": "2025-01-17T11:04:59.958Z",
|
||||
"status": "awaiting_packaging",
|
||||
"substatus": "posting_awaiting_passport_data",
|
||||
"previous_substatus": "posting_transferring_to_delivery",
|
||||
"delivery_method": {
|
||||
"id": 18114520187000,
|
||||
"name": "Ozon Логистика самостоятельно, Москва",
|
||||
@@ -580,6 +630,11 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
||||
"delivering_date": null,
|
||||
"provider_status": "",
|
||||
"delivery_price": "",
|
||||
"optional": {
|
||||
"products_with_possible_mandatory_mark": [
|
||||
0
|
||||
]
|
||||
},
|
||||
"cancellation": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason": "",
|
||||
@@ -593,12 +648,12 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
||||
"products": [
|
||||
{
|
||||
"currency_code": "RUB",
|
||||
"is_blr_traceable": true,
|
||||
"price": "279.0000",
|
||||
"offer_id": "250-7898-1",
|
||||
"name": "Кофе ароматизированный \"Шоколадный апельсин\" 250 гр",
|
||||
"sku": 180550365,
|
||||
"quantity": 1,
|
||||
"mandatory_mark": [],
|
||||
"dimensions": {
|
||||
"height": "40.00",
|
||||
"length": "240.00",
|
||||
@@ -616,7 +671,20 @@ func TestGetShipmentDataByIdentifier(t *testing.T) {
|
||||
"products_requiring_gtd": [],
|
||||
"products_requiring_country": []
|
||||
},
|
||||
"product_exemplars": null
|
||||
"product_exemplars": null,
|
||||
"tariffication": [
|
||||
{
|
||||
"current_tariff_rate": 0,
|
||||
"current_tariff_type": "",
|
||||
"current_tariff_charge": "",
|
||||
"current_tariff_charge_currency_code": "",
|
||||
"next_tariff_rate": 0,
|
||||
"next_tariff_type": "",
|
||||
"next_tariff_charge": "",
|
||||
"next_tariff_starts_at": "2023-11-13T08:05:57.657Z",
|
||||
"next_tariff_charge_currency_code": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
@@ -837,62 +905,6 @@ 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) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1391,52 +1403,52 @@ func TestGetDropOffPointRestrictions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckProductItemsData(t *testing.T) {
|
||||
func TestSetProductItemsData(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *CheckProductItemsDataParams
|
||||
params *SetProductItemsDataParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&CheckProductItemsDataParams{
|
||||
&SetProductItemsDataParams{
|
||||
MultiBoxQuantity: 0,
|
||||
PostingNumber: "1234",
|
||||
Products: []CheckProductItemsDataProduct{
|
||||
Products: []SetProductItemsDataProduct{
|
||||
{
|
||||
Exemplars: []CheckProductItemsDataProductExemplar{
|
||||
Exemplars: []SetProductItemsDataProductExemplar{
|
||||
{
|
||||
ExemplarId: 1,
|
||||
GTD: "string",
|
||||
IsGTDAbsent: true,
|
||||
IsRNPTAbsent: true,
|
||||
MandatoryMark: "string",
|
||||
RNPT: "string",
|
||||
JWUIN: "string",
|
||||
ExemplarId: 1,
|
||||
GTD: "string",
|
||||
IsGTDAbsent: true,
|
||||
RNPT: "string",
|
||||
},
|
||||
},
|
||||
IsGTDNeeded: true,
|
||||
IsMandatoryMarkNeeded: true,
|
||||
IsRNPTNeeded: true,
|
||||
ProductId: 22,
|
||||
Quantity: 11,
|
||||
ProductId: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
`{
|
||||
"result": true
|
||||
"code": 0,
|
||||
"details": [
|
||||
{
|
||||
"typeUrl": "string",
|
||||
"value": "string"
|
||||
}
|
||||
],
|
||||
"message": "string"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&CheckProductItemsDataParams{},
|
||||
&SetProductItemsDataParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
@@ -1448,13 +1460,13 @@ func TestCheckProductItemsData(t *testing.T) {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBS().CheckProductItemsData(ctx, test.params)
|
||||
resp, err := c.FBS().SetProductItemsData(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &CheckProductItemsDataResponse{})
|
||||
compareJsonResponse(t, test.response, &SetProductItemsDataResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
@@ -1482,21 +1494,37 @@ func TestGetProductItemsCheckStatuses(t *testing.T) {
|
||||
"posting_number": "23281294-0063-2",
|
||||
"products": [
|
||||
{
|
||||
"product_id": 476925391,
|
||||
"exemplars": [
|
||||
{
|
||||
"mandatory_mark": "010290000151642731tVMohkbfFgunB",
|
||||
"gtd": "",
|
||||
"exemplar_id": 0,
|
||||
"gtd": "string",
|
||||
"gtd_check_status": "string",
|
||||
"gtd_error_codes": [
|
||||
"string"
|
||||
],
|
||||
"is_gtd_absent": true,
|
||||
"mandatory_mark_check_status": "passed",
|
||||
"mandatory_mark_error_codes": [],
|
||||
"gtd_check_status": "passed",
|
||||
"gtd_error_codes": []
|
||||
"is_rnpt_absent": true,
|
||||
"marks": [
|
||||
{
|
||||
"check_status": "string",
|
||||
"error_codes": [
|
||||
"string"
|
||||
],
|
||||
"mark": "string",
|
||||
"mark_type": "string"
|
||||
}
|
||||
],
|
||||
"rnpt": "string",
|
||||
"rnpt_check_status": "string",
|
||||
"rnpt_error_codes": [
|
||||
"string"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"product_id": 123
|
||||
}
|
||||
],
|
||||
"status": "ship_available"
|
||||
"status": "string"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
@@ -1537,8 +1565,8 @@ func TestGetProductItemsCheckStatuses(t *testing.T) {
|
||||
if resp.Products[0].ProductId == 0 {
|
||||
t.Errorf("Product id cannot be 0")
|
||||
}
|
||||
if len(resp.Products[0].Exemplars) > 0 {
|
||||
if resp.Products[0].Exemplars[0].MandatoryMark == "" {
|
||||
if len(resp.Products[0].Exemplars) > 0 && len(resp.Products[0].Exemplars[0].Marks) > 0 {
|
||||
if resp.Products[0].Exemplars[0].Marks[0].Mark == "" {
|
||||
t.Errorf("Mandatory mark cannot be empty")
|
||||
}
|
||||
}
|
||||
@@ -1883,6 +1911,8 @@ func TestAvailableFreightsList(t *testing.T) {
|
||||
"has_entrusted_acceptance": true,
|
||||
"mandatory_postings_count": 0,
|
||||
"mandatory_packaged_count": 0,
|
||||
"recommended_time_local": "string",
|
||||
"recommended_time_utc_offset_in_minutes": 0,
|
||||
"tpl_provider_icon_url": "string",
|
||||
"tpl_provider_name": "string",
|
||||
"warehouse_city": "string",
|
||||
@@ -2886,13 +2916,19 @@ func TestCreateOrGetProductExemplar(t *testing.T) {
|
||||
"gtd": "string",
|
||||
"is_gtd_absent": true,
|
||||
"is_rnpt_absent": true,
|
||||
"mandatory_mark": "string",
|
||||
"rnpt": "string",
|
||||
"jw_uin": "string"
|
||||
"marks": [
|
||||
{
|
||||
"mark": "string",
|
||||
"mark_type": "string"
|
||||
}
|
||||
],
|
||||
"rnpt": "string"
|
||||
}
|
||||
],
|
||||
"is_gtd_needed": true,
|
||||
"is_jw_uin_needed": true,
|
||||
"is_mandatory_mark_needed": true,
|
||||
"is_mandatory_mark_possible": true,
|
||||
"is_rnpt_needed": true,
|
||||
"product_id": 0,
|
||||
"quantity": 0
|
||||
@@ -3010,3 +3046,485 @@ func TestGetCarriage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCancellationReasons(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
`{
|
||||
"result": [
|
||||
{
|
||||
"id": 352,
|
||||
"title": "The products ran out at the seller's warehouse",
|
||||
"type_id": "seller",
|
||||
"is_available_for_cancellation": true
|
||||
},
|
||||
{
|
||||
"id": 401,
|
||||
"title": "Seller rejects arbitration",
|
||||
"type_id": "seller",
|
||||
"is_available_for_cancellation": false
|
||||
},
|
||||
{
|
||||
"id": 402,
|
||||
"title": "Other (seller's fault)",
|
||||
"type_id": "seller",
|
||||
"is_available_for_cancellation": true
|
||||
},
|
||||
{
|
||||
"id": 666,
|
||||
"title": "Return from the delivery service: there is no delivery to the specified region",
|
||||
"type_id": "seller",
|
||||
"is_available_for_cancellation": false
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBS().GetCancellationReasons(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetCancellationReasonsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetShippingDate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *SetShippingDateParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&SetShippingDateParams{
|
||||
NewCutoffDate: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
},
|
||||
`{
|
||||
"result": true
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&SetShippingDateParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBS().SetShippingDate(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &SetShippingDateResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *SplitOrderParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&SplitOrderParams{
|
||||
PostingNumber: "string",
|
||||
Postings: []SplitOrderParamPosting{
|
||||
{
|
||||
Products: []SplitOrderPostingProduct{
|
||||
{
|
||||
ProductId: 1,
|
||||
Quantity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
`{
|
||||
"parent_posting": {
|
||||
"posting_number": "string",
|
||||
"products": [
|
||||
{
|
||||
"product_id": 0,
|
||||
"quantity": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"postings": [
|
||||
{
|
||||
"posting_number": "string",
|
||||
"products": [
|
||||
{
|
||||
"product_id": 0,
|
||||
"quantity": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&SplitOrderParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBS().SplitOrder(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &SplitOrderResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListUnpaidProducts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListUnpaidProductsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListUnpaidProductsParams{
|
||||
Cursor: "hCGiPPopcBFMgMErdzaCEpzQfinuPyEhUoSmBMADuoFAhBjXeA==",
|
||||
Limit: 1000,
|
||||
},
|
||||
`{
|
||||
"products": [
|
||||
{
|
||||
"product_id": 145123054,
|
||||
"offer_id": "10032",
|
||||
"quantity": 1,
|
||||
"name": "Телевизор LG",
|
||||
"image_url": "https://cdn1.ozon.ru/multimedia/10741275.jpg"
|
||||
}
|
||||
],
|
||||
"cursor": "hCGiPPopcBFMgMErdzaCEpzQfinuPyEhUoSmBMADuoFAhBjXeA=="
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListUnpaidProductsParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.FBS().ListUnpaidProducts(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListUnpaidProductsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
173
ozon/finance.go
173
ozon/finance.go
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Finance struct {
|
||||
@@ -13,8 +13,11 @@ type Finance struct {
|
||||
}
|
||||
|
||||
type ReportOnSoldProductsParams struct {
|
||||
// Time period in the `YYYY-MM` format
|
||||
Date string `json:"date"`
|
||||
// Month
|
||||
Month int32 `json:"month"`
|
||||
|
||||
// Year
|
||||
Year int32 `json:"year"`
|
||||
}
|
||||
|
||||
type ReportOnSoldProductsResponse struct {
|
||||
@@ -34,7 +37,7 @@ type ReportonSoldProductsResult struct {
|
||||
|
||||
type ReportOnSoldProductsResultHeader struct {
|
||||
// Report ID
|
||||
Id string `json:"num"`
|
||||
Id string `json:"number"`
|
||||
|
||||
// Report generation date
|
||||
DocDate string `json:"doc_date"`
|
||||
@@ -43,10 +46,10 @@ type ReportOnSoldProductsResultHeader struct {
|
||||
ContractDate string `json:"contract_date"`
|
||||
|
||||
// Offer agreement number
|
||||
ContractNum string `json:"contract_num"`
|
||||
ContractNum string `json:"contract_number"`
|
||||
|
||||
// Currency of your prices
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
CurrencySysName string `json:"currency_sys_name"`
|
||||
|
||||
// Amount to accrue
|
||||
DocAmount float64 `json:"doc_amount"`
|
||||
@@ -64,13 +67,13 @@ type ReportOnSoldProductsResultHeader struct {
|
||||
PayerName string `json:"payer_name"`
|
||||
|
||||
// Recipient's TIN
|
||||
RecipientINN string `json:"rcv_inn"`
|
||||
RecipientINN string `json:"receiver_inn"`
|
||||
|
||||
// Recipient's Tax Registration Reason Code (KPP)
|
||||
RecipientKPP string `json:"rcv_kpp"`
|
||||
RecipientKPP string `json:"receiver_kpp"`
|
||||
|
||||
// Recipient's name
|
||||
RecipientName string `json:"rcv_name"`
|
||||
RecipientName string `json:"receiver_name"`
|
||||
|
||||
// Period start in the report
|
||||
StartDate string `json:"start_date"`
|
||||
@@ -81,13 +84,28 @@ type ReportOnSoldProductsResultHeader struct {
|
||||
|
||||
type ReportOnSoldProductsResultRow struct {
|
||||
// Row number
|
||||
RowNumber int32 `json:"row_number"`
|
||||
RowNumber int32 `json:"rowNumber"`
|
||||
|
||||
// Product ID
|
||||
ProductId int64 `json:"product_id"`
|
||||
// Product Information
|
||||
Item ReturnOnSoldProduct `json:"item"`
|
||||
|
||||
// Commission including the quantity of products, discounts and extra charges.
|
||||
// Ozon compensates it for the returned products
|
||||
ReturnCommission ReturnCommission `json:"return_commission"`
|
||||
|
||||
// Percentage of sales commission by category
|
||||
CommissionRatio float64 `json:"commission_ratio"`
|
||||
|
||||
// Delivery fee
|
||||
DeliveryCommission ReturnCommission `json:"delivery_commission"`
|
||||
|
||||
// Seller's discounted price
|
||||
SellerPricePerInstance float64 `json:"seller_price_per_instance"`
|
||||
}
|
||||
|
||||
type ReturnOnSoldProduct struct {
|
||||
// Product name
|
||||
ProductName string `json:"product_name"`
|
||||
ProductName string `json:"name"`
|
||||
|
||||
// Product barcode
|
||||
Barcode string `json:"barcode"`
|
||||
@@ -95,65 +113,46 @@ type ReportOnSoldProductsResultRow struct {
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Sales commission by category
|
||||
CommissionPercent float64 `json:"commission_percent"`
|
||||
SKU int64 `json:"sku"`
|
||||
}
|
||||
|
||||
// Seller's price with their discount
|
||||
Price float64 `json:"price"`
|
||||
type ReturnCommission struct {
|
||||
// Amount
|
||||
Amount float64 `json:"amount"`
|
||||
|
||||
// Selling price: the price at which the customer purchased the product. For sold products
|
||||
PriceSale float64 `json:"price_sale"`
|
||||
|
||||
// Sold for amount.
|
||||
//
|
||||
// Sold products cost considering the quantity and regional coefficients. Calculation is made by the sale_amount price
|
||||
SaleAmount float64 `json:"sale_amount"`
|
||||
// Points for discounts
|
||||
Bonus float64 `json:"bonus"`
|
||||
|
||||
// Commission for sold products, including discounts and extra charges
|
||||
SaleCommission float64 `json:"sale_commission"`
|
||||
Commission float64 `json:"commission"`
|
||||
|
||||
// Extra charge at the expense of Ozon.
|
||||
//
|
||||
// Amount that Ozon will compensate the seller if the Ozon discount is greater than or equal to the sales commission
|
||||
SaleDiscount float64 `json:"sale_discount"`
|
||||
// Additional payment at the expense of Ozon
|
||||
Compensation float64 `json:"compensation"`
|
||||
|
||||
// Total accrual for the products sold.
|
||||
//
|
||||
// Amount after deduction of sales commission, application of discounts and extra charges
|
||||
SalePriceSeller float64 `json:"sale_price_seller"`
|
||||
// Price per item
|
||||
PricePerInstance float64 `json:"price_per_instance"`
|
||||
|
||||
// Quantity of products sold at the price_sale price
|
||||
SaleQuantity int32 `json:"sale_qty"`
|
||||
// Product quantity
|
||||
Quantity int32 `json:"quantity"`
|
||||
|
||||
// Price at which the customer purchased the product. For returned products
|
||||
ReturnSale float64 `json:"return_sale"`
|
||||
// Ozon referral fee
|
||||
StandardFee float64 `json:"standard_fee"`
|
||||
|
||||
// Cost of returned products, taking into account the quantity and regional coefficients.
|
||||
// Calculation is carried out at the return_sale price
|
||||
ReturnAmount float64 `json:"return_amount"`
|
||||
// Payouts on partner loyalty mechanics: green prices
|
||||
BankCoinvestment float64 `json:"bank_coinvestment"`
|
||||
|
||||
// Commission including the quantity of products, discounts and extra charges.
|
||||
// Ozon compensates it for the returned products
|
||||
ReturnCommission float64 `json:"return_commission"`
|
||||
// Payouts on partner loyalty mechanics: stars
|
||||
Stars float64 `json:"stars"`
|
||||
|
||||
// Extra charge at the expense of Ozon.
|
||||
//
|
||||
// Amount of the discount at the expense of Ozon on returned products.
|
||||
// Ozon will compensate it to the seller if the Ozon discount is greater than or equal to the sales commission
|
||||
ReturnDiscount float64 `json:"return_discount"`
|
||||
|
||||
// Amount charged to the seller for returned products after deducing sales commissions, applying discounts and extra charges
|
||||
ReturnPriceSeller float64 `json:"return_price_seller"`
|
||||
|
||||
// Quantity of returned products
|
||||
ReturnQuantity int32 `json:"return_qty"`
|
||||
// Total accrual
|
||||
Total float64 `json:"total"`
|
||||
}
|
||||
|
||||
// Returns information on products sold and returned within a month. Canceled or non-purchased products are not included.
|
||||
//
|
||||
// Report is returned no later than the 5th day of the next month
|
||||
func (c Finance) ReportOnSoldProducts(ctx context.Context, params *ReportOnSoldProductsParams) (*ReportOnSoldProductsResponse, error) {
|
||||
url := "/v1/finance/realization"
|
||||
url := "/v2/finance/realization"
|
||||
|
||||
resp := &ReportOnSoldProductsResponse{}
|
||||
|
||||
@@ -306,10 +305,10 @@ type ListTransactionsResult struct {
|
||||
// Transactions infromation
|
||||
Operations []ListTransactionsResultOperation `json:"operations"`
|
||||
|
||||
// Number of pages
|
||||
// Number of pages. If 0, there are no more pages
|
||||
PageCount int64 `json:"page_count"`
|
||||
|
||||
// Number of products
|
||||
// Number of transactions on all pages. If 0, there are no more transactions
|
||||
RowCount int64 `json:"row_count"`
|
||||
}
|
||||
|
||||
@@ -363,11 +362,7 @@ type ListTransactionsResultOperationItem struct {
|
||||
}
|
||||
|
||||
type ListTransactionsResultOperationPosting struct {
|
||||
// Delivery scheme:
|
||||
// - FBO — delivery to Ozon warehouse
|
||||
// - FBS — delivery from seller's warehouse
|
||||
// - RFBS — delivery service of seller's choice
|
||||
// - Crossborder — delivery from abroad
|
||||
// Delivery scheme
|
||||
DeliverySchema string `json:"delivery_schema"`
|
||||
|
||||
// Date the product was accepted for processing
|
||||
@@ -404,3 +399,57 @@ func (c Finance) ListTransactions(ctx context.Context, params *ListTransactionsP
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetReportParams struct {
|
||||
// Time period in the YYYY-MM format
|
||||
Date string `json:"date"`
|
||||
|
||||
// Response language
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
type ReportResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result ReportResult `json:"result"`
|
||||
}
|
||||
|
||||
type ReportResult struct {
|
||||
// Unique report identifier
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// Use the method to get mutual settlements report.
|
||||
// Matches the Finance → Documents → Analytical reports → Mutual
|
||||
// settlements report section in your personal account.
|
||||
func (c Finance) MutualSettlements(ctx context.Context, params *GetReportParams) (*ReportResponse, error) {
|
||||
url := "/v1/finance/mutual-settlement"
|
||||
|
||||
resp := &ReportResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Use the method to get sales to legal entities report.
|
||||
// Matches the Finance → Documents → Legal
|
||||
// entities sales register section in your personal account.
|
||||
func (c Finance) SalesToLegalEntities(ctx context.Context, params *GetReportParams) (*ReportResponse, error) {
|
||||
url := "/v1/finance/mutual-settlement"
|
||||
|
||||
resp := &ReportResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestReportOnSoldProducts(t *testing.T) {
|
||||
@@ -23,52 +23,67 @@ func TestReportOnSoldProducts(t *testing.T) {
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ReportOnSoldProductsParams{
|
||||
Date: "2022-09",
|
||||
Month: 9,
|
||||
Year: 2022,
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"header": {
|
||||
"doc_date": "2022-09-22",
|
||||
"num": "string",
|
||||
"start_date": "2022-09-02",
|
||||
"stop_date": "2022-09-22",
|
||||
"contract_date": "2022-09-02",
|
||||
"contract_num": "string",
|
||||
"payer_name": "string",
|
||||
"payer_inn": "string",
|
||||
"payer_kpp": "string",
|
||||
"rcv_name": "string",
|
||||
"rcv_inn": "string",
|
||||
"rcv_kpp": "string",
|
||||
"doc_amount": 1,
|
||||
"vat_amount": 1,
|
||||
"currency_code": "string"
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"row_number": 0,
|
||||
"product_id": 0,
|
||||
"product_name": "string",
|
||||
"offer_id": "string",
|
||||
"barcode": "string",
|
||||
"price": 0,
|
||||
"commission_percent": 0,
|
||||
"price_sale": 0,
|
||||
"sale_qty": 0,
|
||||
"sale_amount": 0,
|
||||
"sale_discount": 0,
|
||||
"sale_commission": 0,
|
||||
"sale_price_seller": 0,
|
||||
"return_sale": 0,
|
||||
"return_qty": 0,
|
||||
"return_amount": 0,
|
||||
"return_discount": 0,
|
||||
"return_commission": 0,
|
||||
"return_price_seller": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
"result": {
|
||||
"header": {
|
||||
"contract_date": "string",
|
||||
"contract_number": "string",
|
||||
"currency_sys_name": "string",
|
||||
"doc_amount": 0,
|
||||
"doc_date": "string",
|
||||
"number": "string",
|
||||
"payer_inn": "string",
|
||||
"payer_kpp": "string",
|
||||
"payer_name": "string",
|
||||
"receiver_inn": "string",
|
||||
"receiver_kpp": "string",
|
||||
"receiver_name": "string",
|
||||
"start_date": "string",
|
||||
"stop_date": "string",
|
||||
"vat_amount": 0
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"commission_ratio": 0,
|
||||
"delivery_commission": {
|
||||
"amount": 0,
|
||||
"bonus": 0,
|
||||
"commission": 0,
|
||||
"compensation": 0,
|
||||
"price_per_instance": 0,
|
||||
"quantity": 0,
|
||||
"standard_fee": 0,
|
||||
"bank_coinvestment": 0,
|
||||
"stars": 0,
|
||||
"total": 0
|
||||
},
|
||||
"item": {
|
||||
"barcode": "string",
|
||||
"name": "string",
|
||||
"offer_id": "string",
|
||||
"sku": 0
|
||||
},
|
||||
"return_commission": {
|
||||
"amount": 0,
|
||||
"bonus": 0,
|
||||
"commission": 0,
|
||||
"compensation": 0,
|
||||
"price_per_instance": 0,
|
||||
"quantity": 0,
|
||||
"standard_fee": 0,
|
||||
"bank_coinvestment": 0,
|
||||
"stars": 0,
|
||||
"total": 0
|
||||
},
|
||||
"rowNumber": 0,
|
||||
"seller_price_per_instance": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
"",
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
@@ -271,3 +286,127 @@ func TestListTransactions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutualSettlements(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetReportParams
|
||||
response string
|
||||
errorMessage string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetReportParams{
|
||||
Date: "2024-08",
|
||||
Language: "DEFAULT",
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"code": "string"
|
||||
}
|
||||
}`,
|
||||
"",
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetReportParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
"Client-Id and Api-Key headers are required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Finance().MutualSettlements(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ReportResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.Message != test.errorMessage {
|
||||
t.Errorf("got wrong error message: got: %s, expected: %s", resp.Message, test.errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSalesToLegalEntities(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetReportParams
|
||||
response string
|
||||
errorMessage string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetReportParams{
|
||||
Date: "2024-08",
|
||||
Language: "DEFAULT",
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"code": "string"
|
||||
}
|
||||
}`,
|
||||
"",
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetReportParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
"Client-Id and Api-Key headers are required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Finance().SalesToLegalEntities(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ReportResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.Message != test.errorMessage {
|
||||
t.Errorf("got wrong error message: got: %s, expected: %s", resp.Message, test.errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Invoices struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestCreateUpdateProformaLink(t *testing.T) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type testData struct {
|
||||
|
||||
23
ozon/ozon.go
23
ozon/ozon.go
@@ -3,7 +3,7 @@ package ozon
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -42,6 +42,9 @@ type Client struct {
|
||||
strategies *Strategies
|
||||
barcodes *Barcodes
|
||||
passes *Passes
|
||||
clusters *Clusters
|
||||
quants *Quants
|
||||
reviews *Reviews
|
||||
}
|
||||
|
||||
func (c Client) Analytics() *Analytics {
|
||||
@@ -124,6 +127,18 @@ func (c Client) Passes() *Passes {
|
||||
return c.passes
|
||||
}
|
||||
|
||||
func (c Client) Clusters() *Clusters {
|
||||
return c.clusters
|
||||
}
|
||||
|
||||
func (c Client) Quants() *Quants {
|
||||
return c.quants
|
||||
}
|
||||
|
||||
func (c Client) Reviews() *Reviews {
|
||||
return c.reviews
|
||||
}
|
||||
|
||||
type ClientOption func(c *ClientOptions)
|
||||
|
||||
func WithHttpClient(httpClient core.HttpClient) ClientOption {
|
||||
@@ -188,6 +203,9 @@ func NewClient(opts ...ClientOption) *Client {
|
||||
strategies: &Strategies{client: coreClient},
|
||||
barcodes: &Barcodes{client: coreClient},
|
||||
passes: &Passes{client: coreClient},
|
||||
clusters: &Clusters{client: coreClient},
|
||||
quants: &Quants{client: coreClient},
|
||||
reviews: &Reviews{client: coreClient},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,5 +234,8 @@ func NewMockClient(handler http.HandlerFunc) *Client {
|
||||
strategies: &Strategies{client: coreClient},
|
||||
barcodes: &Barcodes{client: coreClient},
|
||||
passes: &Passes{client: coreClient},
|
||||
clusters: &Clusters{client: coreClient},
|
||||
quants: &Quants{client: coreClient},
|
||||
reviews: &Reviews{client: coreClient},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Passes struct {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListPasses(t *testing.T) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Polygons struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestCreateDeliveryPolygon(t *testing.T) {
|
||||
|
||||
1015
ozon/products.go
1015
ozon/products.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Promotions struct {
|
||||
@@ -297,196 +297,6 @@ func (c Promotions) RemoveProduct(ctx context.Context, params *RemoveProductFrom
|
||||
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 {
|
||||
// Discount request status
|
||||
Status ListDiscountRequestsStatus `json:"status" default:"UNKNOWN"`
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetAvailablePromotions(t *testing.T) {
|
||||
@@ -354,248 +354,6 @@ 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) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
356
ozon/quants.go
Normal file
356
ozon/quants.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Quants struct {
|
||||
client *core.Client
|
||||
}
|
||||
|
||||
type ListQuantsParams struct {
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Filter
|
||||
Filter ListQuantsFilter `json:"filter"`
|
||||
|
||||
// Maximum number of values in the response
|
||||
Limit int32 `json:"limit"`
|
||||
|
||||
// Parameter by which products will be sorted
|
||||
Sort string `json:"sort"`
|
||||
|
||||
// Sorting direction
|
||||
SortDir string `json:"sort_dir"`
|
||||
}
|
||||
|
||||
type ListQuantsFilter struct {
|
||||
// MOQ creation period
|
||||
CreatedAt *ListQuantsFilterTime `json:"created_at"`
|
||||
|
||||
// Time for MOQ assembly
|
||||
Cutoff *ListQuantsFilterTime `json:"cutoff"`
|
||||
|
||||
// Destination point identifier
|
||||
DestinationPlaceId int64 `json:"destination_place_id"`
|
||||
|
||||
// MOQ inventory identifiers
|
||||
InvQuantIds []string `json:"inv_quants_ids"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Product name
|
||||
SKUName string `json:"sku_name"`
|
||||
|
||||
// MOQ statuses
|
||||
Statuses []string `json:"statuses"`
|
||||
|
||||
// Warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
}
|
||||
|
||||
type ListQuantsFilterTime struct {
|
||||
// Start date
|
||||
From string `json:"from"`
|
||||
|
||||
// End date
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
type ListQuantsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
Result ListQuantsResult `json:"result"`
|
||||
}
|
||||
|
||||
type ListQuantsResult struct {
|
||||
// Cursor for the next data sample
|
||||
Cursor string `json:"cursor"`
|
||||
|
||||
// Indication that the response returned only a part of characteristic values
|
||||
HasNext bool `json:"has_next"`
|
||||
|
||||
// MOQs list
|
||||
Quants []Quant `json:"quants"`
|
||||
}
|
||||
|
||||
type Quant struct {
|
||||
// List of available actions with MOQ
|
||||
AvailableActions []string `json:"available_actions"`
|
||||
|
||||
// Date until which the leftover stock amount must be specified
|
||||
AwaitingStockDueDate string `json:"awaiting_stock_due_date"`
|
||||
|
||||
// MOQ cancellation reason
|
||||
CancelReason `json:"cancel_reason"`
|
||||
|
||||
// Seller company identifier
|
||||
CompanyId int64 `json:"company_id"`
|
||||
|
||||
// MOQ creation date
|
||||
CreatedAt string `json:"created_at"`
|
||||
|
||||
// Current number of shipments in the MOQ
|
||||
CurrentPostingsCount int64 `json:"current_postings_count"`
|
||||
|
||||
// Time until which the MOQ must be assembled
|
||||
Cutoff string `json:"cutoff"`
|
||||
|
||||
// Delivery method name
|
||||
DeliveryMethod string `json:"delivery_method_name"`
|
||||
|
||||
// Destination point identifier
|
||||
DestinationPlaceId int64 `json:"destination_place_id"`
|
||||
|
||||
// Destination point name
|
||||
DestinationPlaceName string `json:"destination_place_name"`
|
||||
|
||||
// MOQ filling percentage
|
||||
FillingPercent float32 `json:"filling_percent"`
|
||||
|
||||
// Date when the shipments start to get canceled if the MOQ is not reserved
|
||||
FirstPostingCancellationDate string `json:"first_posting_cancellation_date"`
|
||||
|
||||
// MOQ identifier in Ozon system
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// MOQ inventory identifier
|
||||
InvQuantId int64 `json:"inv_quant_id"`
|
||||
|
||||
// Date of the last MOQ status change
|
||||
LastStatusChangeAt string `json:"last_status_change_at"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Total cost of products in the MOQ
|
||||
ProductsPrice float32 `json:"products_price"`
|
||||
|
||||
// Start date of MOQ filling
|
||||
QuantumStartDate string `json:"quantum_start_date"`
|
||||
|
||||
// Product SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Product name
|
||||
SKUName string `json:"sku_name"`
|
||||
|
||||
// MOQ statuses
|
||||
Status string `json:"status"`
|
||||
|
||||
// Required number of products in the MOQ
|
||||
TargetPostingsCount int64 `json:"target_postings_count"`
|
||||
|
||||
// Delivery service name
|
||||
TPLProviderName string `json:"tpl_provider_name"`
|
||||
|
||||
// MOQ type: box or pallet
|
||||
Type string `json:"type"`
|
||||
|
||||
// Seller warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
|
||||
// Seller warehouse name
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
type CancelReason struct {
|
||||
// Identifier of MOQ cancellation reason
|
||||
Id int64 `json:"cancel_reason_id"`
|
||||
|
||||
// Cancellation reason name
|
||||
Name string `json:"cancel_reason_name"`
|
||||
|
||||
// Cancellation initiator
|
||||
Responsible string `json:"responsible"`
|
||||
}
|
||||
|
||||
// You can leave feedback on this method in the comments section to the discussion in the Ozon for dev community.
|
||||
func (q Quants) List(ctx context.Context, params *ListQuantsParams) (*ListQuantsResponse, error) {
|
||||
url := "/v1/quant/list"
|
||||
|
||||
resp := &ListQuantsResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetQuantParams struct {
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"inv_quant_id"`
|
||||
}
|
||||
|
||||
type GetQuantResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// MOQ information
|
||||
Result []GetQuantResult `json:"result"`
|
||||
}
|
||||
|
||||
type GetQuantResult struct {
|
||||
// Available actions
|
||||
AvailableActions []string `json:"available_actions"`
|
||||
|
||||
// Date until which the leftover stock amount must be specified
|
||||
AwaitingStockDueDate time.Time `json:"awaiting_stock_due_date"`
|
||||
|
||||
// Shipment cancellation reason
|
||||
CancelReason CancelReason `json:"cancel_reason"`
|
||||
|
||||
// Current number of shipments in the MOQ
|
||||
CurrentPostingsCount int64 `json:"current_postings_count"`
|
||||
|
||||
// Time until which the MOQ must be assembled
|
||||
Cutoff time.Time `json:"cutoff"`
|
||||
|
||||
// Delivery method name
|
||||
DeliveryMethodName string `json:"delivery_method_name"`
|
||||
|
||||
// Destination point identifier
|
||||
DestinationPlaceId int64 `json:"destination_place_id"`
|
||||
|
||||
// Destination point name
|
||||
DestinationPlaceName string `json:"destination_place_name"`
|
||||
|
||||
// MOQ filling percentage
|
||||
FillingPercent float32 `json:"filling_percent"`
|
||||
|
||||
// Date when the shipments start to get canceled if the MOQ isn't reserved
|
||||
FirstPostingCancellationDate time.Time `json:"first_posting_cancellation_date"`
|
||||
|
||||
// MOQ identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"inv_quant_id"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// Shipments
|
||||
Postings []GetQuantResultPosting `json:"postings"`
|
||||
|
||||
// Link to product photo
|
||||
ProductPictureURL string `json:"product_picture_url"`
|
||||
|
||||
// Total price of products in the MOQ
|
||||
ProductsPrice float32 `json:"products_price"`
|
||||
|
||||
// Start date of MOQ filling
|
||||
QuantumStartDate time.Time `json:"quantum_start_date"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Product name
|
||||
SKUName string `json:"sku_name"`
|
||||
|
||||
// MOQ statuses
|
||||
Status string `json:"status"`
|
||||
|
||||
// Required number of products in the MOQ
|
||||
TargetPostingsCount int64 `json:"target_postings_count"`
|
||||
|
||||
// Delivery service name
|
||||
TPLProviderName string `json:"tpl_provider_name"`
|
||||
|
||||
// MOQ type: box or pallet
|
||||
Type string `json:"type"`
|
||||
|
||||
// Warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
|
||||
// Warehouse name
|
||||
WarehouseName string `json:"warehouse_name"`
|
||||
}
|
||||
|
||||
type GetQuantResultPosting struct {
|
||||
// Shipment cancellation reason
|
||||
CancelReason CancelReason `json:"cancel_reason"`
|
||||
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Total price of products in the MOQ
|
||||
ProductsPrice float32 `json:"products_price"`
|
||||
|
||||
// Status text
|
||||
StatusAlias string `json:"status_alias"`
|
||||
|
||||
// Status identifier
|
||||
StatusId int64 `json:"status_id"`
|
||||
}
|
||||
|
||||
func (q Quants) Get(ctx context.Context, params *GetQuantParams) (*GetQuantResponse, error) {
|
||||
url := "/v1/quant/get"
|
||||
|
||||
resp := &GetQuantResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ShipQuantParams struct {
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"quant_id"`
|
||||
}
|
||||
|
||||
type ShipQuantResponse struct {
|
||||
core.CommonResponse
|
||||
}
|
||||
|
||||
func (q Quants) Ship(ctx context.Context, params *ShipQuantParams) (*ShipQuantResponse, error) {
|
||||
url := "/v1/quant/ship"
|
||||
|
||||
resp := &ShipQuantResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type StatusQuantParams struct {
|
||||
// MOQ inventory identifier
|
||||
QuantId int64 `json:"inv_quant_id"`
|
||||
}
|
||||
|
||||
type StatusQuantResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// MOQ statuses
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (q Quants) Status(ctx context.Context, params *StatusQuantParams) (*StatusQuantResponse, error) {
|
||||
url := "/v1/quant/ship"
|
||||
|
||||
resp := &StatusQuantResponse{}
|
||||
|
||||
response, err := q.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
303
ozon/quants_test.go
Normal file
303
ozon/quants_test.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package ozon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListQuants(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListQuantsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListQuantsParams{
|
||||
Cursor: "string",
|
||||
Filter: ListQuantsFilter{
|
||||
InvQuantIds: []string{"string"},
|
||||
DestinationPlaceId: 123,
|
||||
OfferId: "string",
|
||||
SKUName: "string",
|
||||
Statuses: []string{"unknown"},
|
||||
WarehouseId: 456,
|
||||
},
|
||||
Limit: 10,
|
||||
Sort: "string",
|
||||
SortDir: "string",
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"cursor": "string",
|
||||
"has_next": true,
|
||||
"quants": [
|
||||
{
|
||||
"available_actions": [
|
||||
"string"
|
||||
],
|
||||
"awaiting_stock_due_date": "2019-08-24T14:15:22Z",
|
||||
"cancel_reason": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason_name": "string",
|
||||
"responsible": "string"
|
||||
},
|
||||
"company_id": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"current_postings_count": 0,
|
||||
"cutoff": "2019-08-24T14:15:22Z",
|
||||
"delivery_method_name": "string",
|
||||
"destination_place_id": 0,
|
||||
"destination_place_name": "string",
|
||||
"filling_percent": 0,
|
||||
"first_posting_cancellation_date": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"inv_quant_id": 0,
|
||||
"last_status_change_at": "2019-08-24T14:15:22Z",
|
||||
"offer_id": "string",
|
||||
"products_price": 0,
|
||||
"quantum_start_date": "2019-08-24T14:15:22Z",
|
||||
"sku": 0,
|
||||
"sku_name": "string",
|
||||
"status": "unknown",
|
||||
"target_postings_count": 0,
|
||||
"tpl_provider_name": "string",
|
||||
"type": "string",
|
||||
"warehouse_id": 0,
|
||||
"warehouse_name": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListQuantsParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().List(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListQuantsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetQuant(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetQuantParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetQuantParams{
|
||||
QuantId: 456,
|
||||
},
|
||||
`{
|
||||
"result": [
|
||||
{
|
||||
"available_actions": [
|
||||
"string"
|
||||
],
|
||||
"awaiting_stock_due_date": "2019-08-24T14:15:22Z",
|
||||
"cancel_reason": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason_name": "string",
|
||||
"responsible": "string"
|
||||
},
|
||||
"current_postings_count": 0,
|
||||
"cutoff": "2019-08-24T14:15:22Z",
|
||||
"delivery_method_name": "string",
|
||||
"destination_place_id": 0,
|
||||
"destination_place_name": "string",
|
||||
"filling_percent": 0,
|
||||
"first_posting_cancellation_date": "2019-08-24T14:15:22Z",
|
||||
"id": 0,
|
||||
"inv_quant_id": 0,
|
||||
"offer_id": "string",
|
||||
"postings": [
|
||||
{
|
||||
"cancel_reason": {
|
||||
"cancel_reason_id": 0,
|
||||
"cancel_reason_name": "string",
|
||||
"responsible": "string"
|
||||
},
|
||||
"posting_number": "string",
|
||||
"products_price": 0,
|
||||
"status_alias": "string",
|
||||
"status_id": 0
|
||||
}
|
||||
],
|
||||
"product_picture_url": "string",
|
||||
"products_price": 0,
|
||||
"quantum_start_date": "2019-08-24T14:15:22Z",
|
||||
"sku": 0,
|
||||
"sku_name": "string",
|
||||
"status": "unknown",
|
||||
"target_postings_count": 0,
|
||||
"tpl_provider_name": "string",
|
||||
"type": "string",
|
||||
"warehouse_id": 0,
|
||||
"warehouse_name": "string"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetQuantParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().Get(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetQuantResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShipQuant(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ShipQuantParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ShipQuantParams{
|
||||
QuantId: 456,
|
||||
},
|
||||
`{}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ShipQuantParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().Ship(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ShipQuantResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusQuant(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *StatusQuantParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&StatusQuantParams{
|
||||
QuantId: 456,
|
||||
},
|
||||
`{
|
||||
"status": "unknown"
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&StatusQuantParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Quants().Status(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &StatusQuantResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Rating struct {
|
||||
@@ -18,6 +18,11 @@ type GetCurrentSellerRatingInfoResponse struct {
|
||||
// Rating groups list
|
||||
Groups []GetCurrentSellerRatingInfoGroup `json:"groups"`
|
||||
|
||||
// Localization index details.
|
||||
// If you had no sales in the last 14 days,
|
||||
// the parameter fields will be empty
|
||||
LocalizationIndex []LocalizationIndex `json:"localization_index"`
|
||||
|
||||
// An indication that the penalty points balance is exceeded
|
||||
PenaltyScoreExceeded bool `json:"penalty_score_exceeded"`
|
||||
|
||||
@@ -25,6 +30,14 @@ type GetCurrentSellerRatingInfoResponse struct {
|
||||
Premium bool `json:"premium"`
|
||||
}
|
||||
|
||||
type LocalizationIndex struct {
|
||||
// Date of localization index calculation
|
||||
CalculationDate time.Time `json:"calculation_date"`
|
||||
|
||||
// Localization index value
|
||||
LocalizationPercentage int32 `json:"localization_percentage"`
|
||||
}
|
||||
|
||||
type GetCurrentSellerRatingInfoGroup struct {
|
||||
// Ratings group name
|
||||
GroupName string `json:"group_name"`
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetCurrentRatingInfo(t *testing.T) {
|
||||
@@ -41,6 +41,12 @@ func TestGetCurrentRatingInfo(t *testing.T) {
|
||||
]
|
||||
}
|
||||
],
|
||||
"localization_index": [
|
||||
{
|
||||
"calculation_date": "2019-08-24T14:15:22Z",
|
||||
"localization_percentage": 0
|
||||
}
|
||||
],
|
||||
"penalty_score_exceeded": true,
|
||||
"premium": true
|
||||
}`,
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Reports struct {
|
||||
@@ -463,8 +463,15 @@ type GetReturnsReportsFilter struct {
|
||||
// Order delivery scheme: fbs — delivery from seller's warehouse
|
||||
DeliverySchema string `json:"delivery_schema"`
|
||||
|
||||
// Order identifier
|
||||
OrderId int64 `json:"order_id"`
|
||||
// Date from which the data is displayed in the report.
|
||||
//
|
||||
// Available for the last three months only
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
|
||||
// Date up to which the data is displayed in the report.
|
||||
//
|
||||
// Available for the last three months only
|
||||
DateTo time.Time `json:"date_to"`
|
||||
|
||||
// Order status
|
||||
Status string `json:"status"`
|
||||
@@ -473,20 +480,17 @@ type GetReturnsReportsFilter struct {
|
||||
type GetReturnsReportResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Method result
|
||||
Result GetReturnReportResult `json:"result"`
|
||||
Result GetReturnsReportResult `json:"result"`
|
||||
}
|
||||
|
||||
type GetReturnReportResult struct {
|
||||
type GetReturnsReportResult struct {
|
||||
// Unique report identifier. The report is available for downloading within 3 days after making a request.
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// The report contains information about returned products that were accepted from the customer, ready for pickup, or delivered to the seller.
|
||||
//
|
||||
// The method is only suitable for orders shipped from the seller's warehouse
|
||||
// Method for getting a report on FBO and FBS returns
|
||||
func (c Reports) GetReturns(ctx context.Context, params *GetReturnsReportParams) (*GetReturnsReportResponse, error) {
|
||||
url := "/v1/report/returns/create"
|
||||
url := "/v2/report/returns/create"
|
||||
|
||||
resp := &GetReturnsReportResponse{}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetList(t *testing.T) {
|
||||
@@ -381,7 +381,7 @@ func TestGetReturnsReport(t *testing.T) {
|
||||
},
|
||||
`{
|
||||
"result": {
|
||||
"code": "d55f4517-8347-4e24-9d93-d6e736c1c07c"
|
||||
"code": "REPORT_seller_products_924336_1720170405_a9ea2f27-a473-4b13-99f9-d0cfcb5b1a69"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
@@ -407,16 +407,12 @@ func TestGetReturnsReport(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetReturnsReportResponse{})
|
||||
|
||||
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.Code == "" {
|
||||
t.Errorf("Code cannot be empty")
|
||||
}
|
||||
compareJsonResponse(t, test.response, &GetReturnsReportResponse{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
490
ozon/returns.go
490
ozon/returns.go
@@ -5,132 +5,13 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Returns struct {
|
||||
client *core.Client
|
||||
}
|
||||
|
||||
type GetFBOReturnsParams struct {
|
||||
// Filter
|
||||
Filter *GetFBOReturnsFilter `json:"filter,omitempty"`
|
||||
|
||||
// Identifier of the last value on the page. Leave this field blank in the first request.
|
||||
//
|
||||
// To get the next values, specify the recieved value in the next request in the `last_id` parameter
|
||||
LastId int64 `json:"last_id"`
|
||||
|
||||
// Number of values in the response
|
||||
Limit int64 `json:"limit"`
|
||||
}
|
||||
|
||||
type GetFBOReturnsFilter struct {
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Return status
|
||||
Status []GetFBOReturnsFilterStatus `json:"status"`
|
||||
}
|
||||
|
||||
type GetFBOReturnsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Identifier of the last value on the page
|
||||
LastId int64 `json:"last_id"`
|
||||
|
||||
// Returns information
|
||||
Returns []GetFBOReturnsReturn `json:"returns"`
|
||||
}
|
||||
|
||||
type GetFBOReturnsReturn struct {
|
||||
// Time when a return was received from the customer
|
||||
AcceptedFromCustomerMoment time.Time `json:"accepted_from_customer_moment"`
|
||||
|
||||
// Seller identifier
|
||||
CompanyId int64 `json:"company_id"`
|
||||
|
||||
// Current return location
|
||||
CurrentPlaceName string `json:"current_place_name"`
|
||||
|
||||
// Return destination
|
||||
DestinationPlaceName string `json:"dst_place_name"`
|
||||
|
||||
// Return shipment identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Indication that the package has been opened. true, if it has been
|
||||
IsOpened bool `json:"is_opened"`
|
||||
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Unique return record identifier
|
||||
ReturnId int64 `json:"return_id"`
|
||||
|
||||
// Return reason
|
||||
ReturnReasonName string `json:"return_reason_name"`
|
||||
|
||||
// Return delivery time to the Ozon warehouse
|
||||
ReturnedToOzonMoment time.Time `json:"returned_to_ozon_moment"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Return status
|
||||
Status GetFBOReturnsReturnStatus `json:"status_name"`
|
||||
}
|
||||
|
||||
// Method for getting information on returned products that are sold from the Ozon warehouse
|
||||
func (c Returns) GetFBOReturns(ctx context.Context, params *GetFBOReturnsParams) (*GetFBOReturnsResponse, error) {
|
||||
url := "/v3/returns/company/fbo"
|
||||
|
||||
resp := &GetFBOReturnsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetFBSReturnsParams struct {
|
||||
// Filter
|
||||
Filter *GetFBSReturnsFilter `json:"filter,omitempty"`
|
||||
|
||||
// Number of values in the response:
|
||||
// - maximum — 1000,
|
||||
// - minimum — 1
|
||||
Limit int64 `json:"limit"`
|
||||
|
||||
// Return identifier that was loaded the last time.
|
||||
// Return identifiers with the higher value than `last_id`
|
||||
// will be returned in the response.
|
||||
LastId int64 `json:"offset"`
|
||||
}
|
||||
|
||||
type GetFBSReturnsFilter struct {
|
||||
// Last day of free storage
|
||||
LastFreeWaitingDay GetFBSReturnsFilterTimeRange `json:"last_free_waiting_dat"`
|
||||
|
||||
// Order ID
|
||||
OrderId int64 `json:"order_id"`
|
||||
|
||||
// Shipment ID
|
||||
PostingNumber []string `json:"posting_number"`
|
||||
|
||||
// Product name
|
||||
ProductName string `json:"product_name"`
|
||||
|
||||
// Product ID
|
||||
ProductOfferId string `json:"product_offer_id"`
|
||||
|
||||
// Return status
|
||||
Status GetFBSReturnsFilterStatus `json:"status"`
|
||||
}
|
||||
|
||||
type GetFBSReturnsFilterTimeRange struct {
|
||||
// The beginning of the period.
|
||||
//
|
||||
@@ -147,118 +28,6 @@ type GetFBSReturnsFilterTimeRange struct {
|
||||
TimeTo time.Time `json:"time_to"`
|
||||
}
|
||||
|
||||
type GetFBSReturnsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Return identifier that was loaded the last time.
|
||||
// Return identifiers with the higher value than `last_id`
|
||||
// will be returned in the response
|
||||
LastId int64 `json:"last_id"`
|
||||
|
||||
// Returns information
|
||||
Returns []GetFBSReturnResultReturn `json:"returns"`
|
||||
}
|
||||
|
||||
type GetFBSReturnResultReturn struct {
|
||||
// Bottom barcode on the product label
|
||||
ClearingId int64 `json:"clearing_id"`
|
||||
|
||||
// Commission fee
|
||||
Commission float64 `json:"commission"`
|
||||
|
||||
// Commission percentage
|
||||
CommissionPercent float64 `json:"commission_percent"`
|
||||
|
||||
// Product item identifier in the Ozon logistics system
|
||||
ExemplarId int64 `json:"exemplar_id"`
|
||||
|
||||
// Return identifier in the Ozon accounting system
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// If the product is in transit — true
|
||||
IsMoving bool `json:"is_moving"`
|
||||
|
||||
// Indication that the package has been opened. true, if it has been
|
||||
IsOpened bool `json:"is_opened"`
|
||||
|
||||
// Last day of free storage
|
||||
LastFreeWaitingDay string `json:"last_free_waiting_day"`
|
||||
|
||||
// ID of the warehouse the product is being transported to
|
||||
PlaceId int64 `json:"place_id"`
|
||||
|
||||
// Intermediate return point
|
||||
MovingToPlaceName string `json:"moving_to_place_name"`
|
||||
|
||||
// Delivery cost
|
||||
PickingAmount float64 `json:"picking_amount"`
|
||||
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
PickingTag string `json:"picking_tag"`
|
||||
|
||||
// Current product price without a discount
|
||||
Price float64 `json:"price"`
|
||||
|
||||
// Product price without commission
|
||||
PriceWithoutCommission float64 `json:"price_without_commission"`
|
||||
|
||||
// Product identifier — SKU
|
||||
ProductId int64 `json:"product_id"`
|
||||
|
||||
// Product name
|
||||
ProductName string `json:"product_name"`
|
||||
|
||||
// Product quantity
|
||||
Quantity int64 `json:"quantity"`
|
||||
|
||||
// Barcode on the return label. Use this parameter value to work with the return label
|
||||
ReturnBarcode string `json:"return_barcode"`
|
||||
|
||||
// Package unit identifier in the Ozon logistics system
|
||||
ReturnClearingId int64 `json:"return_clearing_id"`
|
||||
|
||||
// Product return date
|
||||
ReturnDate string `json:"return_date"`
|
||||
|
||||
// Return reason
|
||||
ReturnReasonName string `json:"return_reason_name"`
|
||||
|
||||
// Date when the product is ready to be handed over to the seller
|
||||
WaitingForSellerDate string `json:"waiting_for_seller_date_time"`
|
||||
|
||||
// Date of handing over the product to the seller
|
||||
ReturnedToSellerDate string `json:"returned_to_seller_date_time"`
|
||||
|
||||
// Return storage period in days
|
||||
WaitingForSellerDays int64 `json:"waiting_for_seller_days"`
|
||||
|
||||
// Return storage cost
|
||||
ReturnsKeepingCost float64 `json:"returns_keeping_cost"`
|
||||
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Return status
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// Method for getting information on returned products that are sold from the seller's warehouse
|
||||
func (c Returns) GetFBSReturns(ctx context.Context, params *GetFBSReturnsParams) (*GetFBSReturnsResponse, error) {
|
||||
url := "/v3/returns/company/fbs"
|
||||
|
||||
resp := &GetFBSReturnsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type GetRFBSReturnsParams struct {
|
||||
// Filter
|
||||
Filter *GetRFBSReturnsFilter `json:"filter,omitempty"`
|
||||
@@ -898,9 +667,6 @@ type GetFBSQuantityReturnsPagination struct {
|
||||
type GetFBSQuantityReturnsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Seller identifier
|
||||
CompanyId int64 `json:"company_id"`
|
||||
|
||||
DropoffPoints []GetFBSQuantityDropoffPoint `json:"drop_off_points"`
|
||||
|
||||
// true if there are any other points where sellers have orders waiting
|
||||
@@ -928,6 +694,12 @@ type GetFBSQuantityDropoffPoint struct {
|
||||
|
||||
// Seller's warehouses identifiers
|
||||
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 {
|
||||
@@ -943,7 +715,253 @@ func (c Returns) FBSQuantity(ctx context.Context, params *GetFBSQuantityReturnsP
|
||||
|
||||
resp := &GetFBSQuantityReturnsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, nil, resp, nil)
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ListReturnsParams struct {
|
||||
// Filters. Use only one filter per request. Otherwise it returns an error
|
||||
Filter *ListReturnsFilter `json:"filter,omitempty"`
|
||||
|
||||
// Number of loaded returns. The maximum value is 500
|
||||
Limit int32 `json:"limit"`
|
||||
|
||||
// Identifier of the last loaded return
|
||||
LastId int64 `json:"last_id"`
|
||||
}
|
||||
|
||||
type ListReturnsFilter struct {
|
||||
// Filter by return creation date
|
||||
LogisticReturnDate *GetFBSReturnsFilterTimeRange `json:"logistic_return_date"`
|
||||
|
||||
// Filter by storage fees start date
|
||||
StorageTarifficationDate *GetFBSReturnsFilterTimeRange `json:"storage_tariffication_start_date"`
|
||||
|
||||
// Filter by date the return status changed
|
||||
VisualStatusChangeMoment *GetFBSReturnsFilterTimeRange `json:"visual_status_change_moment"`
|
||||
|
||||
// Filter by order identifier
|
||||
OrderId int64 `json:"order_id,omitempty"`
|
||||
|
||||
// Filter by shipment number. Pass no more than 50 postings
|
||||
PostingNumbers []string `json:"posting_numbers,omitempty"`
|
||||
|
||||
// Filter by product name
|
||||
ProductName string `json:"product_name,omitempty"`
|
||||
|
||||
// Filter by product identifier in the seller's system
|
||||
OfferId string `json:"offer_id,omitempty"`
|
||||
|
||||
// Filter by return status
|
||||
VisualStatusName VisualStatus `json:"visual_status_name,omitempty"`
|
||||
|
||||
// Filter by warehouse identifier
|
||||
WarehouseId int64 `json:"warehouse_id,omitempty"`
|
||||
|
||||
// Filter by return label barcode
|
||||
Barcode string `json:"barcode,omitempty"`
|
||||
|
||||
// Filter by delivery scheme: FBS or FBO
|
||||
ReturnSchema string `json:"return_schema,omitempty"`
|
||||
}
|
||||
|
||||
type ListReturnsResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Returns details
|
||||
Returns []Return `json:"returns"`
|
||||
|
||||
// true, if the seller has other returns
|
||||
HasNext bool `json:"has_next"`
|
||||
}
|
||||
|
||||
type Return struct {
|
||||
// Product items data
|
||||
Exemplars []ReturnExemplar `json:"exemplars"`
|
||||
|
||||
// Return identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Company identifier
|
||||
CompanyId int64 `json:"company_id"`
|
||||
|
||||
// Return reason
|
||||
ReturnReasonName string `json:"return_reason_name"`
|
||||
|
||||
// Return type
|
||||
Type string `json:"type"`
|
||||
|
||||
// Return scheme
|
||||
Schema string `json:"schema"`
|
||||
|
||||
// Order identifier
|
||||
OrderId int64 `json:"order_id"`
|
||||
|
||||
// Order number
|
||||
OrderNumber string `json:"order_number"`
|
||||
|
||||
// Warehouse where the return is stored
|
||||
Place ReturnPlace `json:"place"`
|
||||
|
||||
// Warehouse where returns are sent to
|
||||
TargetPlace ReturnPlace `json:"target_place"`
|
||||
|
||||
// Storage details
|
||||
Storage ReturnStorage `json:"storage"`
|
||||
|
||||
// Product details
|
||||
Product ReturnProduct `json:"product"`
|
||||
|
||||
// Return details
|
||||
Logistic ReturnLogistic `json:"logistic"`
|
||||
|
||||
// Return status details
|
||||
Visual ReturnVisual `json:"visual"`
|
||||
|
||||
// Additional information
|
||||
AdditionalInfo ReturnAdditionalInfo `json:"additional_info"`
|
||||
|
||||
// Previous return identifier
|
||||
SourceId int64 `json:"source_id"`
|
||||
|
||||
// Shipment number
|
||||
PostingNumber string `json:"posting_number"`
|
||||
|
||||
// Original shipment barcode
|
||||
ClearingId int64 `json:"clearing_id"`
|
||||
|
||||
// Package unit identifier in the Ozon logistics system
|
||||
ReturnClearingId int64 `json:"return_clearing_id"`
|
||||
}
|
||||
|
||||
type ReturnExemplar struct {
|
||||
// Product identifier
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
type ReturnPlace struct {
|
||||
// Warehouse identifier
|
||||
Id int64 `json:"id"`
|
||||
|
||||
// Warehouse name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Warehouse address
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type ReturnStorage struct {
|
||||
// Storage cost details
|
||||
Sum ReturnSum `json:"sum"`
|
||||
|
||||
// First day of charging for storage
|
||||
TarifficationsFirstDate time.Time `json:"tariffication_first_date"`
|
||||
|
||||
// Start date for storage fees
|
||||
TarifficationsStartDate time.Time `json:"tariffication_start_date"`
|
||||
|
||||
// Date when the return was ready for handover
|
||||
ArrivedMoment time.Time `json:"arrived_moment"`
|
||||
|
||||
// Number of days the return has been waiting for handover
|
||||
Days int64 `json:"days"`
|
||||
|
||||
// Disposal cost details
|
||||
UtilizationSum ReturnSum `json:"utilization_sum"`
|
||||
|
||||
// Planned disposal date
|
||||
UtilizationForecastDate string `json:"utilization_forecast_date"`
|
||||
}
|
||||
|
||||
type ReturnSum struct {
|
||||
// Currency
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
|
||||
// Disposal cost
|
||||
Price float64 `json:"price"`
|
||||
}
|
||||
|
||||
type ReturnProduct struct {
|
||||
// Product identifier in the Ozon system, SKU
|
||||
SKU int64 `json:"sku"`
|
||||
|
||||
// Product identifier in the seller's system
|
||||
OfferId string `json:"offer_id"`
|
||||
|
||||
// product name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Product price details
|
||||
Price ReturnSum `json:"price"`
|
||||
|
||||
// Product cost without commission
|
||||
PriceWithoutCommission ReturnSum `json:"price_without_commission"`
|
||||
|
||||
// Sales commission by category
|
||||
CommissionPercent float64 `json:"commission_percent"`
|
||||
|
||||
// Commission details
|
||||
Commission ReturnSum `json:"commission"`
|
||||
|
||||
// Product quantity
|
||||
Quantity int32 `json:"quantity"`
|
||||
}
|
||||
|
||||
type ReturnLogistic struct {
|
||||
// Date when the order was placed for technical return
|
||||
TechnicalReturnMoment time.Time `json:"technical_return_moment"`
|
||||
|
||||
// Date when the return arrived to the warehouse or was handed over to the seller
|
||||
FinalMoment time.Time `json:"final_moment"`
|
||||
|
||||
// Date when the seller received compensation for the return
|
||||
CancelledWithCompensationMoment time.Time `json:"cancelled_with_compensation_moment"`
|
||||
|
||||
// Date when the customer returned the product
|
||||
ReturnDate time.Time `json:"return_date"`
|
||||
|
||||
// Return label barcode
|
||||
Barcode string `json:"barcode"`
|
||||
}
|
||||
|
||||
type ReturnVisual struct {
|
||||
// Return status
|
||||
Status ReturnVisualStatus `json:"status"`
|
||||
|
||||
// Date the return status changed
|
||||
ChangeMoment time.Time `json:"change_moment"`
|
||||
}
|
||||
|
||||
type ReturnVisualStatus struct {
|
||||
// Return status identifier
|
||||
Id int32 `json:"id"`
|
||||
|
||||
// Return status name
|
||||
DisplayName string `json:"display_name"`
|
||||
|
||||
// System name of the return status
|
||||
SystemName string `json:"sys_name"`
|
||||
}
|
||||
|
||||
type ReturnAdditionalInfo struct {
|
||||
// true, if the return package is opened
|
||||
IsOpened bool `json:"is_opened"`
|
||||
|
||||
// true, if the return belongs to Super Economy products
|
||||
IsSuperEconom bool `json:"is_super_econom"`
|
||||
}
|
||||
|
||||
func (c Returns) List(ctx context.Context, params *ListReturnsParams) (*ListReturnsResponse, error) {
|
||||
url := "/v1/returns/list"
|
||||
|
||||
resp := &ListReturnsResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,196 +5,9 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetFBOReturns(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetFBOReturnsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetFBOReturnsParams{
|
||||
Filter: &GetFBOReturnsFilter{
|
||||
PostingNumber: "some number",
|
||||
},
|
||||
LastId: 123,
|
||||
Limit: 100,
|
||||
},
|
||||
`{
|
||||
"last_id": 0,
|
||||
"returns": [
|
||||
{
|
||||
"accepted_from_customer_moment": "2019-08-24T14:15:22Z",
|
||||
"company_id": 123456789,
|
||||
"current_place_name": "my-place",
|
||||
"dst_place_name": "that-place",
|
||||
"id": 123456789,
|
||||
"is_opened": true,
|
||||
"posting_number": "some number",
|
||||
"return_reason_name": "ripped",
|
||||
"returned_to_ozon_moment": "2019-08-24T14:15:22Z",
|
||||
"sku": 123456789,
|
||||
"status_name": "delivering"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetFBOReturnsParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Returns().GetFBOReturns(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetFBOReturnsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Returns) > 0 {
|
||||
if resp.Returns[0].Id == 0 {
|
||||
t.Errorf("Id cannot be 0")
|
||||
}
|
||||
if resp.Returns[0].CompanyId == 0 {
|
||||
t.Errorf("Company id cannot be 0")
|
||||
}
|
||||
if resp.Returns[0].SKU == 0 {
|
||||
t.Errorf("SKU cannot be 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFBSReturns(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *GetFBSReturnsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&GetFBSReturnsParams{
|
||||
Filter: &GetFBSReturnsFilter{
|
||||
PostingNumber: []string{"07402477-0022-2"},
|
||||
Status: "returned_to_seller",
|
||||
},
|
||||
Limit: 1000,
|
||||
LastId: 0,
|
||||
},
|
||||
`{
|
||||
"last_id": 0,
|
||||
"returns": [
|
||||
{
|
||||
"clearing_id": 23,
|
||||
"commission": 21,
|
||||
"commission_percent": 0,
|
||||
"exemplar_id": 42,
|
||||
"id": 123,
|
||||
"is_moving": true,
|
||||
"is_opened": true,
|
||||
"last_free_waiting_day": "string",
|
||||
"place_id": 122,
|
||||
"moving_to_place_name": "string",
|
||||
"picking_amount": 0,
|
||||
"posting_number": "string",
|
||||
"picking_tag": "string",
|
||||
"price": 0,
|
||||
"price_without_commission": 0,
|
||||
"product_id": 2222,
|
||||
"product_name": "string",
|
||||
"quantity": 0,
|
||||
"return_barcode": "string",
|
||||
"return_clearing_id": 0,
|
||||
"return_date": "string",
|
||||
"return_reason_name": "string",
|
||||
"waiting_for_seller_date_time": "string",
|
||||
"returned_to_seller_date_time": "string",
|
||||
"waiting_for_seller_days": 0,
|
||||
"returns_keeping_cost": 0,
|
||||
"sku": 33332,
|
||||
"status": "string"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&GetFBSReturnsParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Returns().GetFBSReturns(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &GetFBSReturnsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
if len(resp.Returns) > 0 {
|
||||
if resp.Returns[0].Id == 0 {
|
||||
t.Errorf("Id cannot be 0")
|
||||
}
|
||||
if resp.Returns[0].ProductId == 0 {
|
||||
t.Errorf("Product id cannot be 0")
|
||||
}
|
||||
if resp.Returns[0].SKU == 0 {
|
||||
t.Errorf("SKU cannot be 0")
|
||||
}
|
||||
if resp.Returns[0].Status == "" {
|
||||
t.Errorf("Status cannot be empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRFBSReturns(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1010,10 +823,10 @@ func TestFBSQuantity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
`{
|
||||
"company_id": 0,
|
||||
"drop_off_points": [
|
||||
{
|
||||
"address": "string",
|
||||
"box_count": 0,
|
||||
"id": 0,
|
||||
"name": "string",
|
||||
"pass_info": {
|
||||
@@ -1022,6 +835,7 @@ func TestFBSQuantity(t *testing.T) {
|
||||
},
|
||||
"place_id": 0,
|
||||
"returns_count": 0,
|
||||
"utc_offset": "string",
|
||||
"warehouses_ids": [
|
||||
"string"
|
||||
]
|
||||
@@ -1059,3 +873,154 @@ func TestFBSQuantity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListReturns(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListReturnsParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListReturnsParams{
|
||||
Filter: &ListReturnsFilter{
|
||||
LogisticReturnDate: &GetFBSReturnsFilterTimeRange{
|
||||
TimeFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
TimeTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
},
|
||||
StorageTarifficationDate: &GetFBSReturnsFilterTimeRange{
|
||||
TimeFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
TimeTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
},
|
||||
VisualStatusChangeMoment: &GetFBSReturnsFilterTimeRange{
|
||||
TimeFrom: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
TimeTo: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2019-08-24T14:15:22Z"),
|
||||
},
|
||||
WarehouseId: 911,
|
||||
ReturnSchema: "FBO",
|
||||
ProductName: "string",
|
||||
},
|
||||
Limit: 500,
|
||||
LastId: 0,
|
||||
},
|
||||
`{
|
||||
"returns": [
|
||||
{
|
||||
"exemplars": [
|
||||
{
|
||||
"id": 1019562967545956
|
||||
}
|
||||
],
|
||||
"id": 1000015552,
|
||||
"company_id": 3058,
|
||||
"return_reason_name": "Customer refused on receipt: not satisfied with the quality of the product",
|
||||
"type": "FullReturn",
|
||||
"schema": "Fbs",
|
||||
"order_id": 24540784250,
|
||||
"order_number": "58544282-0057",
|
||||
"place": {
|
||||
"id": 23869688194000,
|
||||
"name": "СЦ_Львовский_Возвраты",
|
||||
"address": "Россия, обл. Московская, г. Подольск, промышленная зона Львовский, ул. Московская, д. 69, стр. 5"
|
||||
},
|
||||
"target_place": {
|
||||
"id": 23869688194000,
|
||||
"name": "СЦ_Львовский_Возвраты",
|
||||
"address": "Россия, обл. Московская, г. Подольск, промышленная зона Львовский, ул. Московская, д. 69, стр. 5"
|
||||
},
|
||||
"storage": {
|
||||
"sum": {
|
||||
"currency_code": "RUB",
|
||||
"price": 1231
|
||||
},
|
||||
"tariffication_first_date": "2024-07-30T06:15:48.998146Z",
|
||||
"tariffication_start_date": "2024-07-29T06:15:48.998146Z",
|
||||
"arrived_moment": "2024-07-29T06:15:48.998146Z",
|
||||
"days": 0,
|
||||
"utilization_sum": {
|
||||
"currency_code": "RUB",
|
||||
"price": 1231
|
||||
},
|
||||
"utilization_forecast_date": "2024-07-29T06:15:48.998146Z"
|
||||
},
|
||||
"product": {
|
||||
"sku": 1100526203,
|
||||
"offer_id": "81451",
|
||||
"name": "Кукла Дотти Плачущий младенец Cry Babies Dressy Dotty",
|
||||
"price": {
|
||||
"currency_code": "RUB",
|
||||
"price": 3318
|
||||
},
|
||||
"price_without_commission": {
|
||||
"currency_code": "RUB",
|
||||
"price": 3318
|
||||
},
|
||||
"commission_percent": 1.2,
|
||||
"commission": {
|
||||
"currency_code": "RUB",
|
||||
"price": 2312
|
||||
},
|
||||
"quantity": 1
|
||||
},
|
||||
"logistic": {
|
||||
"technical_return_moment": "2024-07-29T06:15:48.998146Z",
|
||||
"final_moment": "2024-07-29T06:15:48.998146Z",
|
||||
"cancelled_with_compensation_moment": "2024-07-29T06:15:48.998146Z",
|
||||
"return_date": "2024-07-29T06:15:48.998146Z",
|
||||
"barcode": "ii5275210303"
|
||||
},
|
||||
"visual": {
|
||||
"status": {
|
||||
"id": 3,
|
||||
"display_name": "At the pick-up point",
|
||||
"sys_name": "ArrivedAtReturnPlace"
|
||||
},
|
||||
"change_moment": "2024-07-29T06:15:48.998146Z"
|
||||
},
|
||||
"additional_info": {
|
||||
"is_opened": true,
|
||||
"is_super_econom": false
|
||||
},
|
||||
"source_id": 90426223,
|
||||
"posting_number": "58544282-0057-1",
|
||||
"clearing_id": 21190893156000,
|
||||
"return_clearing_id": null
|
||||
}
|
||||
],
|
||||
"has_next": false
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListReturnsParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Returns().List(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListReturnsResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
334
ozon/reviews.go
Normal file
334
ozon/reviews.go
Normal file
@@ -0,0 +1,334 @@
|
||||
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
|
||||
}
|
||||
416
ozon/reviews_test.go
Normal file
416
ozon/reviews_test.go
Normal file
@@ -0,0 +1,416 @@
|
||||
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"
|
||||
"net/http"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Strategies struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestListCompetitors(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
type Warehouses struct {
|
||||
@@ -48,6 +48,9 @@ type GetListOfWarehousesResult struct {
|
||||
// Indication that the warehouse accepts bulky products
|
||||
IsKGT bool `json:"is_kgt"`
|
||||
|
||||
// true if the warehouse handles economy products
|
||||
IsEconomy bool `json:"is_economy"`
|
||||
|
||||
// Indication that warehouse schedule can be changed
|
||||
IsTimetableEditable bool `json:"is_timetable_editable"`
|
||||
|
||||
@@ -85,7 +88,8 @@ type GetListOfWarehousesResultFirstMile struct {
|
||||
FirstMileType string `json:"first_mile_type"`
|
||||
}
|
||||
|
||||
// You do not need to specify any parameters in the request. Your company will be identified by the Warehouses ID
|
||||
// Method returns the list of FBS and rFBS warehouses.
|
||||
// To get the list of FBO warehouses, use the /v1/cluster/list method.
|
||||
func (c Warehouses) GetListOfWarehouses(ctx context.Context) (*GetListOfWarehousesResponse, error) {
|
||||
url := "/v1/warehouse/list"
|
||||
|
||||
@@ -158,6 +162,9 @@ type GetListOfDeliveryMethodsResult struct {
|
||||
// Delivery service identifier
|
||||
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:
|
||||
// - NEW—created,
|
||||
// - EDITED—being edited,
|
||||
@@ -189,3 +196,60 @@ func (c Warehouses) GetListOfDeliveryMethods(ctx context.Context, params *GetLis
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type ListForShippingParams struct {
|
||||
// Supply type
|
||||
FilterBySupplyType []string `json:"filter_by_supply_type"`
|
||||
|
||||
// Search by warehouse name. To search for pick-up points, specify the full name
|
||||
Search string `json:"search"`
|
||||
}
|
||||
|
||||
type ListForShippingResponse struct {
|
||||
core.CommonResponse
|
||||
|
||||
// Warehouse search result
|
||||
Search []ListForShippingSearch `json:"search"`
|
||||
}
|
||||
|
||||
type ListForShippingSearch struct {
|
||||
// Warehouse address
|
||||
Address string `json:"address"`
|
||||
|
||||
// Warehouse coordinates
|
||||
Coordinates Coordinates `json:"coordinates"`
|
||||
|
||||
// Warehouse name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Identifier of the warehouse, pick-up point, or sorting center
|
||||
WarehouseId int64 `json:"warehouse_id"`
|
||||
|
||||
// Type of warehouse, pick-up point, or sorting center
|
||||
WarehouseType string `json:"warehouse_type"`
|
||||
}
|
||||
|
||||
type Coordinates struct {
|
||||
// Latitude
|
||||
Latitude float64 `json:"latitude"`
|
||||
|
||||
// Longitude
|
||||
Longitude float64 `json:"longitude"`
|
||||
}
|
||||
|
||||
// Use the method to find sorting centres, pick-up points, and drop-off 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) {
|
||||
url := "/v1/warehouse/fbo/list"
|
||||
|
||||
resp := &ListForShippingResponse{}
|
||||
|
||||
response, err := c.client.Request(ctx, http.MethodPost, url, params, resp, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.CopyCommonResponse(&resp.CommonResponse)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
core "github.com/diphantxm/ozon-api-client"
|
||||
core "git.denco.store/fakz9/ozon-api-client"
|
||||
)
|
||||
|
||||
func TestGetListOfWarehouses(t *testing.T) {
|
||||
@@ -127,7 +127,8 @@ func TestGetListOfDeliveryMethods(t *testing.T) {
|
||||
"template_id": 0,
|
||||
"warehouse_id": 15588127982000,
|
||||
"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
|
||||
@@ -179,3 +180,65 @@ func TestGetListOfDeliveryMethods(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListForShipping(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
headers map[string]string
|
||||
params *ListForShippingParams
|
||||
response string
|
||||
}{
|
||||
// Test Ok
|
||||
{
|
||||
http.StatusOK,
|
||||
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
|
||||
&ListForShippingParams{
|
||||
FilterBySupplyType: []string{"CREATE_TYPE_UNKNOWN"},
|
||||
Search: "string",
|
||||
},
|
||||
`{
|
||||
"search": [
|
||||
{
|
||||
"address": "string",
|
||||
"coordinates": {
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
},
|
||||
"name": "string",
|
||||
"warehouse_id": 0,
|
||||
"warehouse_type": "WAREHOUSE_TYPE_UNKNOWN"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
// Test No Client-Id or Api-Key
|
||||
{
|
||||
http.StatusUnauthorized,
|
||||
map[string]string{},
|
||||
&ListForShippingParams{},
|
||||
`{
|
||||
"code": 16,
|
||||
"message": "Client-Id and Api-Key headers are required"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), testTimeout)
|
||||
resp, err := c.Warehouses().ListForShipping(ctx, test.params)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
compareJsonResponse(t, test.response, &ListForShippingResponse{})
|
||||
|
||||
if resp.StatusCode != test.statusCode {
|
||||
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user