36 Commits

Author SHA1 Message Date
diPhantxm
7beee39eb2 Added the prices.price_strategy_enabled parameter to the method request 2023-07-20 02:48:09 +03:00
diPhantxm
baeeef9b46 fix json names for GetDigitalActResponse 2023-07-15 03:02:45 +03:00
diPhantxm
336c49baa4 Added the response example to TestGetLabeling 2023-07-15 03:02:45 +03:00
diPhantxm
f11ccb4714 updated the schemas and examples for method responses: methods return the result in binary format 2023-07-15 03:02:45 +03:00
diPhantxm
1958dfb94e Added a method for getting a barcode from the /v2/posting/fbs/act/get-barcode response in text format 2023-07-12 19:14:48 +03:00
diPhantxm
2ca20d9b12 Added a method for getting a barcode used during product shipment 2023-07-12 19:14:48 +03:00
Anton Salnikov
eea0f99066 Change address field tag 2023-07-09 19:54:28 +03:00
diPhantxm
7adaa92ad4 Specify enum values for status in GetFBOReturns method 2023-07-06 19:48:33 +03:00
diPhantxm
f54aa64b63 Update GetFBSReturns to v3. Add exemplar_id and return_barcode fields to method reponse 2023-07-06 19:39:54 +03:00
diPhantxm
e7c5dc320e fix params field in GetReportsListResultReport 2023-06-24 02:06:18 +03:00
diPhantxm
006ecdf877 commmented all data types and methods 2023-06-23 23:23:23 +03:00
diPhantxm
f81e4ed350 flat structures 2023-06-23 20:57:38 +03:00
Kirill
e7290069f5 Create LICENSE
Add MIT License
2023-06-20 23:33:02 +03:00
diphantxm
422f959b9f add prr option to some methods 2023-06-16 21:52:49 +03:00
diphantxm
ac99c5ba2f added new status to getting act list filter, updated descriptions, changed test according to examples 2023-06-16 21:35:33 +03:00
diPhantxm
e60a3f1ca2 add method for getting ozon warehouses workload 2023-06-02 18:55:45 +03:00
diPhantxm
2f1dbd5c00 add method for updating product characteristics 2023-06-02 18:55:33 +03:00
diPhantxm
387af0e30e add pricing strategy methods 2023-04-24 01:03:24 +03:00
diPhantxm
c1e7f2b370 add optional paramter with_details to request and details to response in GetFinancial in reports 2023-04-24 01:03:24 +03:00
diPhantxm
a1c92ae26f change price index to multiple indexes in product/info/prices 2023-04-12 01:32:24 +03:00
diPhantxm
08180d901c update posting methods status/substatus, changed price index to multiple indexes in products responses 2023-04-12 01:32:24 +03:00
diPhantxm
838f28d3d9 fix 2023-04-11 23:20:46 +03:00
diPhantxm
226f40275a add supply order methods 2023-04-11 23:18:10 +03:00
Kirill
4139692ac3 Merge pull request #5 from diPhantxm/refactor-enum-types
add enum types where possible
2023-04-02 12:54:17 +03:00
diPhantxm
3507be6ec2 add enum types where possible 2023-04-02 12:52:55 +03:00
Kirill
9990719060 Merge pull request #4 from diPhantxm/feature/add-content-type-to-request
add Content-Type to request header
2023-03-25 17:22:33 +03:00
diPhantxm
18470fcae5 add Content-Type to request header 2023-03-25 17:18:20 +03:00
diPhantxm
3af6ad40c5 add methods for quality certificates 2023-03-23 23:41:02 +03:00
diPhantxm
89816cb239 add methods for chats with customers 2023-03-23 22:45:37 +03:00
diPhantxm
3f1f0711c8 add methods for brands 2023-03-23 21:55:02 +03:00
diPhantxm
6b921ad101 add methods for invoices 2023-03-23 21:45:02 +03:00
diPhantxm
021a577bf9 add methods for polygons 2023-03-23 00:22:27 +03:00
diPhantxm
c1661f8cb9 add methods for ozon attributes and characteristics 2023-03-21 19:31:52 +03:00
diPhantxm
098b257746 add all methods for cancellations, add methods for getting fbo shipment details 2023-03-20 02:36:46 +03:00
diPhantxm
9effb88b5f add all methods for promotions 2023-03-20 01:24:54 +03:00
diPhantxm
533a2439de add remained methods for managing prices and stocks 2023-03-19 23:20:45 +03:00
40 changed files with 11742 additions and 1993 deletions

View File

@@ -1,9 +1,9 @@
# Supported Endpoints # Supported Endpoints
## Ozon attributes and characteristics ## Ozon attributes and characteristics
- [ ] Product category tree - [x] Product category tree
- [ ] Category characteristics list - [x] Category characteristics list
- [ ] Characteristics value directory - [x] Characteristics value directory
## Uploading and updating products ## Uploading and updating products
- [x] Create or update a product - [x] Create or update a product
@@ -32,55 +32,54 @@
- [x] Information about product quantity - [x] Information about product quantity
- [x] Stocks in seller's warehouses (FBS и rFBS) - [x] Stocks in seller's warehouses (FBS и rFBS)
- [x] Update prices - [x] Update prices
- [ ] Get product price information - [x] Get product price information
- [ ] Get information about the markdown and the main product by the markdown product SKU - [x] Get information about the markdown and the main product by the markdown product SKU
- [ ] Set a discount on a markdown product - [x] Set a discount on a markdown product
## Promotions ## Promotions
- [x] Available promotions - [x] Available promotions
- [ ] Products that can participate in a promotion - [x] Products that can participate in a promotion
- [ ] Products in a promotion - [x] Products in a promotion
- [x] Add products to promotion - [x] Add products to promotion
- [ ] Remove products from promotion - [x] Remove products from promotion
- [ ] List of available Hot Sale promotions - [x] List of available Hot Sale promotions
- [ ] List of products participating in the Hot Sale promotion - [x] List of products participating in the Hot Sale promotion
- [ ] Add products to the Hot Sale promotion - [x] Add products to the Hot Sale promotion
- [ ] Remove product from the Hot Sale promotion - [x] Remove product from the Hot Sale promotion
- [ ] List of discount requests - [x] List of discount requests
- [ ] Approve a discount request - [x] Approve a discount request
- [ ] Decline a discount request - [x] Decline a discount request
## Brand certificates ## Brand certificates
- [ ] List of certified brands - [x] List of certified brands
## Quality certificates ## Quality certificates
- [ ] List of accordance types (version 1) - [x] List of accordance types (version 2)
- [ ] List of accordance types (version 2) - [x] Directory of document types
- [ ] Directory of document types - [x] List of certified categories
- [ ] List of certified categories - [x] Adding certificates for products
- [ ] Adding certificates for products - [x] Link the certificate to the product
- [ ] Link the certificate to the product - [x] Delete certificate
- [ ] Delete certificate - [x] Certificate information
- [ ] Certificate information - [x] Certificates list
- [ ] Certificates list - [x] Product statuses list
- [ ] Product statuses list - [x] List of products associated with the certificate
- [ ] List of products associated with the certificate - [x] Unbind products from a certificate
- [ ] Unbind products from a certificate - [x] Possible certificate rejection reasons
- [ ] Possible certificate rejection reasons - [x] Possible certificate statuses
- [ ] Possible certificate statuses
## Warehouses ## Warehouses
- [x] List of warehouses - [x] List of warehouses
- [x] List of delivery methods for a warehouse - [x] List of delivery methods for a warehouse
## Polygons ## Polygons
- [ ] Create delivery polygon - [x] Create delivery polygon
- [ ] Link delivery method to a delivery polygon - [x] Link delivery method to a delivery polygon
- [ ] Delete polygon - [x] Delete polygon
## FBO ## FBO
- [x] Shipments list - [x] Shipments list
- [ ] Shipment details - [x] Shipment details
## FBS and rFBS products labeling ## FBS and rFBS products labeling
- [x] Validate labeling codes - [x] Validate labeling codes
@@ -132,24 +131,24 @@
- [x] Get information about FBS returns - [x] Get information about FBS returns
## Cancellations ## Cancellations
- [ ] Get information about a rFBS cancellation request - [x] Get information about a rFBS cancellation request
- [ ] Get a list of rFBS cancellation requests - [x] Get a list of rFBS cancellation requests
- [ ] Approve a rFBS cancellation request - [x] Approve a rFBS cancellation request
- [ ] Reject a rFBS cancellation request - [x] Reject a rFBS cancellation request
## Chats with customers ## Chats with customers
- [ ] Chats list - [x] Chats list
- [ ] Send message - [x] Send message
- [ ] Send file - [x] Send file
- [ ] Chat history - [x] Chat history
- [ ] Update chat - [x] Update chat
- [ ] Create a new chat - [x] Create a new chat
- [ ] Mark messages as read - [x] Mark messages as read
## Invoices ## Invoices
- [ ] Create or edit proforma invoice link - [x] Create or edit proforma invoice link
- [ ] Get a proforma invoice link - [x] Get a proforma invoice link
- [ ] Delete the proforma invoice link - [x] Delete the proforma invoice link
## Reports ## Reports
- [x] Report details - [x] Report details
@@ -160,9 +159,9 @@
- [x] Returns report - [x] Returns report
- [x] Shipment report - [x] Shipment report
- [x] Financial report - [x] Financial report
- [ ] Issue a report on discounted products - [x] Issue a report on discounted products
- [ ] Report on discounted products - [x] Report on discounted products
- [ ] List of reports on discounted products - [x] List of reports on discounted products
## Analytics ## Analytics
- [x] Analytics data - [x] Analytics data
@@ -170,7 +169,7 @@
## Finance ## Finance
- [x] Report on sold products - [x] Report on sold products
- [ ] Transactions list (version 3) - [x] Transactions list (version 3)
- [x] Total transactions sum - [x] Total transactions sum
## Seller rating ## Seller rating

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Kirill
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -56,7 +56,7 @@ func (c Client) newRequest(method string, url string, body interface{}) (*http.R
return req, nil return req, nil
} }
func (c Client) Request(method string, path string, req, resp interface{}) (*Response, error) { func (c Client) Request(method string, path string, req, resp interface{}, options map[string]string) (*Response, error) {
httpReq, err := c.newRequest(method, path, req) httpReq, err := c.newRequest(method, path, req)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -55,7 +55,7 @@ func TestRequest(t *testing.T) {
c := NewMockClient(NewMockHttpHandler(test.statusCode, test.response, test.headers)) c := NewMockClient(NewMockHttpHandler(test.statusCode, test.response, test.headers))
respStruct := &TestRequestResponse{} respStruct := &TestRequestResponse{}
resp, err := c.Request(http.MethodPost, "/", test.params, respStruct) resp, err := c.Request(http.MethodPost, "/", test.params, respStruct, nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)

View File

@@ -19,21 +19,10 @@ type GetAnalyticsDataParams struct {
DateTo time.Time `json:"date_to"` DateTo time.Time `json:"date_to"`
// Items Enum: "unknownDimension" "sku" "spu" "day" "week" "month" "year" "category1" "category2" "category3" "category4" "brand" "modelID" // Items Enum: "unknownDimension" "sku" "spu" "day" "week" "month" "year" "category1" "category2" "category3" "category4" "brand" "modelID"
Dimension []string `json:"dimension"` Dimension []GetAnalyticsDataDimension `json:"dimension"`
// Filters // Filters
Filters []struct { Filters []GetAnalyticsDataFilter `json:"filters"`
// Sorting parameter. You can pass any attribute from the `dimension` and `metric` parameters except the `brand` attribute
Key string `json:"key"`
// Comparison operation
//
// Enum: "EQ" "GT" "GTE" "LT" "LTE"
Operation string `json:"operation"`
// Value for comparison
Value string `json:"value"`
} `json:"filters"`
// Number of items in the respones: // Number of items in the respones:
// - maximum is 1000, // - maximum is 1000,
@@ -41,11 +30,7 @@ type GetAnalyticsDataParams struct {
Limit int64 `json:"limit"` Limit int64 `json:"limit"`
// Specify up to 14 metrics. If there are more, you will get an error with the InvalidArgument code // Specify up to 14 metrics. If there are more, you will get an error with the InvalidArgument code
// Metrics []GetAnalyticsDataFilterMetric `json:"metrics"`
// Items Enum: "unknown_metric" "hits_view_search" "hits_view_pdp" "hits_view" "hits_tocart_search" "hits_tocart_pdp" "hits_tocart" "session_view_search"
// "session_view_pdp" "session_view" "conv_tocart_search" "conv_tocart_pdp" "conv_tocart" "revenue" "returns" "cancellations" "ordered_units" "delivered_units"
// "adv_view_pdp" "adv_view_search_category" "adv_view_all" "adv_sum_all" "position_category" "postings" "postings_premium"
Metrics []string `json:"metrics"`
// Number of elements that will be skipped in the response. For example, if `offset=10`, the response will start with the 11th element found // 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 int64 `json:"offset"` Offset int64 `json:"offset"`
@@ -54,52 +39,67 @@ type GetAnalyticsDataParams struct {
Sort []GetAnalyticsDataSort `json:"sort"` Sort []GetAnalyticsDataSort `json:"sort"`
} }
type GetAnalyticsDataFilter struct {
// Sorting parameter. You can pass any attribute from the `dimension` and `metric` parameters except the `brand` attribute
Key string `json:"key"`
// Comparison operation
Operation GetAnalyticsDataFilterOperation `json:"operation"`
// Value for comparison
Value string `json:"value"`
}
// Report sorting settings // Report sorting settings
type GetAnalyticsDataSort struct { type GetAnalyticsDataSort struct {
// Metric by which the method result will be sorted // Metric by which the method result will be sorted
Key string `json:"key"` Key GetAnalyticsDataFilterMetric `json:"key"`
// Sorting type // Sorting type
// - ASC — in ascending order, Order Order `json:"order"`
// - DESC — in descending order.
Order string `json:"order"`
} }
type GetAnalyticsDataResponse struct { type GetAnalyticsDataResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetAnalyticsDataResult `json:"result"`
// Data array
Data []struct {
// Data grouping in the report
Dimensions []struct {
// Identifier
Id string `json:"id"`
// Name
Name string `json:"name"`
} `json:"dimensions"`
// Metric values list
Metrics []float64 `json:"metrics"`
} `json:"data"`
// Total and average metrics values
Totals []float64 `json:"totals"`
} `json:"result"`
// Report creation time // Report creation time
Timestamp string `json:"timestamp"` Timestamp string `json:"timestamp"`
} }
type GetAnalyticsDataResult struct {
// Data array
Data []GetAnalyticsDataResultData `json:"data"`
// Total and average metrics values
Totals []float64 `json:"totals"`
}
type GetAnalyticsDataResultData struct {
// Data grouping in the report
Dimensions []GetAnalyticsDataResultDimension `json:"dimensions"`
// Metric values list
Metrics []float64 `json:"metrics"`
}
type GetAnalyticsDataResultDimension struct {
// Identifier
Id string `json:"id"`
// Name
Name string `json:"name"`
}
// Specify the period and metrics that are required. The response will contain analytical data grouped by the `dimensions` parameter. // Specify the period and metrics that are required. The response will contain analytical data grouped by the `dimensions` parameter.
func (c Analytics) GetAnalyticsData(params *GetAnalyticsDataParams) (*GetAnalyticsDataResponse, error) { func (c Analytics) GetAnalyticsData(params *GetAnalyticsDataParams) (*GetAnalyticsDataResponse, error) {
url := "/v1/analytics/data" url := "/v1/analytics/data"
resp := &GetAnalyticsDataResponse{} resp := &GetAnalyticsDataResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -118,41 +118,42 @@ type GetStocksOnWarehousesParams struct {
Offset int64 `json:"offset"` Offset int64 `json:"offset"`
// Warehouse type filter: // Warehouse type filter:
// - EXPRESS_DARK_STORE — Ozon warehouses with Fresh delivery. WarehouseType WarehouseType `json:"warehouse_type"`
// - NOT_EXPRESS_DARK_STORE — Ozon warehouses without Fresh delivery.
// - ALL — all Ozon warehouses.
WarehouseType string `json:"warehouse_type"`
} }
type GetStocksOnWarehousesResponse struct { type GetStocksOnWarehousesResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetStocksOnWarehousesResult `json:"result"`
// Information about products and stocks }
Rows []struct {
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
// Product identifier in the seller's system type GetStocksOnWarehousesResult struct {
ItemCode string `json:"item_code"` // Information about products and stocks
Rows []GetStocksOnWarehousesResultRow `json:"rows"`
}
// Product name in the Ozon system type GetStocksOnWarehousesResultRow struct {
ItemName string `json:"item_name"` // Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
// Product amount available for sale on Ozon // Product identifier in the seller's system
FreeToSellAmount int64 `json:"free_to_sell_amount"` ItemCode string `json:"item_code"`
// Product amount specified for confirmed future supplies // Product name in the Ozon system
PromisedAmount int64 `json:"promised_amount"` ItemName string `json:"item_name"`
// Product amount reserved for purchase, returns, and transportation between warehouses // Product amount available for sale on Ozon
ReservedAmount int64 `json:"reserved_amount"` FreeToSellAmount int64 `json:"free_to_sell_amount"`
// Name of the warehouse where the products are stored // Product amount specified for confirmed future supplies
WarehouseName string `json:"warehouse_name"` PromisedAmount int64 `json:"promised_amount"`
} `json:"rows"`
} `json:"result"` // Product amount reserved for purchase, returns, and transportation between warehouses
ReservedAmount int64 `json:"reserved_amount"`
// Name of the warehouse where the products are stored
WarehouseName string `json:"warehouse_name"`
} }
// Report on stocks and products movement at Ozon warehouses // Report on stocks and products movement at Ozon warehouses
@@ -161,7 +162,7 @@ func (c Analytics) GetStocksOnWarehouses(params *GetStocksOnWarehousesParams) (*
resp := &GetStocksOnWarehousesResponse{} resp := &GetStocksOnWarehousesResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -23,12 +23,12 @@ func TestGetAnalyticsData(t *testing.T) {
&GetAnalyticsDataParams{ &GetAnalyticsDataParams{
DateFrom: core.TimeFromString(t, "2006-01-02", "2020-09-01"), DateFrom: core.TimeFromString(t, "2006-01-02", "2020-09-01"),
DateTo: core.TimeFromString(t, "2006-01-02", "2021-10-15"), DateTo: core.TimeFromString(t, "2006-01-02", "2021-10-15"),
Dimension: []string{"sku", "day"}, Dimension: []GetAnalyticsDataDimension{SKUDimension, DayDimension},
Metrics: []string{"hits_view_search"}, Metrics: []GetAnalyticsDataFilterMetric{AdvViewAll},
Sort: []GetAnalyticsDataSort{ Sort: []GetAnalyticsDataSort{
{ {
Key: "hits_view_search", Key: HistViewPDP,
Order: "DESC", Order: Descending,
}, },
}, },
Limit: 1000, Limit: 1000,

59
ozon/brands.go Normal file
View File

@@ -0,0 +1,59 @@
package ozon
import (
"net/http"
core "github.com/diphantxm/ozon-api-client"
)
type Brands struct {
client *core.Client
}
type ListCertifiedBrandsParams struct {
// Number of the page returned in the request
Page int32 `json:"page"`
// Number of elements on the page
PageSize int32 `json:"page_size"`
}
type ListCertifiedBrandsResponse struct {
core.CommonResponse
// Method result
Result ListCertifiedBrandsResult `json:"result"`
}
type ListCertifiedBrandsResult struct {
// Certified brands details
BrandCertification []ListCertifiedBrandsResultCertificate `json:"brand_certification"`
// Total number of brands
Total int64 `json:"total"`
}
type ListCertifiedBrandsResultCertificate struct {
// Brand name
BrandName string `json:"brand_name"`
// Indication that the certificate is required:
// - true if the certificate is required;
// - false if not
HasCertificate bool `json:"has_certificate"`
}
// List of certified brands
func (c Brands) List(params *ListCertifiedBrandsParams) (*ListCertifiedBrandsResponse, error) {
url := "/v1/brand/company-certification/list"
resp := &ListCertifiedBrandsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

70
ozon/brands_test.go Normal file
View File

@@ -0,0 +1,70 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestListCertifiedBrands(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListCertifiedBrandsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListCertifiedBrandsParams{
Page: 0,
PageSize: 0,
},
`{
"result": {
"brand_certification": [
{
"brand_id": 135476863,
"brand_name": "Sea of Spa",
"has_certificate": false
}
],
"total": 1
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListCertifiedBrandsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Brands().List(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if int64(len(resp.Result.BrandCertification)) != resp.Result.Total {
t.Errorf("Length of brands in response is not equal to total field in response")
}
}
}
}

213
ozon/cancellations.go Normal file
View File

@@ -0,0 +1,213 @@
package ozon
import (
"net/http"
"time"
core "github.com/diphantxm/ozon-api-client"
)
type Cancellations struct {
client *core.Client
}
type GetCancellationInfoParams struct {
// Cancellation request identifier
CancellationId int64 `json:"cancellation_id"`
}
type GetCancellationInfoResponse struct {
core.CommonResponse
// Method result
Result CancellationInfo `json:"result"`
}
type CancellationInfo struct {
// Cancellation request identifier
CancellationId int64 `json:"cancellation_id"`
// Shipment number
PostingNumber string `json:"posting_number"`
// Cancellation reason
CancellationReason CancellationInfoReason `json:"cancellation_reason"`
// Cancellation request creation date
CancelledAt time.Time `json:"cancelled_at"`
// Comment to cancellation submitted by cancellation initiator
CancellationReasonMessage string `json:"cancellation_reason_message"`
// Delivery service integration type
TPLIntegrationType string `json:"tpl_integration_type"`
// Cancellation request status
State CancellationInfoState `json:"state"`
// Cancellation initiator
CancellationInitiator string `json:"cancellation_initiator"`
// Order creation date
OrderDate time.Time `json:"order_date"`
// Comment submitted on the cancellation request approval or rejection
ApproveComment string `json:"approve_comment"`
// Cancellation request approval or rejection date
ApproveDate time.Time `json:"approve_date"`
// Date after which the request will be automatically approved
AutoApproveDate time.Time `json:"auto_approve_date"`
}
type CancellationInfoReason struct {
// Cancellation reason identifier
Id int64 `json:"id"`
// Cancellation reason name
Name string `json:"name"`
}
type CancellationInfoState struct {
// Status identifier
Id int64 `json:"id"`
// Status name
Name string `json:"name"`
// Request status
State string `json:"state"`
}
// Method for getting information about a rFBS cancellation request
func (c Cancellations) GetInfo(params *GetCancellationInfoParams) (*GetCancellationInfoResponse, error) {
url := "/v1/delivery-method/list"
resp := &GetCancellationInfoResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListCancellationsParams struct {
// Filters
Filter ListCancellationsFilter `json:"filter"`
// Number of cancellation requests in the response
Limit int32 `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 int32 `json:"offset"`
// Additional information
With ListCancellationWith `json:"with"`
}
type ListCancellationsFilter struct {
// Filter by cancellation initiator
CancellationInitiator []string `json:"cancellation_initiator"`
// Filter by shipment number.
//
// Optional parameter. You can pass several values here
PostingNumber string `json:"posting_number"`
// Filter by cancellation request status
State string `json:"state"`
}
type ListCancellationWith struct {
// Indication that the counter of requests in different statuses should be displayed in the response
Counters bool `json:"counters"`
}
type ListCancellationsResponse struct {
core.CommonResponse
// Cancellation requests list
Result []CancellationInfo `json:"result"`
// The total number of requests by the specified filters
Total int32 `json:"total"`
// Counter of requests in different statuses
Counters ListCancellationResponseCounters `json:"counters"`
}
type ListCancellationResponseCounters struct {
// Number of requests for approval
OnApproval int64 `json:"on_approval"`
// Number of approved requests
Approved int64 `json:"approved"`
// Number of rejected requests
Rejected int64 `json:"rejected"`
}
// Method for getting a list of rFBS cancellation requests
func (c Cancellations) List(params *ListCancellationsParams) (*ListCancellationsResponse, error) {
url := "/v1/conditional-cancellation/list"
resp := &ListCancellationsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ApproveRejectCancellationsParams struct {
// Cancellation request identifier
CancellationId int64 `json:"cancellation_id"`
// Comment
Comment string `json:"comment"`
}
type ApproveRejectCancellationsResponse struct {
core.CommonResponse
}
// The method allows to approve an rFBS cancellation request in the ON_APPROVAL status.
// The order will be canceled and the money will be returned to the customer
func (c Cancellations) Approve(params *ApproveRejectCancellationsParams) (*ApproveRejectCancellationsResponse, error) {
url := "/v1/conditional-cancellation/approve"
resp := &ApproveRejectCancellationsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
// The method allows to reject an rFBS cancellation request in the ON_APPROVAL status. Explain your decision in the comment parameter.
//
// The order will remain in the same status and must be delivered to the customer
func (c Cancellations) Reject(params *ApproveRejectCancellationsParams) (*ApproveRejectCancellationsResponse, error) {
url := "/v1/conditional-cancellation/reject"
resp := &ApproveRejectCancellationsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

271
ozon/cancellations_test.go Normal file
View File

@@ -0,0 +1,271 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestGetCancellationInfo(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetCancellationInfoParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetCancellationInfoParams{
CancellationId: 90066344,
},
`{
"result": {
"cancellation_id": 90066344,
"posting_number": "47134289-0029-1",
"cancellation_reason": {
"id": 508,
"name": "Покупатель отменил заказ"
},
"cancelled_at": "2022-04-07T06:37:26.871105Z",
"cancellation_reason_message": "Изменение пункта выдачи заказа.",
"tpl_integration_type": "ThirdPartyTracking",
"state": {
"id": 2,
"name": "Подтверждена",
"state": "APPROVED"
},
"cancellation_initiator": "CLIENT",
"order_date": "2022-04-06T17:17:24.517Z",
"approve_comment": "",
"approve_date": "2022-04-07T07:52:45.971824Z",
"auto_approve_date": "2022-04-09T06:37:26.871105Z"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetCancellationInfoParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Cancellations().GetInfo(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Result.CancellationId != test.params.CancellationId {
t.Errorf("Cancellation ids in request and response are not equal")
}
}
}
}
func TestListCancellations(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListCancellationsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListCancellationsParams{
Filter: ListCancellationsFilter{
CancellationInitiator: []string{"CLIENT"},
State: "ALL",
},
Limit: 2,
Offset: 0,
With: ListCancellationWith{
Counters: true,
},
},
`{
"result": [
{
"cancellation_id": 50186754,
"posting_number": "41267064-0032-1",
"cancellation_reason": {
"id": 508,
"name": "Покупатель отменил заказ"
},
"cancelled_at": "2021-09-03T07:17:12.116114Z",
"cancellation_reason_message": "",
"tpl_integration_type": "ThirdPartyTracking",
"state": {
"id": 2,
"name": "Подтверждена",
"state": "APPROVED"
},
"cancellation_initiator": "CLIENT",
"order_date": "2021-09-03T07:04:53.220Z",
"approve_comment": "",
"approve_date": "2021-09-03T09:13:12.614200Z",
"auto_approve_date": "2021-09-06T07:17:12.116114Z"
},
{
"cancellation_id": 51956491,
"posting_number": "14094410-0018-1",
"cancellation_reason": {
"id": 507,
"name": "Покупатель передумал"
},
"cancelled_at": "2021-09-13T15:03:25.155827Z",
"cancellation_reason_message": "",
"tpl_integration_type": "ThirdPartyTracking",
"state": {
"id": 5,
"name": "Автоматически отменена",
"state": "REJECTED"
},
"cancellation_initiator": "CLIENT",
"order_date": "2021-09-13T07:48:50.143Z",
"approve_comment": "",
"approve_date": null,
"auto_approve_date": "2021-09-16T15:03:25.155827Z"
}
],
"total": 19,
"counters": {
"on_approval": 0,
"approved": 14,
"rejected": 5
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListCancellationsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Cancellations().List(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestApproveCancellations(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ApproveRejectCancellationsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ApproveRejectCancellationsParams{
CancellationId: 74393917,
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ApproveRejectCancellationsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Cancellations().Approve(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestRejectCancellations(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ApproveRejectCancellationsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ApproveRejectCancellationsParams{
CancellationId: 74393917,
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ApproveRejectCancellationsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Cancellations().Reject(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

202
ozon/categories.go Normal file
View File

@@ -0,0 +1,202 @@
package ozon
import (
"net/http"
core "github.com/diphantxm/ozon-api-client"
)
type Categories struct {
client *core.Client
}
type GetProductTreeParams struct {
// Category identifier
CategoryId int64 `json:"category_id"`
// Response language
Language Language `json:"language" default:"DEFAULT"`
}
type GetProductTreeResponse struct {
core.CommonResponse
// Category list
Result []GetProductTreeResult `json:"result"`
}
type GetProductTreeResult struct {
// Category identifier
CategoryId int64 `json:"category_id"`
// Subcategory tree
Children []GetProductTreeResponse `json:"children"`
// Category name
Title string `json:"title"`
}
// Returns product categories in the tree view.
// New products can be created in the last level categories only.
// This means that you need to match these particular categories with the categories of your site.
// It is not possible to create categories by user request
func (c Categories) Tree(params *GetProductTreeParams) (*GetProductTreeResponse, error) {
url := "/v2/category/tree"
resp := &GetProductTreeResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetCategoryAttributesParams struct {
// Filter by characteristics
AttributeType AttributeType `json:"attribute_type" default:"ALL"`
// Category identifier
CategoryId []int64 `json:"category_id"`
// Response language
Language Language `json:"language" default:"DEFAULT"`
}
type GetCategoryAttributesResponse struct {
core.CommonResponse
// Method result
Result []GetCategoryAttributesResult `json:"result"`
}
type GetCategoryAttributesResult struct {
// Array of product characteristics
Attributes []GetCategoryAttributesResultAttribute `json:"attributes"`
// Category identifier
CategoryId int64 `json:"category_id"`
}
type GetCategoryAttributesResultAttribute struct {
// Indication that the dictionary attribute values depend on the category:
// - true — the attribute has its own set of values for each category.
// - false — the attribute has the same set of values for all categories
CategoryDependent bool `json:"category_dependent"`
// Characteristic description
Description string `json:"description"`
// Directory identifier
DictionaryId int64 `json:"dictionary_id"`
// Characteristics group identifier
GroupId int64 `json:"group_id"`
// Characteristics group name
GroupName string `json:"group_name"`
// Document generation task number
Id int64 `json:"id"`
// Indicates that the attribute is aspect. An aspect attribute is a characteristic that distinguishes products of the same model.
//
// For example, clothes and shoes of the same model may have different colors and sizes. That is, color and size are aspect attributes.
//
// Values description:
// - true — the attribute is aspect and cannot be changed after the products are delivered to the warehouse or sold from the seller's warehouse.
// - false — the attribute is not aspect and can be changed at any time
IsAspect bool `json:"is_aspect"`
// Indicates that the characteristic is a set of values:
// - true — the characteristic is a set of values,
// - false — the characteristic consists of a single value
IsCollection bool `json:"is_collection"`
// Indicates that the characteristic is mandatory:
// - true — a mandatory characteristic,
// - false — you can leave the characteristic out
IsRequired bool `json:"is_required"`
// Name
Name string `json:"name"`
// Characteristic type
Type string `json:"type"`
}
// Getting characteristics for specified product category.
//
// Pass up to 20 category identifiers in the `category_id` list.
//
// You can check whether the attribute has a nested directory by the `dictionary_id` parameter.
// The 0 value means there is no directory. If the value is different, then there are directories.
// You can get them using the `/v2/category/attribute/values` method
func (c Categories) Attributes(params *GetCategoryAttributesParams) (*GetCategoryAttributesResponse, error) {
url := "/v3/category/attribute"
resp := &GetCategoryAttributesResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetAttributeDictionaryParams struct {
// Characteristics identifier
AttributeId int64 `json:"attribute_id"`
// Category identifier
CategoryId int64 `json:"category_id"`
// Response language
// The default language is Russian
Language Language `json:"language" default:"DEFAULT"`
LastValueId int64 `json:"last_value_id"`
// Number of values in the response:
// - maximum — 5000
// - minimum — 1
Limit int64 `json:"limit"`
}
type GetAttributeDictionaryResponse struct {
core.CommonResponse
HasNext bool `json:"has_next"`
// Method result
Result []GetAttributeDictionaryResult `json:"result"`
}
type GetAttributeDictionaryResult struct {
Id int64 `json:"id"`
Info string `json:"info"`
Picture string `json:"picture"`
// Product characteristic value
Value string `json:"value"`
}
// You can use the `/v3/category/attribute` method to check if an attribute has a nested directory.
// If there are directories, get them using this method
func (c Categories) AttributesDictionary(params *GetAttributeDictionaryParams) (*GetAttributeDictionaryResponse, error) {
url := "/v2/category/attribute/values"
resp := &GetAttributeDictionaryResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

217
ozon/categories_test.go Normal file
View File

@@ -0,0 +1,217 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestGetProductTree(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetProductTreeParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetProductTreeParams{
CategoryId: 17034410,
},
`{
"result": [
{
"category_id": 17034410,
"title": "Развивающие игрушки",
"children": []
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetProductTreeParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Categories().Tree(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result) > 0 {
if resp.Result[0].CategoryId != test.params.CategoryId {
t.Errorf("First category ids in request and response are not equal")
}
}
}
}
}
func TestGetCategoryAttributes(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetCategoryAttributesParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetCategoryAttributesParams{
CategoryId: []int64{17034410},
},
`{
"result": [
{
"category_id": 17034410,
"attributes": [
{
"id": 85,
"name": "Бренд",
"description": "Укажите наименование бренда, под которым произведен товар. Если товар не имеет бренда, используйте значение \"Нет бренда\"",
"type": "String",
"is_collection": false,
"is_required": true,
"group_id": 0,
"group_name": "",
"dictionary_id": 28732849
}
]
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetCategoryAttributesParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Categories().Attributes(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result) != len(test.params.CategoryId) {
t.Errorf("Length of categories in request and response are not equal")
}
if len(resp.Result) > 0 {
if resp.Result[0].CategoryId != test.params.CategoryId[0] {
t.Errorf("Category ids in request and response are not equal")
}
}
}
}
}
func TestGetAttributeDictionary(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetAttributeDictionaryParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetAttributeDictionaryParams{
AttributeId: 10096,
CategoryId: 17028968,
LastValueId: 0,
Limit: 3,
},
`{
"result": [
{
"id": 61571,
"value": "белый",
"info": "",
"picture": ""
},
{
"id": 61572,
"value": "прозрачный",
"info": "",
"picture": ""
},
{
"id": 61573,
"value": "бежевый",
"info": "",
"picture": ""
}
],
"has_next": true
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetAttributeDictionaryParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Categories().AttributesDictionary(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result) > int(test.params.Limit) {
t.Errorf("Length of response result is bigger than limit")
}
}
}
}

573
ozon/certificates.go Normal file
View File

@@ -0,0 +1,573 @@
package ozon
import (
"net/http"
"time"
core "github.com/diphantxm/ozon-api-client"
)
type Certificates struct {
client *core.Client
}
type ListOfAccordanceTypesResponse struct {
core.CommonResponse
// Accordance types
Result ListOfAccordanceTypesResult `json:"result"`
}
type ListOfAccordanceTypesResult struct {
// Main accordance types
Base []ListOfAccordanceTypesResultBase `json:"base"`
// Main accordance types related to dangerous products
Hazard []ListOfAccordanceTypesResultHazard `json:"hazard"`
}
type ListOfAccordanceTypesResultBase struct {
// Accordance type code
Code string `json:"code"`
// Accordance type description
Title string `json:"title"`
}
type ListOfAccordanceTypesResultHazard struct {
// Accordance type code
Code string `json:"code"`
// Accordance type description
Title string `json:"title"`
}
// List of accordance types (version 2)
func (c Certificates) ListOfAccordanceTypes() (*ListOfAccordanceTypesResponse, error) {
url := "/v2/product/certificate/accordance-types/list"
resp := &ListOfAccordanceTypesResponse{}
response, err := c.client.Request(http.MethodGet, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type DirectoryOfDocumentTypesResponse struct {
core.CommonResponse
// List of certificate types and names
Result []DirectoryOfDocumentTypesResult `json:"result"`
}
type DirectoryOfDocumentTypesResult struct {
// Certificate name
Name string `json:"name"`
// Certificate type
Value string `json:"value"`
}
// Directory of document types
func (c Certificates) DirectoryOfDocumentTypes() (*DirectoryOfDocumentTypesResponse, error) {
url := "/v1/product/certificate/types"
resp := &DirectoryOfDocumentTypesResponse{}
response, err := c.client.Request(http.MethodGet, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListOfCertifiedCategoriesParams struct {
// Number of the page returned in the query
Page int32 `json:"page"`
// Number of elements on the page
PageSize int32 `json:"page_size"`
}
type ListOfCertifiedCategoriesResponse struct {
core.CommonResponse
// Method result
Result ListOfCertifiedCategoriesResult `json:"reult"`
}
type ListOfCertifiedCategoriesResult struct {
// Certified categories details
Certification []ListOfCertifiedCategoriesResultCert `json:"certification"`
// Total number of categories
Total int64 `json:"total"`
}
type ListOfCertifiedCategoriesResultCert struct {
// Category name
CategoryName string `json:"category_name"`
// Indication of a mandatory category
IsRequired bool `json:"is_required"`
}
// List of certified categories
func (c Certificates) ListOfCertifiedCategories(params *ListOfCertifiedCategoriesParams) (*ListOfCertifiedCategoriesResponse, error) {
url := "/v1/product/certificate/types"
resp := &ListOfCertifiedCategoriesResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type LinkCertificateToProductParams struct {
// Certificate identifier that was assigned when it was uploaded
CertificateId int64 `json:"certificate_id"`
// An array of product identifiers that this certificate applies to
ProductId []int64 `json:"product_id"`
}
type LinkCertificateToProductResponse struct {
core.CommonResponse
// The result of processing the request. true if the request was executed without errors
Result bool `json:"result"`
}
// Link the certificate to the product
func (c Certificates) LinkToProduct(params *LinkCertificateToProductParams) (*LinkCertificateToProductResponse, error) {
url := "/v1/product/certificate/bind"
resp := &LinkCertificateToProductResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type DeleteCertificateParams struct {
// Certificate identifier
CertificateId int32 `json:"certificate_id"`
}
type DeleteCertificateResponse struct {
core.CommonResponse
// Result of deleting the certificate
Result DeleteCertificateResult `json:"result"`
}
type DeleteCertificateResult struct {
// Indication that a certificate has been deleted:
// - true — deleted
// - false — not deleted
IsDelete bool `json:"is_delete"`
// Description of errors during certificate deletion
ErrorMessage string `json:"error_message"`
}
// Delete certificate
func (c Certificates) Delete(params *DeleteCertificateParams) (*DeleteCertificateResponse, error) {
url := "/v1/product/certificate/delete"
resp := &DeleteCertificateResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetCertificateInfoParams struct {
// Certificate identifier
CertificateNumber string `json:"certificate_number"`
}
type GetCertificateInfoResponse struct {
core.CommonResponse
// Certificate information
Result GetCertificateInfoResult `json:"result"`
}
type GetCertificateInfoResult struct {
// Identifier
CertificateId int32 `json:"certificate_id"`
// Number
CertificateNumber string `json:"certificate_number"`
// Name
CertificateName string `json:"certificate_name"`
// Type
TypeCode string `json:"type_code"`
// Status
StatusCode string `json:"status_code"`
// Accordance type
AccordanceTypeCode string `json:"accordance_type_code"`
// Certificate rejection reason
RejectionReasonCode string `json:"rejectio_reason_code"`
// Moderator's comment
VerificationComment string `json:"verification_comment"`
// Issue date
IssueDate time.Time `json:"issue_date"`
// Expire date
ExpireDate time.Time `json:"expire_date"`
// Number of products associated with a certificate
ProductsCount int32 `json:"products_count"`
}
// Certificate information
func (c Certificates) GetInfo(params *GetCertificateInfoParams) (*GetCertificateInfoResponse, error) {
url := "/v1/product/certificate/info"
resp := &GetCertificateInfoResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListCertificatesParams struct {
// Product identifier associated with the certificate.
// Pass the parameter if you need certificates that certain products are associated with
OfferId string `json:"offer_id"`
// Certificate status. Pass the parameter if you need certificates with a certain status
Status string `json:"status"`
// Certificate type. Pass the parameter if you need certificates with a certain type
Type string `json:"type"`
// Page from which the list should be displayed. The minimum value is 1
Page int32 `json:"page"`
// Number of objects on the page. The value is from 1 to 1000
PageSize int32 `json:"page_size"`
}
type ListCertificatesResponse struct {
core.CommonResponse
// Certificates
Result ListCertificatesResult `json:"result"`
}
type ListCertificatesResult struct {
// Сertificate information
Certificates []ListCertificatesResultCert `json:"certificates"`
// Number of pages
PageCount int32 `json:"page_count"`
}
type ListCertificatesResultCert struct {
// Identifier
CertificateId int32 `json:"certificate_id"`
// Number
CertificateNumber string `json:"certificate_number"`
// Name
CertificateName string `json:"certificate_name"`
// Type
TypeCode string `json:"type"`
// Status
StatusCode string `json:"status_code"`
// Accordance type
AccordanceTypecode string `json:"accordance_type_code"`
// Certificate rejection reason
RejectionReasonCode string `json:"rejectio_reason_code"`
// Moderator's comment
VerificationComment string `json:"verification_comment"`
// Issue date
IssueDate time.Time `json:"issue_data"`
// Expire date
ExpireDate time.Time `json:"expire_date"`
// Number of products associated with a certificate
ProductsCount int32 `json:"products_count"`
}
// Certificates list
func (c Certificates) List(params *ListCertificatesParams) (*ListCertificatesResponse, error) {
url := "/v1/product/certificate/list"
resp := &ListCertificatesResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ProductStatusesResponse struct {
core.CommonResponse
// Product statuses
Result []ProductStatusesResult `json:"result"`
}
type ProductStatusesResult struct {
// Product status code when linking it to the certificate
Code string `json:"code"`
// Status description
Name string `json:"name"`
}
func (c Certificates) ProductStatuses() (*ProductStatusesResponse, error) {
url := "/v1/product/certificate/list"
resp := &ProductStatusesResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListProductsForCertificateParams struct {
// Certificate identifier
CertificateId int32 `json:"certificate_id"`
// Status of the product verification when binding to a certificate
ProductStatusCode string `json:"product_status_code"`
// Page from which the list should be displayed. The minimum value is 1
Page int32 `json:"page"`
// Number of objects on the page. The value is from 1 to 1000
PageSize int32 `json:"page_size"`
}
type ListProductsForCertificateResponse struct {
core.CommonResponse
// Method result
Result ListProductsForCertificateResult `json:"result"`
}
type ListProductsForCertificateResult struct {
// List of products
Items []struct {
// Product identifier
ProductId int64 `json:"product_id"`
// Status of the product processing when binding to a certificate
ProductStatusCode string `json:"product_status_code"`
} `json:"items"`
// Number of products found
Count int64 `json:"count"`
}
// A method for getting a list of possible statuses of products when binding them to a certificate
func (c Certificates) ListProductsForCertificate(params *ListProductsForCertificateParams) (*ListProductsForCertificateResponse, error) {
url := "/v1/product/certificate/products/list"
resp := &ListProductsForCertificateResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type UnlinkFromProductParams struct {
// Certificate identifier
CertificateId int32 `json:"certificate_id"`
// List of product identifiers that you want to unbind from a certificate
ProductId []int64 `json:"product_id"`
}
type UnlinkFromProductResponse struct {
core.CommonResponse
// Method result
Result []UnlinkFromProductResult `json:"result"`
}
type UnlinkFromProductResult struct {
// Error message when unbinding a product
Error string `json:"error"`
// Product identifier
ProductId int64 `json:"product_id"`
// Indication that the product was unbound from a certificate:
// - true — it was unbound,
// - false — it is still bound
Updated bool `json:"updated"`
}
// Unbind products from a certificate
func (c Certificates) UnlinkFromProduct(params *UnlinkFromProductParams) (*UnlinkFromProductResponse, error) {
url := "/v1/product/certificate/unbind"
resp := &UnlinkFromProductResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type PossibleRejectReasonsResponse struct {
core.CommonResponse
// Certificate rejection reasons
Result []PossibleRejectReasonsResult `json:"result"`
}
type PossibleRejectReasonsResult struct {
// Сode of a certificate rejection reason
Code string `json:"code"`
// Description of a certificate rejection reason
Name string `json:"name"`
}
// Possible certificate rejection reasons
func (c Certificates) PossibleRejectReasons() (*PossibleRejectReasonsResponse, error) {
url := "/v1/product/certificate/rejection_reasons/list"
resp := &PossibleRejectReasonsResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type PossibleStatusesResponse struct {
core.CommonResponse
// Possible certificate statuses
Result []PossibleStatusesResult `json:"result"`
}
type PossibleStatusesResult struct {
// Certificate status code
Code string `json:"code"`
// Status description
Name string `json:"name"`
}
func (c Certificates) PossibleStatuses() (*PossibleStatusesResponse, error) {
url := "/v1/product/certificate/status/list"
resp := &PossibleStatusesResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type AddCertificatesForProductsParams struct {
// Array of certificates for the product. Valid extensions are jpg, jpeg, png, pdf
Files []byte `json:"files"`
// Certificate name. No more than 100 characters
Name string `json:"name"`
// Certificate number. No more than 100 characters
Number string `json:"number"`
// Certificate type. To get the list of types, use the GET `/v1/product/certificate/types` method
TypeCode string `json:"type_code"`
// Accordance type. To get the list of types, use the GET `/v1/product/certificate/accordance-types` method
AccordanceTypeCode string `json:"accordance_type_code"`
// Issue date of the certificate
IssueDate time.Time `json:"issue_date"`
// Expiration date of the certificate. Can be empty for permanent certificates
ExpireDate time.Time `json:"expire_date"`
}
type AddCertificatesForProductsResponse struct {
core.CommonResponse
Id int `json:"id"`
}
// Adding certificates for products
func (c Certificates) AddForProducts(params *AddCertificatesForProductsParams) (*AddCertificatesForProductsResponse, error) {
url := "/v1/product/certificate/create"
resp := &AddCertificatesForProductsResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, map[string]string{
"Content-Type": "multipart/form-data",
})
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

707
ozon/certificates_test.go Normal file
View File

@@ -0,0 +1,707 @@
package ozon
import (
"net/http"
"testing"
"time"
core "github.com/diphantxm/ozon-api-client"
)
func TestListOfAccordanceTypes(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": {
"base": [
{
"code": "string",
"title": "string"
}
],
"hazard": [
{
"code": "string",
"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))
resp, err := c.Certificates().ListOfAccordanceTypes()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestDirectoryOfDocumentTypes(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": [
{
"name": "Сертификат соответствия",
"value": "certificate_of_conformity"
},
{
"name": "Декларация",
"value": "declaration"
},
{
"name": "Свидетельство о гос регистрации",
"value": "certificate_of_registration"
},
{
"name": "Регистрационное удостоверение",
"value": "registration_certificate"
},
{
"name": "Отказное письмо",
"value": "refused_letter"
}
]
}`,
},
// 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))
resp, err := c.Certificates().DirectoryOfDocumentTypes()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestListOfCertifiedCategories(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListOfCertifiedCategoriesParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListOfCertifiedCategoriesParams{
Page: 1,
PageSize: 100,
},
`{
"result": {
"certification": [
{
"is_required": true,
"category_name": "Витаминно-минеральные комплексы для взрослых"
}
],
"total": 1
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListOfCertifiedCategoriesParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().ListOfCertifiedCategories(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestLinkCertificateToProduct(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *LinkCertificateToProductParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&LinkCertificateToProductParams{
CertificateId: 50058,
ProductId: []int64{290},
},
`{
"result": true
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&LinkCertificateToProductParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().LinkToProduct(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestDeleteCertificate(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *DeleteCertificateParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&DeleteCertificateParams{
CertificateId: 0,
},
`{
"result": {
"is_delete": true,
"error_message": "string"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&DeleteCertificateParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().Delete(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetCertificateInfo(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetCertificateInfoParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetCertificateInfoParams{
CertificateNumber: "certificate number",
},
`{
"result": {
"certificate_id": 0,
"certificate_number": "certificate number",
"certificate_name": "string",
"type_code": "string",
"status_code": "string",
"accordance_type_code": "string",
"rejection_reason_code": "string",
"verification_comment": "string",
"issue_date": "2019-08-24T14:15:22Z",
"expire_date": "2019-08-24T14:15:22Z",
"products_count": 0
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetCertificateInfoParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().GetInfo(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Result.CertificateNumber != test.params.CertificateNumber {
t.Errorf("Certificate numbers in request and response are not equal")
}
}
}
}
func TestListCertificates(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListCertificatesParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListCertificatesParams{
OfferId: "id",
Status: "some status",
Type: "some type",
Page: 1,
PageSize: 1,
},
`{
"result": {
"certificates": [
{
"certificate_id": 0,
"certificate_number": "string",
"certificate_name": "string",
"type_code": "string",
"status_code": "string",
"accordance_type_code": "string",
"rejection_reason_code": "string",
"verification_comment": "string",
"issue_date": "2019-08-24T14:15:22Z",
"expire_date": "2019-08-24T14:15:22Z",
"products_count": 0
}
],
"page_count": 0
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListCertificatesParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().List(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestProductStatuses(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": [
{
"code": "string",
"name": "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))
resp, err := c.Certificates().ProductStatuses()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestListProductsForCertificate(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListProductsForCertificateParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListProductsForCertificateParams{
CertificateId: 0,
ProductStatusCode: "status code",
Page: 0,
PageSize: 0,
},
`{
"result": {
"items": [
{
"product_id": 0,
"product_status_code": "string"
}
],
"count": 0
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListProductsForCertificateParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().ListProductsForCertificate(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestUnlinkFromProduct(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *UnlinkFromProductParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&UnlinkFromProductParams{
CertificateId: 0,
ProductId: []int64{0},
},
`{
"result": [
{
"error": "string",
"product_id": 0,
"updated": true
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&UnlinkFromProductParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().UnlinkFromProduct(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestPossibleRejectReasons(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": [
{
"code": "string",
"name": "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))
resp, err := c.Certificates().PossibleRejectReasons()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestPossibleStatuses(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": [
{
"code": "string",
"name": "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))
resp, err := c.Certificates().PossibleStatuses()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestAddCertificatesForProducts(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *AddCertificatesForProductsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&AddCertificatesForProductsParams{
Files: []byte{10, 15, 2, 0},
Name: "Certificate name",
Number: "10a-d5s9-4asdf2",
TypeCode: "declaration",
AccordanceTypeCode: "gost",
IssueDate: time.Now(),
ExpireDate: time.Now(),
},
`{
"id": 50058
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&AddCertificatesForProductsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Certificates().AddForProducts(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

414
ozon/chats.go Normal file
View File

@@ -0,0 +1,414 @@
package ozon
import (
"net/http"
"time"
core "github.com/diphantxm/ozon-api-client"
)
type Chats struct {
client *core.Client
}
type ListChatsParams struct {
// Chats filter
Filter ListChatsFilter `json:"filter"`
// Number of values in the response. Default value is 1
Limit int64 `json:"limit" default:"1"`
// 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 int64 `json:"offset"`
}
type ListChatsFilter struct {
// Filter by chat status:
// - All
// - Opened
// - Closed
ChatStatus string `json:"chat_status" default:"ALL"`
// Filter by chats with unread messages
UnreadOnly bool `json:"unread_only"`
}
type ListChatsResponse struct {
core.CommonResponse
// Chats data
Chats []ListChatsChat `json:"chats"`
// Total number of chats
TotalChatsCount int64 `json:"total_chats_count"`
// Total number of unread messages
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"`
// Chat status:
// - All
// - Opened
// - Closed
ChatStatus string `json:"chat_status"`
// Chat type:
// - Seller_Support — support chat
// - Buyer_Seller — chat with a customer
ChatType string `json:"chat_type"`
// Chat creation date
CreatedAt time.Time `json:"created_at"`
}
// Returns information about chats by specified filters
func (c Chats) List(params *ListChatsParams) (*ListChatsResponse, error) {
url := "/v2/chat/list"
resp := &ListChatsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type SendMessageParams struct {
// Chat identifier
ChatId string `json:"chat_id"`
// Message text in the plain text format
Text string `json:"text"`
}
type SendMessageResponse struct {
core.CommonResponse
// Method result
Result string `json:"result"`
}
// Sends a message to an existing chat by its identifier
func (c Chats) SendMessage(params *SendMessageParams) (*SendMessageResponse, error) {
url := "/v1/chat/send/message"
resp := &SendMessageResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type SendFileParams struct {
// File as a base64 string
Base64Content string `json:"base64_content"`
// Chat identifier
ChatId string `json:"chat_id"`
// File name with extension
Name string `json:"name"`
}
type SendFileResponse struct {
core.CommonResponse
// Method result
Result string `json:"result"`
}
// Sends a file to an existing chat by its identifier
func (c Chats) SendFile(params *SendFileParams) (*SendFileResponse, error) {
url := "/v1/chat/send/file"
resp := &SendFileResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ChatHistoryParams struct {
// Chat idenitifier
ChatId string `json:"chat_id"`
// Messages sorting direction:
// - Forward—from old messages to new ones.
// - Backward—from new messages to old ones.
// The default value is `Backward`. You can set the number of messages in the limit parameter
Direction string `json:"direction" default:"Backward"`
// 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"`
// Number of messages in the response. The default value is 50
Limit int64 `json:"limit" default:"50"`
}
type ChatHistoryResponse struct {
core.CommonResponse
// Indicates that the response returned only a part of messages
HasNext bool `json:"has_next"`
// An array of messages sorted according to the direction parameter in the request body
Messages []ChatHistoryMessage `json:"messages"`
}
type ChatHistoryMessage struct {
// Message creation date
CreatedAt time.Time `json:"created_at"`
// Array with message content in Markdown format
Data []string `json:"data"`
// Indication of the read message
IsRead bool `json:"is_read"`
// Message identifier
MessageId string `json:"message_id"`
// Chat participant identifier
User ChatHistoryMessageUser `json:"user"`
}
type ChatHistoryMessageUser struct {
// Chat participant identifier
Id string `json:"id"`
// Chat participant type:
// - customer
// - seller
// - crm—system messages
// - courier
// - support
Type string `json:"type"`
}
// Chat history
func (c Chats) History(params *ChatHistoryParams) (*ChatHistoryResponse, error) {
url := "/v2/chat/history"
resp := &ChatHistoryResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type UpdateChatParams struct {
// Chat identifier
ChatId string `json:"chat_id"`
// Message identifier
FromMessageId uint64 `json:"from_message_id"`
// Number of messages in the response
Limit int64 `json:"limit"`
}
type UpdateChatResponse struct {
core.CommonResponse
// Method result
Result []UpdateChatResult `json:"result"`
}
type UpdateChatResult struct {
// An order or a product user wrote about in the chat
Context UpdateChatResultContext `json:"context"`
// Creation date and time
CreatedAt time.Time `json:"created_at"`
// Information about the file in the chat. Displayed only for `type = file`
File UpdateChatResultFile `json:"file"`
// File identifier
Id uint64 `json:"id"`
// Message. Displayed only for `type = text`
Text string `json:"text"`
// Message type:
// - text
// - file
Type string `json:"type"`
// Chat participant information
User UpdateChatResultUser `json:"user"`
}
type UpdateChatResultContext struct {
// Product inforamtion
Item UpdateChatResultContextItem `json:"item"`
// Order information
Order UpdateChatResultContextOrder `json:"order"`
}
type UpdateChatResultContextItem struct {
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
}
type UpdateChatResultContextOrder struct {
// Order number
OrderNumber string `json:"order_number"`
// Shipment information
Postings []UpdateChatResultContextOrderPosting `json:"postings"`
}
type UpdateChatResultContextOrderPosting struct {
// Delivery scheme:
// - FBO
// - FBS
// - RFBS
// - Crossborder
DeliverySchema string `json:"delivery_schema"`
// Shipment number
PostingNumber string `json:"posting_number"`
// List of product identifiers in the shipment
SKUList []int64 `json:"sku_list"`
}
type UpdateChatResultFile struct {
// File type
Mime string `json:"mime"`
// File name
Name string `json:"name"`
// File size in bytes
Size int64 `json:"size"`
// File URL
URL string `json:"url"`
}
type UpdateChatResultUser struct {
// Chat participant identifier
Id string `json:"id"`
// Chat participant chat:
// - customer
// - seller
// - crm—system messages
// - courier
Type string `json:"type"`
}
// Update chat
func (c Chats) Update(params *UpdateChatParams) (*UpdateChatResponse, error) {
url := "/v1/chat/updates"
resp := &UpdateChatResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type CreateNewChatParams struct {
// Shipment identifier
PostingNumber string `json:"posting_number"`
}
type CreateNewChatResponse struct {
core.CommonResponse
//Method result
Result CreateNewChatResult `json:"result"`
}
type CreateNewChatResult struct {
// Chat identifier
ChatId string `json:"chat_id"`
}
// Creates a new chat on the shipment with the customer. For example, to clarify the address or the product model
func (c Chats) Create(params *CreateNewChatParams) (*CreateNewChatResponse, error) {
url := "/v1/chat/start"
resp := &CreateNewChatResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type MarkAsReadParams struct {
// Chat identifier
Chatid string `json:"chat_id"`
// Message identifier
FromMessageId uint64 `json:"from_message_id"`
}
type MarkAsReadResponse struct {
core.CommonResponse
// Number of unread messages in the chat
UnreadCount int64 `json:"unread_count"`
}
// A method for marking the selected message and messages before it as read
func (c Chats) MarkAsRead(params *MarkAsReadParams) (*MarkAsReadResponse, error) {
url := "/v2/chat/read"
resp := &MarkAsReadResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

409
ozon/chats_test.go Normal file
View File

@@ -0,0 +1,409 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestListChats(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListChatsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListChatsParams{
Filter: ListChatsFilter{
ChatStatus: "Opened",
UnreadOnly: true,
},
Limit: 1,
Offset: 0,
},
`{
"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
}
],
"total_chats_count": 25,
"total_unread_count": 5
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListChatsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().List(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Chats) > 0 {
if resp.Chats[0].Chat.ChatStatus == "" {
t.Errorf("Chat status cannot be empty")
}
if resp.Chats[0].Chat.ChatType == "" {
t.Errorf("Chat type cannot be empty")
}
}
}
}
}
func TestSendMessage(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *SendMessageParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&SendMessageParams{
ChatId: "99feb3fc-a474-469f-95d5-268b470cc607",
Text: "test",
},
`{
"result": "success"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&SendMessageParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().SendMessage(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestSendFile(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *SendFileParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&SendFileParams{
ChatId: "99feb3fc-a474-469f-95d5-268b470cc607",
Name: "tempor",
Base64Content: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
},
`{
"result": "success"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&SendFileParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().SendFile(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestChatHistory(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ChatHistoryParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ChatHistoryParams{
ChatId: "18b8e1f9-4ae7-461c-84ea-8e1f54d1a45e",
Direction: "Forward",
FromMessageId: "3000000000118032000",
Limit: 1,
},
`{
"has_next": true,
"messages": [
{
"message_id": "3000000000817031942",
"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) телефона?"
]
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ChatHistoryParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().History(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestUpdateChat(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *UpdateChatParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&UpdateChatParams{
ChatId: "99feb3fc-a474-469f-95d5-268b470cc607",
FromMessageId: 0,
Limit: 1000,
},
`{
"result": [
{
"id": 3000000000012735500,
"user": {
"id": "15",
"type": "seller"
},
"type": "file",
"text": "",
"file": {
"url": "https://cdn-stg.ozonru.me/s3/fs-chat-api/108a0370-4dfa-11ec-bd02-06a332735108.png",
"mime": "image/png",
"size": 68,
"name": "tempor"
},
"created_at": "2021-11-25T14:14:55Z",
"context": null
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&UpdateChatParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().Update(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestCreateNewChat(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *CreateNewChatParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&CreateNewChatParams{
PostingNumber: "47873153-0052-1",
},
`{
"result": {
"chat_id": "5969c331-2e64-44b7-8a0e-ff9526762c62"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&CreateNewChatParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().Create(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Result.ChatId == "" {
t.Errorf("Chat id cannot be empty")
}
}
}
}
func TestMarkAsRead(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *MarkAsReadParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&MarkAsReadParams{
Chatid: "99feb3fc-a474-469f-95d5-268b470cc607",
FromMessageId: 3000000000118032000,
},
`{
"unread_count": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&MarkAsReadParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Chats().MarkAsRead(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

507
ozon/common.go Normal file
View File

@@ -0,0 +1,507 @@
package ozon
type Order string
const (
Ascending Order = "ASC"
Descending Order = "DESC"
)
type GetAnalyticsDataFilterOperation string
const (
Equal GetAnalyticsDataFilterOperation = "EQ"
Greater GetAnalyticsDataFilterOperation = "GT"
GreaterEqual GetAnalyticsDataFilterOperation = "GTE"
Lesser GetAnalyticsDataFilterOperation = "LT"
LesserEqual GetAnalyticsDataFilterOperation = "LTE"
)
type GetAnalyticsDataFilterMetric string
const (
UnknownMetric GetAnalyticsDataFilterMetric = "unknown_metric"
HitsViewSearch GetAnalyticsDataFilterMetric = "hits_view_search"
HistViewPDP GetAnalyticsDataFilterMetric = "hits_view_pdp"
HitsView GetAnalyticsDataFilterMetric = "hist_view"
HitsToCartSearch GetAnalyticsDataFilterMetric = "hits_tocart_search"
HitsToCartPDP GetAnalyticsDataFilterMetric = "hits_tocart_pdp"
SessionViewSearch GetAnalyticsDataFilterMetric = "session_view_search"
SessionViewPDP GetAnalyticsDataFilterMetric = "session_view_pdp"
SessionView GetAnalyticsDataFilterMetric = "session_view"
ConvToCartSearch GetAnalyticsDataFilterMetric = "conv_tocart_search"
ConvToCartPDP GetAnalyticsDataFilterMetric = "conv_tocart_pdp"
ConvToCart GetAnalyticsDataFilterMetric = "conv_tocart"
Revenue GetAnalyticsDataFilterMetric = "revenue"
ReturnsMetric GetAnalyticsDataFilterMetric = "returns"
CancellationsMetric GetAnalyticsDataFilterMetric = "cancellations"
OrderedUnits GetAnalyticsDataFilterMetric = "ordered_units"
DeliveredUnits GetAnalyticsDataFilterMetric = "delivered_units"
AdvViewPDP GetAnalyticsDataFilterMetric = "adv_view_pdp"
AdvViewSearchCategory GetAnalyticsDataFilterMetric = "adv_view_search_category"
AdvViewAll GetAnalyticsDataFilterMetric = "adv_view_all"
AdvSumAll GetAnalyticsDataFilterMetric = "adv_sum_all"
PositionCategory GetAnalyticsDataFilterMetric = "position_category"
PostingsMetric GetAnalyticsDataFilterMetric = "postings"
PostingsPremium GetAnalyticsDataFilterMetric = "postings_premium"
)
type WarehouseType string
const (
// Ozon warehouses with Fresh delivery
ExpressDarkStore WarehouseType = "EXPRESS_DARK_STORE"
// Ozon warehouses without Fresh delivery
NotExressDarkStore WarehouseType = "NOT_EXPRESS_DARK_STORE"
// All Ozon warehouses
ALLWarehouseType WarehouseType = "ALL"
)
type Language string
const (
Default Language = "DEFAULT"
Russian Language = "RU"
English Language = "EN"
Turkish Language = "TR"
Chinese Language = "ZH_HANS"
)
type AttributeType string
const (
All AttributeType = "ALL"
Required AttributeType = "REQUIRED"
Optional AttributeType = "OPTIONAL"
)
type ListDiscountRequestsStatus string
const (
New ListDiscountRequestsStatus = "NEW"
Seen ListDiscountRequestsStatus = "SEEN"
Approved ListDiscountRequestsStatus = "APPROVED"
PartlyApproved ListDiscountRequestsStatus = "PARTLY_APPROVED"
Declined ListDiscountRequestsStatus = "DECLINED"
AutoDeclined ListDiscountRequestsStatus = "AUTO_DECLINED"
DeclinedByUser ListDiscountRequestsStatus = "DECLINED_BY_USER"
Coupon ListDiscountRequestsStatus = "COUPON"
Purchased ListDiscountRequestsStatus = "PURCHASED"
)
type WorkingDay string
const (
Mon WorkingDay = "1"
Tue WorkingDay = "2"
Wed WorkingDay = "3"
Thu WorkingDay = "4"
Fri WorkingDay = "5"
Sat WorkingDay = "6"
Sun WorkingDay = "7"
)
type GetAnalyticsDataDimension string
const (
UnknownDimension GetAnalyticsDataDimension = "unknownDimension"
SKUDimension GetAnalyticsDataDimension = "sku"
SPUDimension GetAnalyticsDataDimension = "spu"
DayDimension GetAnalyticsDataDimension = "day"
WeekDimension GetAnalyticsDataDimension = "week"
MonthDimension GetAnalyticsDataDimension = "month"
YearDimension GetAnalyticsDataDimension = "year"
Category1Dimension GetAnalyticsDataDimension = "category1"
Category2Dimension GetAnalyticsDataDimension = "category2"
Category3Dimension GetAnalyticsDataDimension = "category3"
Category4Dimension GetAnalyticsDataDimension = "category4"
BrandDimension GetAnalyticsDataDimension = "brand"
ModelIDDimension GetAnalyticsDataDimension = "modelID"
)
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"
// ready for shipment
ReadyToSupply SupplyRequestState = "READY_TO_SUPPLY"
// accepted at the shipping point
AcceptedAtSupplyWarehouse SupplyRequestState = "ACCEPTED_AT_SUPPLY_WAREHOUSE"
// on the way
InTransit SupplyRequestState = "IN_TRANSIT"
// acceptance at the warehouse
AcceptanceAtStorageWarehouse SupplyRequestState = "ACCEPTANCE_AT_STORAGE_WAREHOUSE"
// acts being approved
ReportsConfirmationAwaiting SupplyRequestState = "REPORTS_CONFIRMATION_AWAITING"
// dispute
ReportRejected SupplyRequestState = "REPORT_REJECTED"
// completed
Completed SupplyRequestState = "COMPLETED"
// refused acceptance
RejectedAtSupplyWarehouse SupplyRequestState = "REJECTED_AT_SUPPLY_WAREHOUSE"
// cancelled
Cancelled SupplyRequestState = "CANCELLED"
// overdue
Overdue SupplyRequestState = "OVERDUE"
)
type ShipmentStatus string
const (
// acceptance is in progress
AcceptanceInProgress ShipmentStatus = "acceptance_in_progress"
// arbitration
Arbitration ShipmentStatus = "arbitration"
// awaiting confirmation
AwaitingApprove ShipmentStatus = "awaiting_approve"
// awaiting shipping
AwaitingDeliver ShipmentStatus = "awaiting_deliver"
// awaiting packaging
AwaitingPackaging ShipmentStatus = "awaiting_packaging"
// created
AwaitingVerification ShipmentStatus = "awaiting_verification"
// cancelled
CancelledSubstatus ShipmentStatus = "cancelled"
// delivered
Delivered ShipmentStatus = "delivered"
// delivery is in progress
Delivering ShipmentStatus = "delivering"
// picked up by driver
DriverPickup ShipmentStatus = "driver_pickup"
// not accepted at the sorting center
NotAccepted ShipmentStatus = "not_accepted"
// sent by the seller
SentBySeller ShipmentStatus = "sent_by_seller"
)
type ShipmentSubstatus string
const (
// acceptance in progress
PostingAcceptanceInProgress ShipmentStatus = "posting_acceptance_in_progress"
// arbitrage
PostingInArbitration ShipmentStatus = "posting_in_arbitration"
// created
PostingCreated ShipmentStatus = "posting_created"
// in the freight
PostingInCarriage ShipmentStatus = "posting_in_carriage"
// not added to the freight
PostingNotInCarriage ShipmentStatus = "posting_not_in_carriage"
// registered
PostingRegistered ShipmentStatus = "posting_registered"
// is handed over to the delivery service
PostingTransferringToDelivery ShipmentStatus = "posting_transferring_to_delivery"
// waiting for passport data
PostingAwaitingPassportData ShipmentStatus = "posting_awaiting_passport_data"
// created
PostingCreatedSubstatus ShipmentStatus = "posting_created"
// awaiting registration
PostingAwaitingRegistration ShipmentStatus = "posting_awaiting_registration"
// registration error
PostingRegistrationError ShipmentStatus = "posting_registration_error"
// created
PostingSplitPending ShipmentStatus = "posting_split_pending"
// canceled
PostingCancelled ShipmentStatus = "posting_canceled"
// customer delivery arbitrage
PostingInClientArbitration ShipmentStatus = "posting_in_client_arbitration"
// delivered
PostingDelivered ShipmentStatus = "posting_delivered"
// recieved
PostingReceived ShipmentStatus = "posting_received"
// presumably delivered
PostingConditionallyDelivered ShipmentStatus = "posting_conditionally_delivered"
// courier on the way
PostingInCourierService ShipmentStatus = "posting_in_courier_service"
// at the pick-up point
PostingInPickupPoint ShipmentStatus = "posting_in_pickup_point"
// on the way to the city
PostingOnWayToCity ShipmentStatus = "posting_on_way_to_city"
// on the way to the pick-up point
PostingOnWayToPickupPoint ShipmentStatus = "posting_on_way_to_pickup_point"
// returned to the warehouse
PostingReturnedToWarehouse ShipmentStatus = "posting_returned_to_warehouse"
// is handed over to the courier
PostingTransferredToCourierService ShipmentStatus = "posting_transferred_to_courier_service"
// handed over to the driver
PostingDriverPickup ShipmentStatus = "posting_driver_pick_up"
// not accepted at the sorting center
PostingNotInSortCenter ShipmentStatus = "posting_not_in_sort_center"
// sent by the seller
SentBySellerSubstatus ShipmentStatus = "sent_by_seller"
)
type TPLIntegrationType string
const (
// delivery by the Ozon logistics
OzonTPLType TPLIntegrationType = "ozon"
// delivery by a third-party service, Ozon registers the order
AggregatorTPLType TPLIntegrationType = "aggregator"
// delivery by a third-party service, the seller registers the order
TrackingTPLType TPLIntegrationType = "3pl_tracking"
// delivery by the seller
NonIntegratedTPLType TPLIntegrationType = "non_integrated"
)
type DetailsDeliveryItemName string
const (
DirectFlowLogisticSumDetailsDeliveryItemName DetailsDeliveryItemName = "MarketplaceServiceItemDirectFlowLogisticSum"
DropoffDetailsDeliveryItemName DetailsDeliveryItemName = "MarketplaceServiceItemDropoff"
DelivToCustomerDetailsDeliveryItemName DetailsDeliveryItemName = "MarketplaceServiceItemDelivToCustomer"
)
type DetailsReturnServiceName string
const (
ReturnAfterDelivToCustomerDetailsReturnServiceName DetailsReturnServiceName = "MarketplaceServiceItemReturnAfterDelivToCustomer"
ReturnPartGoodsCustomerDetailsReturnServiceName DetailsReturnServiceName = "MarketplaceServiceItemReturnPartGoodsCustomer"
ReturnNotDelivToCustomerDetailsReturnServiceName DetailsReturnServiceName = "MarketplaceServiceItemReturnNotDelivToCustomer"
ReturnFlowLogisticDetailsReturnServiceName DetailsReturnServiceName = "MarketplaceServiceItemReturnFlowLogistic"
)
type DetailsServiceItemName string
const (
OtherMarketAndTech DetailsServiceItemName = "MarketplaceServiceItemOtherMarketAndTechService"
ReturnStorageServiceAtThePickupPointFbsItem DetailsServiceItemName = "MarketplaceReturnStorageServiceAtThePickupPointFbsItem"
SaleReviewsItem DetailsServiceItemName = "MarketplaceSaleReviewsItem"
ServicePremiumCashbackIndividualPoints DetailsServiceItemName = "MarketplaceServicePremiumCashbackIndividualPoints"
ServiceStorageItem DetailsServiceItemName = "MarketplaceServiceStorageItem"
ServiceStockDisposal DetailsServiceItemName = "MarketplaceServiceStockDisposal"
ReturnDisposalServiceFbsItem DetailsServiceItemName = "MarketplaceReturnDisposalServiceFbsItem"
ServiceItemFlexiblePaymentSchedule DetailsServiceItemName = "MarketplaceServiceItemFlexiblePaymentSchedule"
ServiceProcessingSpoilage DetailsServiceItemName = "MarketplaceServiceProcessingSpoilage"
ServiceProcessingIdentifiedSurplus DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedSurplus"
ServiceProcessingIdentifiedDiscrepancies DetailsServiceItemName = "MarketplaceServiceProcessingIdentifiedDiscrepancies"
ServiceItemInternetSiteAdvertising DetailsServiceItemName = "MarketplaceServiceItemInternetSiteAdvertising"
ServiceItemPremiumSubscribtion DetailsServiceItemName = "MarketplaceServiceItemPremiumSubscribtion"
AgencyFeeAggregator3PLGlobalItem DetailsServiceItemName = "MarketplaceAgencyFeeAggregator3PLGlobalItem"
)
type DetailsOtherItemName string
const (
RedistributionOfAcquiringOperation DetailsOtherItemName = "MarketplaceRedistributionOfAcquiringOperation"
CompensationLossOfGoodsOperation DetailsOtherItemName = "MarketplaceSellerCompensationLossOfGoodsOperation"
CorrectionOperation DetailsOtherItemName = "MarketplaceSellerCorrectionOperation"
OperationCorrectionSeller DetailsOtherItemName = "OperationCorrectionSeller"
OperationMarketplaceWithHoldingForUndeliverableGoods DetailsOtherItemName = "OperationMarketplaceWithHoldingForUndeliverableGoods"
OperationClaim DetailsOtherItemName = "OperationClaim"
)
type StrategyType string
const (
MinExtPrice StrategyType = "MIN_EXT_PRICE"
CompPrice StrategyType = "COMP_PRICE"
)
type StrategyUpdateType string
const (
StrategyEnabled StrategyUpdateType = "strategyEnabled"
StrategyDisabled StrategyUpdateType = "strategyDisabled"
StrategyChanged StrategyUpdateType = "strategyChanged"
StrategyCreated StrategyUpdateType = "strategyCreated"
StrategyItemsListChanged StrategyUpdateType = "strategyItemsListChanged"
)
type ShipmentCertificateFilterStatus string
const (
// new
ShitmentCertificateFilterNew ShipmentCertificateFilterStatus = "new"
// retry creation
ShitmentCertificateFilterAwaitingRetry ShipmentCertificateFilterStatus = "awaiting-retry"
// is being packaged
ShitmentCertificateFilterInProcess ShipmentCertificateFilterStatus = "in_process"
// created
ShitmentCertificateFilterSuccess ShipmentCertificateFilterStatus = "success"
// creation error
ShitmentCertificateFilterError ShipmentCertificateFilterStatus = "error"
// sent
ShitmentCertificateFilterSend ShipmentCertificateFilterStatus = "sent"
// received
ShitmentCertificateFilterReceived ShipmentCertificateFilterStatus = "received"
// packaged
ShitmentCertificateFilterFormed ShipmentCertificateFilterStatus = "formed"
// canceled
ShitmentCertificateFilterCancelled ShipmentCertificateFilterStatus = "cancelled"
// in the queue for packaging
ShitmentCertificateFilterPending ShipmentCertificateFilterStatus = "pending"
// in the queue for completion
ShitmentCertificateFilterCompletionEnqueued ShipmentCertificateFilterStatus = "completion_enqueued"
// in the process of completion
ShitmentCertificateFilterCompletionProcessing ShipmentCertificateFilterStatus = "completion_processing"
// completion error
ShitmentCertificateFilterCompletionFailed ShipmentCertificateFilterStatus = "completion_failed"
// in the queue for cancellation
ShitmentCertificateFilterCancelationEnqueued ShipmentCertificateFilterStatus = "cancelation_enqueued"
// in the process of cancellation
ShitmentCertificateFilterCancelationProcessing ShipmentCertificateFilterStatus = "cancelation_processing"
// cancellation error
ShitmentCertificateFilterCancelationFailed ShipmentCertificateFilterStatus = "cancelation_failed"
// completed
ShitmentCertificateFilterCompleted ShipmentCertificateFilterStatus = "completed"
// closed
ShitmentCertificateFilterClosed ShipmentCertificateFilterStatus = "closed"
)
type PRROptionStatus string
const (
// carrying the bulky product using the elevator
PRROptionLift PRROptionStatus = "lift"
// carrying the bulky product upstairs
PRROptionStairs PRROptionStatus = "stairs"
// the customer canceled the service,
// you don't need to lift the shipment
PRROptionNone PRROptionStatus = "none"
// delivery is included in the price.
// According to the offer you need to
// deliver products to the floor
PRROptionDeliveryDefault PRROptionStatus = "delivery_default"
)
type GetFBSReturnsFilterStatus string
const (
ReturnedToSeller GetFBSReturnsFilterStatus = "returned_to_seller"
WaitingForSeller GetFBSReturnsFilterStatus = "waiting_for_seller"
AcceptedFromCustomer GetFBSReturnsFilterStatus = "accepted_from_customer"
CancelledWithCompensation GetFBSReturnsFilterStatus = "cancelled_with_compensation"
ReadyForShipment GetFBSReturnsFilterStatus = "ready_for_shipment"
)
type GetFBOReturnsFilterStatus string
const (
GetFBOReturnsFilterStatusReturnedToOzon GetFBOReturnsFilterStatus = "ReturnedToOzon"
GetFBOReturnsFilterStatusCancelled GetFBOReturnsFilterStatus = "Cancelled"
)
type GetFBOReturnsReturnStatus string
const (
GetFBOReturnsReturnStatusCancelled GetFBOReturnsReturnStatus = "Возврат отменен"
GetFBOReturnsReturnStatusAcceptedFromCustomer GetFBOReturnsReturnStatus = "Принят от покупателя"
GetFBOReturnsReturnStatusReceivedAtOzon GetFBOReturnsReturnStatus = "Получен в Ozon"
)
type DigitalActType string
const (
// acceptance certificate
DigitalActTypeOfAcceptance DigitalActType = "act_of_acceptance"
// discrepancy certificate
DigitalActTypeOfMismatch DigitalActType = "act_of_mismatch"
// surplus certificate
DigitalActTypeOfExcess DigitalActType = "act_of_excess"
)
type PriceStrategy string
const (
// enable
PriceStrategyEnabled PriceStrategy = "ENABLED"
// disable
PriceStrategyDisabled PriceStrategy = "DISABLED"
// don't change anything. Default value
PriceStrategyUnknown PriceStrategy = "UNKNOWN"
)

View File

@@ -56,111 +56,490 @@ type GetFBOShipmentsListResponse struct {
core.CommonResponse core.CommonResponse
// Shipments list // Shipments list
Result []struct { Result []GetFBOShipmentsListResult `json:"result"`
// Additional data for shipment list }
AdditionalData []struct {
Key string `json:"key"`
Value string `json:"value"`
} `json:"additional_data"`
// Analytical data type GetFBOShipmentsListResult struct {
AnalyticsData struct { // Additional data for shipment list
// Delivery city AdditionalData []GetFBOShipmentsListResultAdditionalData `json:"additional_data"`
City string `json:"city"`
// Delivery method // Analytical data
DeliveryType string `json:"delivery_type"` AnalyticsData GetFBOShipmentsListResultAnalyticsData `json:"analytics_data"`
// Indication that the recipient is a legal person // Shipment cancellation reason identifier
// * true — a legal person, CancelReasonId int64 `json:"cancel_reason_id"`
// * false — a natural person.
IsLegal bool `json:"is_legal"`
// Premium subscription // Date and time of shipment creation
IsPremium bool `json:"is_premium"` CreatedAt time.Time `json:"created_at"`
// Payment method // Financial data
PaymentTypeGroupName string `json:"payment_type_group_name"` FinancialData FBOFinancialData `json:"financial_data"`
// Delivery region // Date and time of shipment processing start
Region string `json:"region"` InProccessAt time.Time `json:"in_process_at"`
// Warehouse identifier // Identifier of the order to which the shipment belongs
WarehouseId int64 `json:"warehouse_id"` OrderId int64 `json:"order_id"`
// Name of the warehouse from which the order is shipped // Number of the order to which the shipment belongs
WarehouseName string `json:"warehouse_name"` OrderNumber string `json:"order_number"`
} `json:"analytics_data"`
// Shipment cancellation reason identifier // Shipment number
CancelReasonId int64 `json:"cancel_reason_id"` PostingNumber string `json:"posting_number"`
// Date and time of shipment creation // Number of products in the shipment
CreatedAt time.Time `json:"created_at"` Products []FBOPostingProduct `json:"products"`
// Financial data // Shipment status
FinancialData struct { Status string `json:"status"`
// Identifier of the cluster, where the shipment is sent from }
ClusterFrom string `json:"cluster_from"`
// Identifier of the cluster, where the shipment is delivered to type GetFBOShipmentsListResultAdditionalData struct {
ClusterTo string `json:"cluster_to"` Key string `json:"key"`
Value string `json:"value"`
}
// Services type GetFBOShipmentsListResultAnalyticsData struct {
PostingServices MarketplaceServices `json:"posting_services"` // Delivery city
City string `json:"city"`
// Products list // Delivery method
Products []FinancialDataProduct `json:"products"` DeliveryType string `json:"delivery_type"`
} `json:"financial_data"`
// Date and time of shipment processing start // Indication that the recipient is a legal person
InProccessAt time.Time `json:"in_process_at"` // * true — a legal person,
// * false — a natural person.
IsLegal bool `json:"is_legal"`
// Identifier of the order to which the shipment belongs // Premium subscription
OrderId int64 `json:"order_id"` IsPremium bool `json:"is_premium"`
// Number of the order to which the shipment belongs // Payment method
OrderNumber string `json:"order_number"` PaymentTypeGroupName string `json:"payment_type_group_name"`
// Shipment number // Delivery region
PostingNumber string `json:"posting_number"` Region string `json:"region"`
// Number of products in the shipment // Warehouse identifier
Products []struct { WarehouseId int64 `json:"warehouse_id"`
// Activation codes for services and digital products
DigitalCodes []string `json:"digital_codes"`
// Currency of your prices. It matches the currency set in the personal account settings // Name of the warehouse from which the order is shipped
CurrencyCode string `json:"currency_code"` WarehouseName string `json:"warehouse_name"`
}
// Product name type FBOPostingProduct struct {
Name string `json:"name"` // Activation codes for services and digital products
DigitalCodes []string `json:"digital_codes"`
// Product identifier in the seller's system // Currency of your prices. It matches the currency set in the personal account settings
OfferId string `json:"offer_id"` CurrencyCode string `json:"currency_code"`
// Product price // Product name
Price string `json:"price"` Name string `json:"name"`
// Quantity of products in the shipment // Product identifier in the seller's system
Quantity int64 `json:"quantity"` OfferId string `json:"offer_id"`
// Product identifier in the Ozon system, SKU // Product price
SKU int64 `json:"sku"` Price string `json:"price"`
} `json:"products"`
// Shipment status // Quantity of products in the shipment
Status string `json:"status"` Quantity int64 `json:"quantity"`
} `json:"result"`
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
}
type FBOFinancialData struct {
// Identifier of the cluster, where the shipment is sent from
ClusterFrom string `json:"cluster_from"`
// Identifier of the cluster, where the shipment is delivered to
ClusterTo string `json:"cluster_to"`
// Services
PostingServices MarketplaceServices `json:"posting_services"`
// Products list
Products []FinancialDataProduct `json:"products"`
} }
// Returns a list of shipments for a specified period of time. You can additionally filter the shipments by their status // Returns a list of shipments for a specified period of time. You can additionally filter the shipments by their status
func (c FBO) GetFBOShipmentsList(params *GetFBOShipmentsListParams) (*GetFBOShipmentsListResponse, error) { func (c FBO) GetShipmentsList(params *GetFBOShipmentsListParams) (*GetFBOShipmentsListResponse, error) {
url := "/v2/posting/fbo/list" url := "/v2/posting/fbo/list"
resp := &GetFBOShipmentsListResponse{} resp := &GetFBOShipmentsListResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetShipmentDetailsParams struct {
// Shipment number
PostingNumber string `json:"posting_number"`
// true if the address transliteration from Cyrillic to Latin is enabled
Translit bool `json:"translit"`
// Additional fields to add to the response
With GetShipmentDetailsWith `json:"with"`
}
type GetShipmentDetailsWith struct {
// Specify true to add analytics data to the response
AnalyticsData bool `json:"analytics_data"`
// Specify true to add financial data to the response
FinancialData bool `json:"financial_data"`
}
type GetShipmentDetailsResponse struct {
core.CommonResponse
// Method result
Result GetShipmentDetailsResult `json:"result"`
}
type GetShipmentDetailsResult struct {
// Additional data
AdditionalData []GetShipmentDetailsResultAdditionalData `json:"additional_data"`
// Analytical data
AnalyticsData GetShipmentDetailsResultAnalyticsData `json:"analytics_data"`
// Shipment cancellation reason identifier
CancelReasonId int64 `json:"cancel_reason_id"`
// Date and time of shipment creation
CreatedAt time.Time `json:"created_at"`
// Financial data
FinancialData FBOFinancialData `json:"financial_data"`
// Date and time of shipment processing start
InProcessAt time.Time `json:"in_process_at"`
// Identifier of the order to which the shipment belongs
OrderId int64 `json:"order_id"`
// Number of the order to which the shipment belongs
OrderNumber string `json:"order_number"`
// Shipment number
PostingNumber string `json:"posting_number"`
// Number of products in the shipment
Products []FBOPostingProduct `json:"products"`
// Shipment status
Status string `json:"status"`
}
type GetShipmentDetailsResultAdditionalData struct {
Key string `json:"key"`
Value string `json:"value"`
}
type GetShipmentDetailsResultAnalyticsData struct {
// Delivery city
City string `json:"Delivery city"`
// Delivery method
DeliveryType string `json:"delivery_type"`
// Indication that the recipient is a legal person:
// - true — a legal person
// - false — a natural person
IsLegal bool `json:"is_legal"`
// Premium subscription
IsPremium bool `json:"is_premium"`
// Payment method
PaymentTypeGroupName string `json:"payment_type_group_name"`
// Delivery region
Region string `json:"region"`
// Warehouse identifier
WarehouseId int64 `json:"warehouse_id"`
// Name of the warehouse from which the order is shipped
WarehouseName string `json:"warehouse_name"`
}
// Returns information about the shipment by its identifier
func (c FBO) GetShipmentDetails(params *GetShipmentDetailsParams) (*GetShipmentDetailsResponse, error) {
url := "/v2/posting/fbo/get"
resp := &GetShipmentDetailsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListSupplyRequestsParams struct {
// Number of the page returned in the request
Page int32 `json:"page"`
// Number of elements on the page
PageSize int32 `json:"page_size"`
// Filter on status of a supply by request
States []SupplyRequestState `json:"states"`
}
type ListSupplyRequestsResponse struct {
core.CommonResponse
// Indicates that the response contains not the entire array of supply requests:
// - true — make a new request with a different page and page_size values to get information on the remaining requests;
// - false — the entire array of requests for the filter specified in the request was returned in the response
HasNext bool `json:"has_next"`
// Supply requests list
SupplyOrders []SupplyRequestCommonResponse `json:"supply_orders"`
// Total requests number
TotalSupplyOrdersCount int32 `json:"total_supply_orders_count"`
}
type SupplyRequestCommonResponse struct {
// Supply request creation date
CreatedAt string `json:"created_at"`
// Local time supply interval
LocalTimeslot SupplyRequestCommonResponseLocalTimeslot `json:"local_timeslot"`
// Date from which you want to bring the supply to the warehouse. Only for supplies via vDC
PreferredSupplyDataFrom string `json:"preferred_supply_data_from"`
// Date by which you want to bring the supply to the warehouse. Only for supplies via vDC
PreferredSupplyDataTo string `json:"preferred_supply_data_to"`
// Status of a supply by request
State string `json:"state"`
// Supply request identifier
SupplyOrderId int64 `json:"supply_order_id"`
// Supply request number
SupplyOrderNumber string `json:"supply_order_number"`
// Supply warehouse
SupplyWarehouse SupplyRequestCommonResponseSupplyWarehouse `json:"supply_warehouse"`
// time_left_to_prepare_supply
TimeLeftToPrepareSupply int64 `json:"time_left_to_prepare_supply"`
// Time in seconds left to select the supply option. Only for supplies via vDC
TimeLeftToSelectSupplyVariant int64 `json:"time_left_to_select_supply_variant"`
// total_items_count
TotalItemsCount int32 `json:"total_items_count"`
// Total number of items in the request
TotalQuantity int32 `json:"total_quantity"`
}
type SupplyRequestCommonResponseLocalTimeslot struct {
// Interval start
From string `json:"from"`
// Interval end
To string `json:"to"`
}
type SupplyRequestCommonResponseSupplyWarehouse struct {
// Warehouse address
Address string `json:"address"`
// Warehouse name
Name string `json:"name"`
// Warehouse identifier
WarehouseId int64 `json:"warehouse_id"`
}
// Method for getting a list of supply requests to the Ozon warehouse.
// Requests with supply both to a specific warehouse and via a virtual
// distribution center (vDC) are taken into account
func (c FBO) ListSupplyRequests(params *ListSupplyRequestsParams) (*ListSupplyRequestsResponse, error) {
url := "/v1/supply-order/list"
resp := &ListSupplyRequestsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetSupplyRequestInfoParams struct {
// Supply request identifier
SupplyOrderId int64 `json:"supply_order_id"`
}
type GetSupplyRequestInfoResponse struct {
core.CommonResponse
SupplyRequestCommonResponse
// Driver and car information
VehicleInfo GetSupplyRequestInfoVehicle `json:"vehicle_info"`
}
type GetSupplyRequestInfoVehicle struct {
// Driver name
DriverName string `json:"driver_name"`
// Driver phone number
DriverPhone string `json:"driver_phone"`
// Car model
VehicleModel string `json:"vehicle_model"`
// Car number
VehicleNumber string `json:"vehicle_number"`
}
// Method for getting detailed information on a supply request.
// Requests with supply both to a specific warehouse and via a
// virtual distribution center (vDC) are taken into account
func (c FBO) GetSupplyRequestInfo(params *GetSupplyRequestInfoParams) (*GetSupplyRequestInfoResponse, error) {
url := "/v1/supply-order/get"
resp := &GetSupplyRequestInfoResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListProductsInSupplyRequestParams struct {
// Number of the page returned in the query
Page int32 `json:"page"`
// Number of elements on the page
PageSize int32 `json:"page_size"`
// Supply request identifier
SupplyOrderId int64 `json:"supply_order_id"`
}
type ListProductsInSupplyRequestResponse struct {
core.CommonResponse
// Indicates that the response contains not the entire array of supply requests:
// - true — make a new request with a different page and page_size values to get the remaining products;
// - false — the entire array of product was returned in the response
HasNext bool `json:"has_next"`
// Products list
Items []ListProductsInSupplyRequestItem `json:"items"`
// Total number of products in the request
TotalItemsCount int32 `json:"total_items_count"`
}
type ListProductsInSupplyRequestItem struct {
// Link to product image
IconPath string `json:"icon_path"`
// Product name
Name string `json:"name"`
// Product ID
OfferId string `json:"offer_id"`
// Product quantity
Quantity int64 `json:"quantity"`
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
}
// List of products in the sullpy request
func (c FBO) ListProductsInSupplyRequest(params *ListProductsInSupplyRequestParams) (*ListProductsInSupplyRequestResponse, error) {
url := "/v1/supply-order/items"
resp := &ListProductsInSupplyRequestResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetWarehouseWorkloadResponse struct {
core.CommonResponse
// Method result
Result []GetWarehouseWorkloadResult `json:"result"`
}
type GetWarehouseWorkloadResult struct {
// Workload
Schedule GetWarehouseWorkloadResultSchedule `json:"schedule"`
// Warehouse
Warehouse GetWarehouseWorkloadResultWarehouse `json:"warehouse"`
}
type GetWarehouseWorkloadResultSchedule struct {
// Data on the products quantity supplied to the warehouse
Capacity []GetWarehouseWorkloadResultScheduleCapacity `json:"capacity"`
// The closest available date for supply, local time
Date time.Time `json:"date"`
}
type GetWarehouseWorkloadResultScheduleCapacity struct {
// Period start, local time
Start time.Time `json:"start"`
// Period end, local time
End time.Time `json:"end"`
// Average number of products that the warehouse can accept per day for the period
Value int32 `json:"value"`
}
type GetWarehouseWorkloadResultWarehouse struct {
// Warehouse identifier
Id string `json:"id"`
// Warehouse name
Name string `json:"name"`
}
// Method returns a list of active Ozon warehouses with information about their average workload in the nearest future
func (c FBO) GetWarehouseWorkload() (*GetWarehouseWorkloadResponse, error) {
url := "/v1/supplier/available_warehouses"
resp := &GetWarehouseWorkloadResponse{}
response, err := c.client.Request(http.MethodGet, url, nil, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -43,8 +43,8 @@ func TestGetFBOShipmentsList(t *testing.T) {
"posting_number": "16965409-0014-1", "posting_number": "16965409-0014-1",
"status": "delivered", "status": "delivered",
"cancel_reason_id": 0, "cancel_reason_id": 0,
"created_at": "2021-09-01T00:23:45.607Z", "created_at": "2021-09-01T00:23:45.607000Z",
"in_process_at": "2021-09-01T00:25:30.120Z", "in_process_at": "2021-09-01T00:25:30.120000Z",
"products": [ "products": [
{ {
"sku": 160249683, "sku": 160249683,
@@ -133,7 +133,404 @@ func TestGetFBOShipmentsList(t *testing.T) {
for _, test := range tests { for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers)) c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBO().GetFBOShipmentsList(test.params) resp, err := c.FBO().GetShipmentsList(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetShipmentDetails(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetShipmentDetailsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetShipmentDetailsParams{
PostingNumber: "50520644-0012-7",
Translit: true,
With: GetShipmentDetailsWith{
AnalyticsData: true,
FinancialData: true,
},
},
`{
"result": {
"order_id": 354679434,
"order_number": "50520644-0012",
"posting_number": "50520644-0012-7",
"status": "delivered",
"cancel_reason_id": 0,
"created_at": "2021-09-01T00:34:56.563Z",
"in_process_at": "2021-09-01T00:34:56.103Z",
"products": [
{
"sku": 254665483,
"name": "Мочалка натуральная из люфы с деревянной ручкой",
"quantity": 1,
"offer_id": "PS1033",
"price": "137.00",
"digital_codes": [],
"currency_code": "RUB"
}
],
"analytics_data": {
"region": "МОСКВА",
"city": "Москва",
"delivery_type": "Courier",
"is_premium": false,
"payment_type_group_name": "Карты оплаты",
"warehouse_id": 15431806189000,
"warehouse_name": "ХОРУГВИНО_РФЦ",
"is_legal": false
},
"financial_data": {
"products": [
{
"commission_amount": 13.7,
"commission_percent": 10,
"payout": 123.3,
"product_id": 254665483,
"currency_code": "RUB",
"old_price": 198,
"price": 137,
"total_discount_value": 61,
"total_discount_percent": 30.81,
"actions": [
"Системная виртуальная скидка селлера"
],
"picking": null,
"quantity": 0,
"client_price": "",
"item_services": {
"marketplace_service_item_fulfillment": -31.5,
"marketplace_service_item_pickup": 0,
"marketplace_service_item_dropoff_pvz": 0,
"marketplace_service_item_dropoff_sc": 0,
"marketplace_service_item_dropoff_ff": 0,
"marketplace_service_item_direct_flow_trans": -5,
"marketplace_service_item_return_flow_trans": 0,
"marketplace_service_item_deliv_to_customer": -20,
"marketplace_service_item_return_not_deliv_to_customer": 0,
"marketplace_service_item_return_part_goods_customer": 0,
"marketplace_service_item_return_after_deliv_to_customer": 0
}
}
],
"posting_services": {
"marketplace_service_item_fulfillment": 0,
"marketplace_service_item_pickup": 0,
"marketplace_service_item_dropoff_pvz": 0,
"marketplace_service_item_dropoff_sc": 0,
"marketplace_service_item_dropoff_ff": 0,
"marketplace_service_item_direct_flow_trans": 0,
"marketplace_service_item_return_flow_trans": 0,
"marketplace_service_item_deliv_to_customer": 0,
"marketplace_service_item_return_not_deliv_to_customer": 0,
"marketplace_service_item_return_part_goods_customer": 0,
"marketplace_service_item_return_after_deliv_to_customer": 0
}
},
"additional_data": []
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetShipmentDetailsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBO().GetShipmentDetails(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Result.PostingNumber != test.params.PostingNumber {
t.Errorf("Posting numbers in request and response are not equal")
}
}
}
}
func TestListSupplyRequests(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListSupplyRequestsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListSupplyRequestsParams{
Page: 0,
PageSize: 0,
States: []SupplyRequestState{AcceptanceAtStorageWarehouse},
},
`{
"has_next": true,
"supply_orders": [
{
"created_at": "string",
"local_timeslot": {
"from": "string",
"to": "string"
},
"preferred_supply_date_from": "string",
"preferred_supply_date_to": "string",
"state": "string",
"supply_order_id": 0,
"supply_order_number": "string",
"supply_warehouse": {
"address": "string",
"name": "string",
"warehouse_id": 0
},
"time_left_to_prepare_supply": 0,
"time_left_to_select_supply_variant": 0,
"total_items_count": 0,
"total_quantity": 0
}
],
"total_supply_orders_count": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListSupplyRequestsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBO().ListSupplyRequests(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetSupplyRequestInfo(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetSupplyRequestInfoParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetSupplyRequestInfoParams{
SupplyOrderId: 0,
},
`{
"created_at": "string",
"local_timeslot": {
"from": "string",
"to": "string"
},
"preferred_supply_date_from": "string",
"preferred_supply_date_to": "string",
"seller_warehouse": {
"address": "string",
"name": "string",
"warehouse_id": 0
},
"state": "string",
"supply_order_id": 0,
"supply_order_number": "string",
"supply_warehouse": {
"address": "string",
"name": "string",
"warehouse_id": 0
},
"time_left_to_prepare_supply": 0,
"time_left_to_select_supply_variant": 0,
"total_items_count": 0,
"total_quantity": 0,
"vehicle_info": {
"driver_name": "string",
"driver_phone": "string",
"vehicle_model": "string",
"vehicle_number": "string"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetSupplyRequestInfoParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBO().GetSupplyRequestInfo(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestListProductsInSupplyRequest(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListProductsInSupplyRequestParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListProductsInSupplyRequestParams{
Page: 0,
PageSize: 0,
SupplyOrderId: 0,
},
`{
"has_next": true,
"items": [
{
"icon_path": "string",
"name": "string",
"offer_id": "string",
"quantity": 0,
"sku": 0
}
],
"total_items_count": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListProductsInSupplyRequestParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBO().ListProductsInSupplyRequest(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetWarehouseWorkload(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": [
{
"schedule": {
"capacity": [
{
"start": "2019-08-24T14:15:22Z",
"end": "2019-08-24T14:15:22Z",
"value": 0
}
],
"date": "2019-08-24T14:15:22Z"
},
"warehouse": {
"id": "string",
"name": "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))
resp, err := c.FBO().GetWarehouseWorkload()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

File diff suppressed because it is too large Load Diff

View File

@@ -751,11 +751,11 @@ func TestListOfShipmentCertificates(t *testing.T) {
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"}, map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListOfShipmentCertificatesParams{ &ListOfShipmentCertificatesParams{
Limit: 100, Limit: 100,
Filter: ListOfShipmentCertificates{ Filter: ListOfShipmentCertificatesFilter{
DateFrom: "2021-08-04", DateFrom: "2021-08-04",
DateTo: "2022-08-04", DateTo: "2022-08-04",
IntegrationType: "ozon", IntegrationType: "ozon",
Status: []string{"delivered"}, Status: []ShipmentCertificateFilterStatus{ShitmentCertificateFilterFormed},
}, },
}, },
`{ `{
@@ -1132,9 +1132,8 @@ func TestGetLabeling(t *testing.T) {
}, },
`{ `{
"result": { "result": {
"error": "24", "status": "completed",
"file_url": "some-url", "file_url": "https://cdn1.ozone.ru/s3/sc-temporary/e6/0c/e60cdfd7aed78c2b44d134504fbd591d.pdf"
"status": "completed"
} }
}`, }`,
}, },
@@ -1186,7 +1185,9 @@ func TestPrintLabeling(t *testing.T) {
PostingNumber: []string{"48173252-0034-4"}, PostingNumber: []string{"48173252-0034-4"},
}, },
`{ `{
"content": "https://cdn1.ozone.ru/s3/ozon-disk-api/c4a11c8b748033daf6cdd44aca7ed4c492e55d6f4810f13feae4792afa7934191647255705" "content_type": "application/pdf",
"file_name": "ticket-170660-2023-07-13T13:17:06Z.pdf",
"file_content": "%PDF-1.7\n%âãÏÓ\n53 0 obj\n<</MarkInfo<</Marked true/Type/MarkInfo>>/Pages 9 0 R/StructTreeRoot 10 0 R/Type/Catalog>>\nendobj\n8 0 obj\n<</Filter/FlateDecode/Length 2888>>\nstream\nxœå[[ݶ\u0011~?¿BÏ\u0005Bs†\u001c^\u0000Àwí5ú\u0010 m\u0016Èsà¦)\n;hÒ\u0014èÏïG‰\u0014)‰<{äµ] ]?¬¬oIÎ}†¤F±‰óϤñï\u001bÕü×X­´OÏï?^~¹$<ø¨È9q\u0013Y\u0012åñì§_¼|ÿ‡égü\t+\u0012\u001bxžª}Æxšҿ¿¼›–‡_º¼xg¦Ÿþ5Oku˜œÌ3ýíògüûå\"Ni\u0016C\u0001°\u000fA9g‰'r¢\"\u0013YóĪ\u0018NÑ{\u001d–ÕóZ¬\\Ô\""
}`, }`,
}, },
// Test No Client-Id or Api-Key // Test No Client-Id or Api-Key
@@ -1212,6 +1213,12 @@ func TestPrintLabeling(t *testing.T) {
if resp.StatusCode != test.statusCode { if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
} }
if resp.StatusCode == http.StatusOK {
if resp.Content == "" {
t.Error("content cannot be empty")
}
}
} }
} }
@@ -1480,8 +1487,8 @@ func TestRescheduleShipmentDeliveryDate(t *testing.T) {
&RescheduleShipmentDeliveryDateParams{ &RescheduleShipmentDeliveryDateParams{
PostingNumber: "23281294-0063-2", PostingNumber: "23281294-0063-2",
NewTimeslot: RescheduleShipmentDeliveryDateTimeslot{ NewTimeslot: RescheduleShipmentDeliveryDateTimeslot{
DeliveryDateBegin: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2023-03-03T11:07:00.381Z"), From: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2023-03-25T08:51:56.932Z"),
DeliveryDateEnd: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2023-03-03T11:07:00.381Z"), To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2023-03-25T08:51:56.932Z"),
}, },
}, },
`{ `{
@@ -1888,9 +1895,9 @@ func TestGetDigitalAct(t *testing.T) {
DocType: "act_of_acceptance", DocType: "act_of_acceptance",
}, },
`{ `{
"content": "string", "content_type": "application/pdf",
"name": "string", "file_name": "20816409_act_of_mismatch.pdf",
"type": "string" "file_content": "%PDF-1.4\n%ÓôÌá\n1 0 obj\n<<\n/Creator(Chromium)\n/Producer(PDFsharp 1.50.5147 \\([www.pdfsharp.com|http://www.pdfsharp.com/]\\) \\(Original: Skia/PDF m103\\))\n/CreationDate(D:20230625092529+00'00')\n/ModDate(D:20230625092529+00'00')\n>>\nendobj\n2 0 obj\n<<\n/Type/Page\n/Resources\n<<\n/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]\n/ExtGState\n<<\n/G3 3 0 R\n/G8 8 0 R\n>>\n/XObject\n<<\n/X6 6 0 R\n/X7 7 0 R\n>>\n/Font\n<<\n/F4 4 0 R\n/F5 5 0 R\n>>\n>>\n/MediaBox[0 0 594.96 841.92]\n/Contents 9 0 R\n/StructParents 0\n/Parent 13 0 R\n/Group\n<<\n/CS/DeviceRGB\n/S/Transparency\n>>\n>>\nendobj\n3 0 obj\n<<\n/ca 1\n/BM/Normal\n>>\nendobj\n4 0 obj\n<<\n/Type/Font\n/Subtype/Type0\n/BaseFont/AAAAAA+LiberationSans\n/Encoding/Identity-H\n/DescendantFonts[160 0 R]\n/ToUnicode 161 0 R\n>>\nendobj\n5 0 obj\n<<\n/Type/Font\n/Subtype/Type0\n/BaseFont/BAAAAA+LiberationSans-Bold\n/Encoding/Identity-H\n/DescendantFonts[164 0"
}`, }`,
}, },
// Test No Client-Id or Api-Key // Test No Client-Id or Api-Key
@@ -2526,3 +2533,169 @@ func TestETGBCustomsDeclarations(t *testing.T) {
} }
} }
} }
func TestBarcodeFromProductShipment(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *BarcodeFromProductShipmentParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&BarcodeFromProductShipmentParams{
Id: 295662811,
},
`{
"content_type": "image/png",
"file_name": "20913984_barcode.png",
"file_content": "‰PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0003\u0010\u0000\u0000\u0000\u0010\u0000\u0000\u0000\u0000íZ\u000e'\u0000\u0000\u0002pIDATxœìÕÁJ\u00031\u0014@Q+þÿ/×E\u0017\u000e¼›\u0010u¡-ç¬$£Éˌp?î÷·§t» }ýü¸ÃcåzŸ¹2w˜OWû\\Ϛ뫧×Ùö;œì|rÇýßîç¼úî{˜§¬N?™í7oì•v¸®Ÿµ¹Ãù„û•¹¾ÿÏ9ÿî?›až¸ºéê7O&߿É9çÉ\u000eÏáý¯\u0007\u0000à\u0012\b\u0000’@\u0000\u0004\u0002€$\u0010\u0000$\u0000 \t\u0004\u0000I \u0000H\u0002\u0001@\u0012\b\u0000’@\u0000\u0004\u0002€$\u0010\u0000$\u0000 \t\u0004\u0000I \u0000H\u0002\u0001@\u0012\b\u0000’@\u0000\u0004\u0002€$\u0010\u0000$\u0000 \t\u0004\u0000I \u0000H\u0002\u0001@\u0012\b\u0000’@\u0000\u0004\u0002€$\u0010\u0000$\u0000 \t\u0004\u0000I \u0000H\u0002\u0001@\u0012\b\u0000’@\u0000\u0004\u0002€$\u0010\u0000"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&BarcodeFromProductShipmentParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBS().BarcodeFromProductShipment(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Content == "" {
t.Errorf("content cannot be empty")
}
if resp.Type == "" {
t.Error("type cannot be empty")
}
if resp.Name == "" {
t.Error("name cannot be empty")
}
}
}
}
func TestBarcodeValueFromProductShipment(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *BarcodeValueFromProductShipmentParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&BarcodeValueFromProductShipmentParams{
Id: 295662811,
},
`{
"result": "%303%24276481394"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&BarcodeValueFromProductShipmentParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBS().BarcodeValueFromProductShipment(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Result == "" {
t.Errorf("result cannot be empty")
}
}
}
}
func TestGetActPDF(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetActPDFParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetActPDFParams{
Id: 22435521842000,
},
`{
"content_type": "application/pdf",
"file_name": "20928233.pdf",
"file_content": "binarystring"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetActPDFParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.FBS().GetActPDF(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.FileContent == "" {
t.Errorf("result cannot be empty")
}
}
}
}

View File

@@ -20,126 +20,132 @@ type ReportOnSoldProductsResponse struct {
core.CommonResponse core.CommonResponse
// Query result // Query result
Result []struct { Result []ReportonSoldProductsResult `json:"result"`
// Report title page }
Header []struct {
// Report ID
Id string `json:"num"`
// Report generation date type ReportonSoldProductsResult struct {
DocDate string `json:"doc_date"` // Report title page
Header []ReportOnSoldProductsResultHeader `json:"header"`
// Date of the offer agreement // Report table
ContractDate string `json:"contract_date"` Rows []ReportOnSoldProductsResultRow `json:"rows"`
}
// Offer agreement number type ReportOnSoldProductsResultHeader struct {
ContractNum string `json:"contract_num"` // Report ID
Id string `json:"num"`
// Currency of your prices // Report generation date
CurrencyCode string `json:"currency_code"` DocDate string `json:"doc_date"`
// Amount to accrue // Date of the offer agreement
DocAmount float64 `json:"doc_amount"` ContractDate string `json:"contract_date"`
// Amount to accrue with VAT // Offer agreement number
VATAmount float64 `json:"vat_amount"` ContractNum string `json:"contract_num"`
// Payer's TIN // Currency of your prices
PayerINN string `json:"payer_inn"` CurrencyCode string `json:"currency_code"`
// Payer's Tax Registration Reason Code (KPP) // Amount to accrue
PayerKPP string `json:"payer_kpp"` DocAmount float64 `json:"doc_amount"`
// Payer's name // Amount to accrue with VAT
PayerName string `json:"payer_name"` VATAmount float64 `json:"vat_amount"`
// Recipient's TIN // Payer's TIN
RecipientINN string `json:"rcv_inn"` PayerINN string `json:"payer_inn"`
// Recipient's Tax Registration Reason Code (KPP) // Payer's Tax Registration Reason Code (KPP)
RecipientKPP string `json:"rcv_kpp"` PayerKPP string `json:"payer_kpp"`
// Recipient's name // Payer's name
RecipientName string `json:"rcv_name"` PayerName string `json:"payer_name"`
// Period start in the report // Recipient's TIN
StartDate string `json:"start_date"` RecipientINN string `json:"rcv_inn"`
// Period end in the report // Recipient's Tax Registration Reason Code (KPP)
StopDate string `json:"stop_date"` RecipientKPP string `json:"rcv_kpp"`
} `json:"header"`
// Report table // Recipient's name
Rows []struct { RecipientName string `json:"rcv_name"`
// Row number
RowNumber int32 `json:"row_number"`
// Product ID // Period start in the report
ProductId int64 `json:"product_id"` StartDate string `json:"start_date"`
// Product name // Period end in the report
ProductName string `json:"product_name"` StopDate string `json:"stop_date"`
}
// Product barcode type ReportOnSoldProductsResultRow struct {
Barcode string `json:"barcode"` // Row number
RowNumber int32 `json:"row_number"`
// Product identifier in the seller's system // Product ID
OfferId string `json:"offer_id"` ProductId int64 `json:"product_id"`
// Sales commission by category // Product name
CommissionPercent float64 `json:"commission_percent"` ProductName string `json:"product_name"`
// Seller's price with their discount // Product barcode
Price float64 `json:"price"` Barcode string `json:"barcode"`
// Selling price: the price at which the customer purchased the product. For sold products // Product identifier in the seller's system
PriceSale float64 `json:"price_sale"` OfferId string `json:"offer_id"`
// Sold for amount. // Sales commission by category
// CommissionPercent float64 `json:"commission_percent"`
// Sold products cost considering the quantity and regional coefficients. Calculation is made by the sale_amount price
SaleAmount float64 `json:"sale_amount"`
// Commission for sold products, including discounts and extra charges // Seller's price with their discount
SaleCommission float64 `json:"sale_commission"` Price float64 `json:"price"`
// Extra charge at the expense of Ozon. // Selling price: the price at which the customer purchased the product. For sold products
// PriceSale float64 `json:"price_sale"`
// Amount that Ozon will compensate the seller if the Ozon discount is greater than or equal to the sales commission
SaleDiscount float64 `json:"sale_discount"`
// Total accrual for the products sold. // Sold for amount.
// //
// Amount after deduction of sales commission, application of discounts and extra charges // Sold products cost considering the quantity and regional coefficients. Calculation is made by the sale_amount price
SalePriceSeller float64 `json:"sale_price_seller"` SaleAmount float64 `json:"sale_amount"`
// Quantity of products sold at the price_sale price // Commission for sold products, including discounts and extra charges
SaleQuantity int32 `json:"sale_qty"` SaleCommission float64 `json:"sale_commission"`
// Price at which the customer purchased the product. For returned products // Extra charge at the expense of Ozon.
ReturnSale float64 `json:"return_sale"` //
// 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"`
// Cost of returned products, taking into account the quantity and regional coefficients. // Total accrual for the products sold.
// Calculation is carried out at the return_sale price //
ReturnAmount float64 `json:"return_amount"` // Amount after deduction of sales commission, application of discounts and extra charges
SalePriceSeller float64 `json:"sale_price_seller"`
// Commission including the quantity of products, discounts and extra charges. // Quantity of products sold at the price_sale price
// Ozon compensates it for the returned products SaleQuantity int32 `json:"sale_qty"`
ReturnCommission float64 `json:"return_commission"`
// Extra charge at the expense of Ozon. // Price at which the customer purchased the product. For returned products
// ReturnSale float64 `json:"return_sale"`
// 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 // Cost of returned products, taking into account the quantity and regional coefficients.
ReturnPriceSeller float64 `json:"return_price_seller"` // Calculation is carried out at the return_sale price
ReturnAmount float64 `json:"return_amount"`
// Quantity of returned products // Commission including the quantity of products, discounts and extra charges.
ReturnQuantity int32 `json:"return_qty"` // Ozon compensates it for the returned products
} `json:"rows"` ReturnCommission float64 `json:"return_commission"`
} `json:"result"`
// 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"`
} }
// Returns information on products sold and returned within a month. Canceled or non-purchased products are not included. // Returns information on products sold and returned within a month. Canceled or non-purchased products are not included.
@@ -150,7 +156,7 @@ func (c Finance) ReportOnSoldProducts(params *ReportOnSoldProductsParams) (*Repo
resp := &ReportOnSoldProductsResponse{} resp := &ReportOnSoldProductsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -196,40 +202,42 @@ type GetTotalTransactionsSumResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetTotalTransactionsSumResult `json:"result"`
// Total cost of products and returns for specified period }
AccrualsForSale float64 `json:"accruals_for_sale"`
// Compensations type GetTotalTransactionsSumResult struct {
CompensationAmount float64 `json:"compensatino_amount"` // Total cost of products and returns for specified period
AccrualsForSale float64 `json:"accruals_for_sale"`
// Charges for delivery and returns when working under rFBS scheme // Compensations
MoneyTransfer float64 `json:"money_transfer"` CompensationAmount float64 `json:"compensatino_amount"`
// Other accurals // Charges for delivery and returns when working under rFBS scheme
OthersAmount float64 `json:"others_amount"` MoneyTransfer float64 `json:"money_transfer"`
// Cost of shipment processing, orders packaging, pipeline and last mile services, and delivery cost before the new commissions and rates applied from February 1, 2021. // Other accurals
// OthersAmount float64 `json:"others_amount"`
// Pipeline is delivery of products from one cluster to another.
//
// Last mile is products delivery to the pick-up point, parcle terminal, or by courier
ProcessingAndDelivery float64 `json:"processing_and_delivery"`
// Cost of reverse pipeline, returned, canceled and unredeemed orders processing, and return cost before the new commissions and rates applied from February 1, 2021. // Cost of shipment processing, orders packaging, pipeline and last mile services, and delivery cost before the new commissions and rates applied from February 1, 2021.
// //
// Pipeline is delivery of products from one cluster to another. // Pipeline is delivery of products from one cluster to another.
// //
// Last mile is products delivery to the pick-up point, parcle terminal, or by courier // Last mile is products delivery to the pick-up point, parcle terminal, or by courier
RefundsAndCancellations float64 `json:"refunds_and_cancellations"` ProcessingAndDelivery float64 `json:"processing_and_delivery"`
// The commission withheld when the product was sold and refunded when the product was returned // Cost of reverse pipeline, returned, canceled and unredeemed orders processing, and return cost before the new commissions and rates applied from February 1, 2021.
SaleCommission float64 `json:"sale_commission"` //
// Pipeline is delivery of products from one cluster to another.
//
// Last mile is products delivery to the pick-up point, parcle terminal, or by courier
RefundsAndCancellations float64 `json:"refunds_and_cancellations"`
// The additional services cost that are not directly related to deliveries and returns. // The commission withheld when the product was sold and refunded when the product was returned
// For example, promotion or product placement SaleCommission float64 `json:"sale_commission"`
ServicesAmount float64 `json:"services_amount"`
} `json:"result"` // The additional services cost that are not directly related to deliveries and returns.
// For example, promotion or product placement
ServicesAmount float64 `json:"services_amount"`
} }
// Returns total sums for transactions for specified period // Returns total sums for transactions for specified period
@@ -238,7 +246,156 @@ func (c Finance) GetTotalTransactionsSum(params *GetTotalTransactionsSumParams)
resp := &GetTotalTransactionsSumResponse{} resp := &GetTotalTransactionsSumResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListTransactionsParams struct {
// Filter
Filter ListTransactionsFilter `json:"filter"`
// Number of the page returned in the request
Page int64 `json:"page"`
// Number of items on the page
PageSize int64 `json:"page_size"`
}
type ListTransactionsFilter struct {
// Filter by date
Date ListTransactionsFilterDate `json:"date"`
// Operation type
OperationType string `json:"operation_type"`
// Shipment number
PostingNumber string `json:"posting_number"`
// Transaction type
TransactionType string `json:"transaction_type"`
}
type ListTransactionsFilterDate struct {
// Period start.
//
// Format: YYYY-MM-DDTHH:mm:ss.sssZ.
// Example: 2019-11-25T10:43:06.51
From time.Time `json:"from"`
// Period end.
//
// Format: YYYY-MM-DDTHH:mm:ss.sssZ.
// Example: 2019-11-25T10:43:06.51
To time.Time `json:"to"`
}
type ListTransactionsResponse struct {
core.CommonResponse
// Method result
Result ListTransactionsResult `json:"result"`
}
type ListTransactionsResult struct {
// Transactions infromation
Operations []ListTransactionsResultOperation `json:"operations"`
// Number of pages
PageCount int64 `json:"page_count"`
// Number of products
RowCount int64 `json:"row_count"`
}
type ListTransactionsResultOperation struct {
// Cost of the products with seller's discounts applied
AccrualsForSale float64 `json:"accruals_for_sale"`
// Total transaction sum
Amount float64 `json:"amount"`
// Delivery cost for charges by rates that were in effect until February 1, 2021, and for charges for bulky products
DeliveryCharge float64 `json:"delivery_charge"`
// Product information
Items []ListTransactionsResultOperationItem `json:"items"`
// Operation date
OperationDate string `json:"operation_date"`
// Operation identifier
OperationId int64 `json:"operation_id"`
// Operation type
OperationType string `json:"operation_type"`
// Operation type name
OperationTypeName string `json:"operation_type_name"`
// Shipment information
Posting ListTransactionsResultOperationPosting `json:"posting"`
// Returns and cancellation cost for charges by rates that were in effect until February 1, 2021, and for charges for bulky products
ReturnDeliveryCharge float64 `json:"return_delivery_charge"`
// Sales commission or sales commission refund
SaleCommission float64 `json:"sale_commission"`
// Additional services
Services []ListTransactionsResultOperationService `json:"services"`
// Transaction type
Type string `json:"type"`
}
type ListTransactionsResultOperationItem struct {
// Product name
Name string `json:"name"`
// Product identifier in the Ozon system, SKU
SKU int64 `json:"sku"`
}
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
DeliverySchema string `json:"delivery_schema"`
// Date the product was accepted for processing
OrderDate string `json:"order_date"`
// Shipment number
PostingNumber string `json:"posting_number"`
// Warehouse identifier
WarehouseId int64 `json:"warehouse_id"`
}
type ListTransactionsResultOperationService struct {
// Service name
Name string `json:"name"`
// Price
Price float64 `json:"price"`
}
// Returns detailed information on all accruals. The maximum period for which you can get information in one request is 1 month.
//
// If you don't specify the posting_number in request, the response contains all shipments for the specified period or shipments of a certain type
func (c Finance) ListTransactions(params *ListTransactionsParams) (*ListTransactionsResponse, error) {
url := "/v3/finance/transaction/list"
resp := &ListTransactionsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -174,3 +174,91 @@ func TestGetTotalTransactionsSum(t *testing.T) {
} }
} }
} }
func TestListTransactions(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListTransactionsParams
response string
errorMessage string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListTransactionsParams{
Filter: ListTransactionsFilter{
Date: ListTransactionsFilterDate{
From: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-01T00:00:00.000Z"),
To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2021-11-02T00:00:00.000Z"),
},
TransactionType: "ALL",
},
Page: 1,
PageSize: 1000,
},
`{
"result": {
"operations": [
{
"operation_id": 11401182187840,
"operation_type": "MarketplaceMarketingActionCostOperation",
"operation_date": "2021-11-01 00:00:00",
"operation_type_name": "Услуги продвижения товаров",
"delivery_charge": 0,
"return_delivery_charge": 0,
"accruals_for_sale": 0,
"sale_commission": 0,
"amount": -6.46,
"type": "services",
"posting": {
"delivery_schema": "",
"order_date": "",
"posting_number": "",
"warehouse_id": 0
},
"items": [],
"services": []
}
],
"page_count": 1,
"row_count": 355
}
}`,
"",
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListTransactionsParams{},
`{
"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))
resp, err := c.Finance().ListTransactions(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode != http.StatusOK {
if resp.Message != test.errorMessage {
t.Errorf("got wrong error message: got: %s, expected: %s", resp.Message, test.errorMessage)
}
}
}
}

99
ozon/invoices.go Normal file
View File

@@ -0,0 +1,99 @@
package ozon
import (
"net/http"
core "github.com/diphantxm/ozon-api-client"
)
type Invoices struct {
client *core.Client
}
type CreateUpdateProformaLinkParams struct {
// Shipment number
PostingNumber string `json:"posting_number"`
// Proforma invoice link
URL string `json:"url"`
}
type CreateUpdateProformaLinkResponse struct {
core.CommonResponse
// Method result
Result bool `json:"result"`
}
// Create or edit proforma invoice link for VAT refund to Turkey sellers
func (c Invoices) CreateUpdate(params *CreateUpdateProformaLinkParams) (*CreateUpdateProformaLinkResponse, error) {
url := "/v1/invoice/create-or-update"
resp := &CreateUpdateProformaLinkResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetProformaLinkParams struct {
// Shipment number
PostingNumber string `json:"posting_number"`
}
type GetProformaLinkResponse struct {
core.CommonResponse
// Method result
Result GetProformaLinkResult `json:"result"`
}
type GetProformaLinkResult struct {
// Proforma invoice link
FileURL string `json:"file_url"`
}
// Get a proforma invoice link
func (c Invoices) Get(params *GetProformaLinkParams) (*GetProformaLinkResponse, error) {
url := "/v1/invoice/get"
resp := &GetProformaLinkResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type DeleteProformaLinkParams struct {
// Shipment number
PostingNumber string `json:"posting_number"`
}
type DeleteProformaLinkResponse struct {
core.CommonResponse
// Method result
Result bool `json:"result"`
}
func (c Invoices) Delete(params *DeleteProformaLinkParams) (*DeleteProformaLinkResponse, error) {
url := "/v1/invoice/delete"
resp := &DeleteProformaLinkResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

149
ozon/invoices_test.go Normal file
View File

@@ -0,0 +1,149 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestCreateUpdateProformaLink(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *CreateUpdateProformaLinkParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&CreateUpdateProformaLinkParams{
PostingNumber: "posting number",
URL: "link",
},
`{
"result": true
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&CreateUpdateProformaLinkParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Invoices().CreateUpdate(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetProformaLink(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetProformaLinkParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetProformaLinkParams{
PostingNumber: "posting number",
},
`{
"result": {
"file_url": "string"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetProformaLinkParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Invoices().Get(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestDeleteProformaLink(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *DeleteProformaLinkParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&DeleteProformaLinkParams{
PostingNumber: "posting number",
},
`{
"result": true
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&DeleteProformaLinkParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Invoices().Delete(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -13,16 +13,24 @@ const (
type Client struct { type Client struct {
client *core.Client client *core.Client
analytics *Analytics analytics *Analytics
fbo *FBO fbo *FBO
fbs *FBS fbs *FBS
finance *Finance finance *Finance
products *Products products *Products
promotions *Promotions promotions *Promotions
rating *Rating rating *Rating
warehouses *Warehouses warehouses *Warehouses
returns *Returns returns *Returns
reports *Reports reports *Reports
cancellations *Cancellations
categories *Categories
polygons *Polygons
invoices *Invoices
brands *Brands
chats *Chats
certificates *Certificates
strategies *Strategies
} }
func (c Client) Analytics() *Analytics { func (c Client) Analytics() *Analytics {
@@ -65,6 +73,38 @@ func (c Client) Reports() *Reports {
return c.reports return c.reports
} }
func (c Client) Cancellations() *Cancellations {
return c.cancellations
}
func (c Client) Categories() *Categories {
return c.categories
}
func (c Client) Polygons() *Polygons {
return c.polygons
}
func (c Client) Invoices() *Invoices {
return c.invoices
}
func (c Client) Brands() *Brands {
return c.brands
}
func (c Client) Chats() *Chats {
return c.chats
}
func (c Client) Certificates() *Certificates {
return c.certificates
}
func (c Client) Strategies() *Strategies {
return c.strategies
}
func NewClient(clientId, apiKey string) *Client { func NewClient(clientId, apiKey string) *Client {
coreClient := core.NewClient(DefaultAPIBaseUrl, map[string]string{ coreClient := core.NewClient(DefaultAPIBaseUrl, map[string]string{
"Client-Id": clientId, "Client-Id": clientId,
@@ -72,17 +112,25 @@ func NewClient(clientId, apiKey string) *Client {
}) })
return &Client{ return &Client{
client: coreClient, client: coreClient,
analytics: &Analytics{client: coreClient}, analytics: &Analytics{client: coreClient},
fbo: &FBO{client: coreClient}, fbo: &FBO{client: coreClient},
fbs: &FBS{client: coreClient}, fbs: &FBS{client: coreClient},
finance: &Finance{client: coreClient}, finance: &Finance{client: coreClient},
products: &Products{client: coreClient}, products: &Products{client: coreClient},
promotions: &Promotions{client: coreClient}, promotions: &Promotions{client: coreClient},
rating: &Rating{client: coreClient}, rating: &Rating{client: coreClient},
warehouses: &Warehouses{client: coreClient}, warehouses: &Warehouses{client: coreClient},
returns: &Returns{client: coreClient}, returns: &Returns{client: coreClient},
reports: &Reports{client: coreClient}, reports: &Reports{client: coreClient},
cancellations: &Cancellations{client: coreClient},
categories: &Categories{client: coreClient},
polygons: &Polygons{client: coreClient},
invoices: &Invoices{client: coreClient},
brands: &Brands{client: coreClient},
chats: &Chats{client: coreClient},
certificates: &Certificates{client: coreClient},
strategies: &Strategies{client: coreClient},
} }
} }
@@ -90,16 +138,24 @@ func NewMockClient(handler http.HandlerFunc) *Client {
coreClient := core.NewMockClient(handler) coreClient := core.NewMockClient(handler)
return &Client{ return &Client{
client: coreClient, client: coreClient,
analytics: &Analytics{client: coreClient}, analytics: &Analytics{client: coreClient},
fbo: &FBO{client: coreClient}, fbo: &FBO{client: coreClient},
fbs: &FBS{client: coreClient}, fbs: &FBS{client: coreClient},
finance: &Finance{client: coreClient}, finance: &Finance{client: coreClient},
products: &Products{client: coreClient}, products: &Products{client: coreClient},
promotions: &Promotions{client: coreClient}, promotions: &Promotions{client: coreClient},
rating: &Rating{client: coreClient}, rating: &Rating{client: coreClient},
warehouses: &Warehouses{client: coreClient}, warehouses: &Warehouses{client: coreClient},
returns: &Returns{client: coreClient}, returns: &Returns{client: coreClient},
reports: &Reports{client: coreClient}, reports: &Reports{client: coreClient},
cancellations: &Cancellations{client: coreClient},
categories: &Categories{client: coreClient},
polygons: &Polygons{client: coreClient},
invoices: &Invoices{client: coreClient},
brands: &Brands{client: coreClient},
chats: &Chats{client: coreClient},
certificates: &Certificates{client: coreClient},
strategies: &Strategies{client: coreClient},
} }
} }

110
ozon/polygons.go Normal file
View File

@@ -0,0 +1,110 @@
package ozon
import (
"net/http"
core "github.com/diphantxm/ozon-api-client"
)
type Polygons struct {
client *core.Client
}
type CreateDeliveryPolygonParams struct {
// Delivery polygon coordinates in [[[lat long]]] format
Coordinates string `json:"coordinates"`
}
type CreateDeliveryPolygonResponse struct {
core.CommonResponse
// Polygon identifier
PolygonId int64 `json:"polygon_id"`
}
// You can link a polygon to the delivery method.
//
// Create a polygon getting its coordinates on https://geojson.io: mark at least 3 points on the map and connect them
func (c Polygons) CreateDelivery(params *CreateDeliveryPolygonParams) (*CreateDeliveryPolygonResponse, error) {
url := "/v1/polygon/create"
resp := &CreateDeliveryPolygonResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type LinkDeliveryMethodToPolygonParams struct {
// Delivery method identifier
DeliveryMethodId int32 `json:"delivery_method_id"`
// Polygons list
Polygons []LinkDeliveryMethodToPolygonPolygon `json:"polygons"`
// Warehouse location
WarehouseLocation LinkDeliveryMethodToPolygonWarehouse `json:"warehouse_location"`
}
type LinkDeliveryMethodToPolygonPolygon struct {
// Polygon identifier
PolygonId int64 `json:"polygon_id"`
// Delivery time within polygon in minutes
Time int64 `json:"time"`
}
type LinkDeliveryMethodToPolygonWarehouse struct {
// Warehouse location latitude
Latitude string `json:"lat"`
// Warehouse location longitude
Longitude string `json:"log"`
}
type LinkDeliveryMethodToPolygonResponse struct {
core.CommonResponse
}
// Link delivery method to a delivery polygon
func (c Polygons) Link(params *LinkDeliveryMethodToPolygonParams) (*LinkDeliveryMethodToPolygonResponse, error) {
url := "/v1/polygon/bind"
resp := &LinkDeliveryMethodToPolygonResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type DeletePolygonParams struct {
// Polygons identifiers list
PolygonIds []int64 `json:"polygon_ids"`
}
type DeletePolygonResponse struct {
core.CommonResponse
}
// Delete polygon
func (c Polygons) Delete(params *DeletePolygonParams) (*DeletePolygonResponse, error) {
url := "/v1/polygon/delete"
resp := &DeletePolygonResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

152
ozon/polygons_test.go Normal file
View File

@@ -0,0 +1,152 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestCreateDeliveryPolygon(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *CreateDeliveryPolygonParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&CreateDeliveryPolygonParams{
Coordinates: "[[[30.149574279785153,59.86550435303646],[30.21205902099609,59.846884387977326],[30.255661010742184,59.86240174913176],[30.149574279785153,59.86550435303646]]]",
},
`{
"polygonId": "1323"
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&CreateDeliveryPolygonParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Polygons().CreateDelivery(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestLinkDeliveryMethodToPolygon(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *LinkDeliveryMethodToPolygonParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&LinkDeliveryMethodToPolygonParams{
DeliveryMethodId: 0,
Polygons: []LinkDeliveryMethodToPolygonPolygon{
{
PolygonId: 1323,
Time: 30,
},
},
WarehouseLocation: LinkDeliveryMethodToPolygonWarehouse{
Latitude: "58.52391272075821",
Longitude: "31.236791610717773",
},
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&LinkDeliveryMethodToPolygonParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Polygons().Link(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestDeletePolygon(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *DeletePolygonParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&DeletePolygonParams{
PolygonIds: []int64{1323},
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&DeletePolygonParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Polygons().Delete(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -168,7 +168,24 @@ func TestGetProductDetails(t *testing.T) {
"has_stock": false, "has_stock": false,
"active_product": false "active_product": false
}, },
"price_index": "0.00", "price_indexes": {
"external_index_data": {
"minimal_price": "string",
"minimal_price_currency": "string",
"price_index_value": 0
},
"ozon_index_data": {
"minimal_price": "string",
"minimal_price_currency": "string",
"price_index_value": 0
},
"price_index": "WITHOUT_INDEX",
"self_marketplaces_index_data": {
"minimal_price": "string",
"minimal_price_currency": "string",
"price_index_value": 0
}
},
"commissions": [], "commissions": [],
"volume_weight": 0.1, "volume_weight": 0.1,
"is_prepayment": false, "is_prepayment": false,
@@ -233,7 +250,7 @@ func TestGetProductDetails(t *testing.T) {
"state_updated_at": "2021-10-21T15:48:03.927309Z" "state_updated_at": "2021-10-21T15:48:03.927309Z"
} }
} }
}`, }`,
}, },
// Test No Client-Id or Api-Key // Test No Client-Id or Api-Key
{ {
@@ -431,12 +448,13 @@ func TestUpdatePrices(t *testing.T) {
&UpdatePricesParams{ &UpdatePricesParams{
Prices: []UpdatePricesPrice{ Prices: []UpdatePricesPrice{
{ {
AutoActionEnabled: "UNKNOWN", AutoActionEnabled: "UNKNOWN",
CurrencyCode: "RUB", CurrencyCode: "RUB",
MinPrice: "800", MinPrice: "800",
OldPrice: "0", OldPrice: "0",
Price: "1448", Price: "1448",
ProductId: 1386, ProductId: 1386,
PriceStrategyEnabled: PriceStrategyUnknown,
}, },
}, },
}, },
@@ -2197,3 +2215,338 @@ func TestStatusOfUploadingActivationCodes(t *testing.T) {
} }
} }
} }
func TestGetProductPriceInfo(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetProductPriceInfoParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetProductPriceInfoParams{
Filter: GetProductPriceInfoFilter{
OfferId: []string{"356792"},
ProductId: []int64{243686911},
Visibility: "ALL",
},
},
`{
"result": {
"items": [
{
"product_id": 243686911,
"offer_id": "356792",
"price": {
"currency_code": "RUB",
"price": "499.0000",
"old_price": "579.0000",
"premium_price": "",
"recommended_price": "",
"retail_price": "",
"vat": "0.200000",
"min_ozon_price": "",
"marketing_price": "",
"marketing_seller_price": "",
"auto_action_enabled": true
},
"price_index": "0.00",
"commissions": {
"sales_percent": 15,
"fbo_fulfillment_amount": 0,
"fbo_direct_flow_trans_min_amount": 31,
"fbo_direct_flow_trans_max_amount": 46.5,
"fbo_deliv_to_customer_amount": 14.75,
"fbo_return_flow_amount": 50,
"fbo_return_flow_trans_min_amount": 21.7,
"fbo_return_flow_trans_max_amount": 21.7,
"fbs_first_mile_min_amount": 0,
"fbs_first_mile_max_amount": 0,
"fbs_direct_flow_trans_min_amount": 41,
"fbs_direct_flow_trans_max_amount": 61.5,
"fbs_deliv_to_customer_amount": 60,
"fbs_return_flow_amount": 40,
"fbs_return_flow_trans_min_amount": 41,
"fbs_return_flow_trans_max_amount": 61.5
},
"marketing_actions": null,
"volume_weight": 0
}
],
"total": 1,
"last_id": "ceVуbA=="
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetProductPriceInfoParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Products().GetProductPriceInfo(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetMarkdownInfo(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetMarkdownInfoParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetMarkdownInfoParams{
DiscountedSKUs: []string{"635548518"},
},
`{
"items": [
{
"discounted_sku": 635548518,
"sku": 320067758,
"condition_estimation": "4",
"packaging_violation": "",
"warranty_type": "",
"reason_damaged": "Механическое повреждение",
"comment_reason_damaged": "повреждена заводская упаковка",
"defects": "",
"mechanical_damage": "",
"package_damage": "",
"shortage": "",
"repair": "",
"condition": ""
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetMarkdownInfoParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Products().GetMarkdownInfo(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Items) > 0 {
if fmt.Sprint(resp.Items[0].DiscountedSKU) != test.params.DiscountedSKUs[0] {
t.Errorf("SKUs in reqest and resonse are not equal")
}
}
}
}
}
func TestSetDiscountOnMarkdownProduct(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *SetDiscountOnMarkdownProductParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&SetDiscountOnMarkdownProductParams{
Discount: 0,
ProductId: 0,
},
`{
"result": true
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&SetDiscountOnMarkdownProductParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Products().SetDiscountOnMarkdownProduct(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestNumberOfSubsToProductAvailability(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *NumberOfSubsToProductAvailabilityParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&NumberOfSubsToProductAvailabilityParams{
SKUS: []int64{1234},
},
`{
"result": [
{
"count": 2,
"sku": 1234
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&NumberOfSubsToProductAvailabilityParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Products().NumberOfSubsToProductAvailability(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result) != len(test.params.SKUS) {
t.Errorf("Length of SKUS in request and response are not equal")
}
if len(resp.Result) > 0 {
if resp.Result[0].SKU != test.params.SKUS[0] {
t.Errorf("SKU in request and response are not equal")
}
}
}
}
}
func TestUpdateCharacteristics(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *UpdateCharacteristicsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&UpdateCharacteristicsParams{
Items: []UpdateCharacteristicsItem{
{
Attributes: []UpdateCharacteristicsItemAttribute{
{
ComplexId: 0,
Id: 0,
Values: []UpdateCharacteristicsItemValue{
{
DictionaryValueId: 0,
Value: "string",
},
},
},
},
OfferId: "string",
},
},
},
`{
"task_id": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&UpdateCharacteristicsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Products().UpdateCharacteristics(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -2,6 +2,7 @@ package ozon
import ( import (
"net/http" "net/http"
"time"
core "github.com/diphantxm/ozon-api-client" core "github.com/diphantxm/ozon-api-client"
) )
@@ -14,59 +15,61 @@ type GetAvailablePromotionsResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result []struct { Result []GetAvailablePromotionsResult `json:"result"`
// Promotion identifier }
Id float64 `json:"id"`
// Promotion name type GetAvailablePromotionsResult struct {
Title string `json:"title"` // Promotion identifier
Id float64 `json:"id"`
// Promotion type // Promotion name
ActionType string `json:"action_type"` Title string `json:"title"`
// Promotion description // Promotion type
Description string `json:"description"` ActionType string `json:"action_type"`
// Promotion start date // Promotion description
DateStart string `json:"date_start"` Description string `json:"description"`
// Promotion end date // Promotion start date
DateEnd string `json:"date_end"` DateStart string `json:"date_start"`
// Promotion freeze date. // Promotion end date
// DateEnd string `json:"date_end"`
// 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"`
// Number of products that can participate in the promotion // Promotion freeze date.
PotentialProductsCount float64 `json:"potential_products_count"` //
// 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"`
// Number of products that participate in the promotion // Number of products that can participate in the promotion
ParticipatingProductsCount float64 `json:"participating_products_count"` PotentialProductsCount float64 `json:"potential_products_count"`
// Whether or not you participate in the promotion // Number of products that participate in the promotion
IsParticipating bool `json:"participating"` ParticipatingProductsCount float64 `json:"participating_products_count"`
// Indication that customers need a promo code to participate in the promotion // Whether or not you participate in the promotion
IsVoucherAction bool `json:"is_voucher_action"` IsParticipating bool `json:"participating"`
// Number of blocked products // Indication that customers need a promo code to participate in the promotion
BannedProductsCount float64 `json:"banned_products_count"` IsVoucherAction bool `json:"is_voucher_action"`
// Indication of the promotion is with the target audience // Number of blocked products
WithTargeting bool `json:"with_targeting"` BannedProductsCount float64 `json:"banned_products_count"`
// Order amount // Indication of the promotion is with the target audience
OrderAmount float64 `json:"order_amount"` WithTargeting bool `json:"with_targeting"`
// Discount type // Order amount
DiscountType string `json:"discount_type"` OrderAmount float64 `json:"order_amount"`
// Discount size // Discount type
DiscountValue float64 `json:"discount_value"` DiscountType string `json:"discount_type"`
} `json:"result"`
// Discount size
DiscountValue float64 `json:"discount_value"`
} }
// A method for getting a list of promotions that you can participate in // A method for getting a list of promotions that you can participate in
@@ -75,7 +78,7 @@ func (c Promotions) GetAvailablePromotions() (*GetAvailablePromotionsResponse, e
resp := &GetAvailablePromotionsResponse{} resp := &GetAvailablePromotionsResponse{}
response, err := c.client.Request(http.MethodGet, url, nil, resp) response, err := c.client.Request(http.MethodGet, url, nil, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -107,19 +110,23 @@ type AddProductToPromotionResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result AddProductToPromotionResult `json:"result"`
// List of product identifiers that were added to the promotion }
ProductIds []float64 `json:"product_ids"`
// List of products that weren't added to the promotion type AddProductToPromotionResult struct {
Rejected []struct { // List of product identifiers that were added to the promotion
// Product identifier ProductIds []float64 `json:"product_ids"`
ProductId float64 `json:"product_id"`
// Reason why the product wasn't added to the promotion // List of products that weren't added to the promotion
Reason string `json:"reason"` Rejected []AddProductToPromotionResultRejected `json:"rejected"`
} `json:"rejected"` }
} `json:"result"`
type AddProductToPromotionResultRejected struct {
// Product identifier
ProductId float64 `json:"product_id"`
// Reason why the product wasn't added to the promotion
Reason string `json:"reason"`
} }
// A method for adding products to an available promotion // A method for adding products to an available promotion
@@ -128,7 +135,571 @@ func (c Promotions) AddToPromotion(params *AddProductToPromotionParams) (*AddPro
resp := &AddProductToPromotionResponse{} resp := &AddProductToPromotionResponse{}
response, err := c.client.Request(http.MethodGet, url, params, resp) response, err := c.client.Request(http.MethodGet, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ProductsAvailableForPromotionParams struct {
// Promotion identifier
ActionId float64 `json:"action_id"`
// Number of values in the response. The 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"`
}
type ProductsAvailableForPromotionResponse struct {
core.CommonResponse
// Method result
Result ProductsAvailableForPromotionResult `json:"result"`
}
type ProductsAvailableForPromotionResult struct {
// Products list
Products []PromotionProduct `json:"products"`
// Total number of products that can participate in the promotion
Total float64 `json:"total"`
}
type PromotionProduct struct {
// Product identifier
Id float64 `json:"id"`
// Current product price without a discount
Price float64 `json:"price"`
// Promotional product price
ActionPrice float64 `json:"action_price"`
// Maximum possible promotional product price
MaxActionType float64 `json:"max_action_type"`
// Type of adding a product to the promotion: automatically or manually by the seller
AddMode string `json:"add_mode"`
// 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"`
}
// A method for getting a list of products that can participate in the promotion by the promotion identifier
func (c Promotions) ProductsAvailableForPromotion(params *ProductsAvailableForPromotionParams) (*ProductsAvailableForPromotionResponse, error) {
url := "/v1/actions/candidates"
resp := &ProductsAvailableForPromotionResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ProductsInPromotionParams struct {
// Promotion identifier
ActionId float64 `json:"action_id"`
// Number of values in the response. The 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"`
}
type ProductsInPromotionResponse struct {
core.CommonResponse
// Method result
Result ProductsInPromotionResult `json:"reuslt"`
}
type ProductsInPromotionResult struct {
// Products list
Products []PromotionProduct `json:"products"`
// Total number of products that can participate in the promotion
Total float64 `json:"total"`
}
// A method for getting the list of products participating in the promotion by its identifier
func (c Promotions) ProductsInPromotion(params *ProductsInPromotionParams) (*ProductsInPromotionResponse, error) {
url := "/v1/actions/products"
resp := &ProductsInPromotionResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type RemoveProductFromPromotionParams struct {
// Promotion identifier
ActionId float64 `json:"action_id"`
// List of products identifiers
ProductIds []float64 `json:"product_ids"`
}
type RemoveProductFromPromotionResponse struct {
core.CommonResponse
// Method result
Result RemoveProductFromPromotionResult `json:"result"`
}
type RemoveProductFromPromotionResult struct {
// List of product identifiers that were removed from the promotion
ProductIds []float64 `json:"product_ids"`
// List of product identifiers that weren't removed from the promotion
Rejected []RemoveProductFromPromotionResultRejected `json:"rejected"`
}
type RemoveProductFromPromotionResultRejected struct {
// Product identifier
ProductId float64 `json:"product_id"`
// Reason why the product wasn't removed from the promotion
Reason string `json:"reason"`
}
// A method for removing products from the promotion
func (c Promotions) RemoveProduct(params *RemoveProductFromPromotionParams) (*RemoveProductFromPromotionResponse, error) {
url := "/v1/actions/products/deactivate"
resp := &RemoveProductFromPromotionResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
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() (*ListHotSalePromotionsResponse, error) {
url := "/v1/actions/hotsales/list"
resp := &ListHotSalePromotionsResponse{}
response, err := c.client.Request(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"`
}
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_type"`
// 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(params *ProductsAvailableForHotSalePromotionParams) (*ProductsAvailableForHotSalePromotionResponse, error) {
url := "/v1/actions/hotsales/products"
resp := &ProductsAvailableForHotSalePromotionResponse{}
response, err := c.client.Request(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(params *AddProductsToHotSaleParams) (*ProductsToHotSaleResponse, error) {
url := "/v1/actions/hotsales/activate"
resp := &ProductsToHotSaleResponse{}
response, err := c.client.Request(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(params *RemoveProductsToHotSaleParams) (*ProductsToHotSaleResponse, error) {
url := "/v1/actions/hotsales/activate"
resp := &ProductsToHotSaleResponse{}
response, err := c.client.Request(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"`
// Page number from which you want to download the list of discount requests
Page uint64 `json:"page"`
// The maximum number of requests on a page
Limit uint64 `json:"limit"`
}
type ListDiscountRequestsResponse struct {
core.CommonResponse
// List of requests
Result []ListDiscountRequestsResult `json:"result"`
}
type ListDiscountRequestsResult struct {
// Request ID
Id uint64 `json:"id"`
// Request created date
CreatedAt time.Time `json:"created_at"`
// End time of the request
EndAt time.Time `json:"end_at"`
// Time to change the decision
EditedTill time.Time `json:"edited_till"`
// Request status
Status string `json:"status"`
// Customer's name
CustomerName string `json:"customer_name"`
// Product identifier in the Ozon system, SKU
SKU uint64 `json:"sku"`
// Customer's comment on the request
UserComment string `json:"user_comment"`
// Seller's comment on the request
SellerComment string `json:"seller_comment"`
// Requested price
RequestedPrice float64 `json:"requested_price"`
// Approved price
ApprovedPrice float64 `json:"approved_price"`
// Product price before all discounts
OriginalPrice float64 `json:"original_price"`
// Discount in rubles
Discount float64 `json:"discount"`
// Discount percentage
DiscountPercent float64 `json:"discount_percent"`
// Base price at which a product is selling on Ozon, if not eligible for a promotion
BasePrice float64 `json:"base_price"`
// The minimum price after auto-application of discounts and promotions
MinAutoPrice float64 `json:"min_auto_price"`
// ID of the previous customer request for this product
PrevTaskId uint64 `json:"prev_task_id"`
// If product is damaged — true
IsDamaged bool `json:"is_damaged"`
// Moderation date: review, approval or decline of the request
ModeratedAt time.Time `json:"moderated_at"`
// Discount in rubles approved by the seller. Pass the value 0 if the seller did not approve the request
ApprovedDiscount float64 `json:"approved_discount"`
// Discount percentage approved by the seller. Pass the value 0 if the seller did not approve the request
ApprovedDiscountPercent float64 `json:"approved_discount_percent"`
// Whether the customer has purchased the product. true if purchased
IsPurchased bool `json:"is_purchased"`
// Whether the request was moderated automatically. true if moderation was automatic
IsAutoModerated bool `json:"is_auto_moderated"`
// Product identifier in the seller's system
OfferId string `json:"offer_id"`
// Email of the user who processed the request
Email string `json:"email"`
// Last name of the user who processed the request
LastName string `json:"last_name"`
// First name of the user who processed the request
FirstName string `json:"first_name"`
// Patronymic of the user who processed the request
Patronymic string `json:"patronymic"`
// Approved minimum quantity of products
ApprovedQuantityMin uint64 `json:"approved_quantity_min"`
// Approved maximum quantity of products
ApprovedQuantityMax uint64 `json:"approved_quantity_max"`
// Requested minimum number of products
RequestedQuantityMin uint64 `json:"requested_quantity_min"`
// Requested maximum number of products
RequestedQuantityMax uint64 `json:"requested_quantity_max"`
// Requested price with fee
RequestedPriceWithFee float64 `json:"requested_price_with_fee"`
// Approved price with fee
ApprovedPriceWithFee float64 `json:"approved_price_with_fee"`
// Approved price fee percent
ApprovedPriceFeePercent float64 `json:"approved_price_fee_percent"`
}
// Method for getting a list of products that customers want to buy with discount
func (c Promotions) ListDiscountRequests(params *ListDiscountRequestsParams) (*ListDiscountRequestsResponse, error) {
url := "/v1/actions/discounts-task/list"
resp := &ListDiscountRequestsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type DiscountRequestParams struct {
// List of discount requests
Tasks []DiscountRequestTask `json:"tasks"`
}
type DiscountRequestTask struct {
// Request ID
Id uint64 `json:"id"`
// Approved price
ApprovedPrice float64 `json:"approved_price"`
// Seller's comment on the request
SellerComment string `json:"seller_comment"`
// Approved minimum quantity of products
ApprovedQuantityMin uint64 `json:"approved_quantity_min"`
// Approved maximum quantity of products
ApprovedQuantityMax uint64 `json:"approved_quantity_max"`
}
type DiscountRequestResponse struct {
core.CommonResponse
// Method result
Result DiscountRequestResult `json:"result"`
}
type DiscountRequestResult struct {
// Errors when creating a request
FailDetails []DiscountRequestResultFailDetail `json:"fail_details"`
// The number of requests with a successful status change
SuccessCount int32 `json:"success_count"`
// The number of requests that failed to change their status
FailCount int32 `json:"fail_count"`
}
type DiscountRequestResultFailDetail struct {
// Request ID
TaskId uint64 `json:"task_id"`
// Error message
ErrorForUser string `json:"error_for_user"`
}
// You can approve applications in statuses:
// - NEW — new
// - SEEN — viewed
func (c Promotions) ApproveDiscountRequest(params *DiscountRequestParams) (*DiscountRequestResponse, error) {
url := "/v1/actions/discounts-task/approve"
resp := &DiscountRequestResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
// You can decline applications in statuses:
// - NEW—new
// - SEEN—viewed
func (c Promotions) DeclineDiscountRequest(params *DiscountRequestParams) (*DiscountRequestResponse, error) {
url := "/v1/actions/discounts-task/decline"
resp := &DiscountRequestResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -141,3 +141,632 @@ func TestAddToPromotion(t *testing.T) {
} }
} }
} }
func TestProductsAvailableForPromotion(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ProductsAvailableForPromotionParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ProductsAvailableForPromotionParams{
ActionId: 63337,
Limit: 10,
Offset: 0,
},
`{
"result": {
"products": [
{
"id": 226,
"price": 250,
"action_price": 0,
"max_action_price": 175,
"add_mode": "NOT_SET",
"stock": 0,
"min_stock": 0
},
{
"id": 1366,
"price": 2300,
"action_price": 630,
"max_action_price": 770,
"add_mode": "MANUAL",
"stock": 0,
"min_stock": 0
}
],
"total": 2
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ProductsAvailableForPromotionParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Promotions().ProductsAvailableForPromotion(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestProductsInPromotion(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ProductsInPromotionParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ProductsInPromotionParams{
ActionId: 66011,
Limit: 10,
Offset: 0,
},
`{
"result": {
"products": [
{
"id": 1383,
"price": 5503,
"action_price": 621,
"max_action_price": 3712.1,
"add_mode": "MANUAL",
"stock": 0,
"min_stock": 0
}
],
"total": 1
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ProductsInPromotionParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Promotions().ProductsInPromotion(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestRemoveProduct(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *RemoveProductFromPromotionParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&RemoveProductFromPromotionParams{
ActionId: 66011,
ProductIds: []float64{14975},
},
`{
"result": {
"product_ids": [
14975
],
"rejected": []
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&RemoveProductFromPromotionParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Promotions().RemoveProduct(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result.ProductIds) > 0 {
if resp.Result.ProductIds[0] != test.params.ProductIds[0] {
t.Errorf("Product ids in request and response are not equal")
}
}
}
}
}
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))
resp, err := c.Promotions().ListHotSalePromotions()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func 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))
resp, err := c.Promotions().ProductsAvailableForHotSalePromotion(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func 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))
resp, err := c.Promotions().AddProductsToHotSale(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func 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))
resp, err := c.Promotions().RemoveProductsToHotSale(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestListDiscountRequests(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListDiscountRequestsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListDiscountRequestsParams{
Status: "UNKNOWN",
Page: 0,
Limit: 100,
},
`{
"result": [
{
"id": 0,
"created_at": "2019-08-24T14:15:22Z",
"end_at": "2019-08-24T14:15:22Z",
"edited_till": "2019-08-24T14:15:22Z",
"status": "string",
"customer_name": "string",
"sku": 0,
"user_comment": "string",
"seller_comment": "string",
"requested_price": 0,
"approved_price": 0,
"original_price": 0,
"discount": 0,
"discount_percent": 0,
"base_price": 0,
"min_auto_price": 0,
"prev_task_id": 0,
"is_damaged": true,
"moderated_at": "2019-08-24T14:15:22Z",
"approved_discount": 0,
"approved_discount_percent": 0,
"is_purchased": true,
"is_auto_moderated": true,
"offer_id": "string",
"email": "string",
"last_name": "string",
"first_name": "string",
"patronymic": "string",
"approved_quantity_min": 0,
"approved_quantity_max": 0,
"requested_quantity_min": 0,
"requested_quantity_max": 0,
"requested_price_with_fee": 0,
"approved_price_with_fee": 0,
"approved_price_fee_percent": 0
}
]
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListDiscountRequestsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Promotions().ListDiscountRequests(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestApproveDiscountRequest(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *DiscountRequestParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&DiscountRequestParams{
Tasks: []DiscountRequestTask{
{
Id: 123,
ApprovedPrice: 11,
SellerComment: "string",
ApprovedQuantityMin: 1,
ApprovedQuantityMax: 2,
},
},
},
`{
"result": {
"fail_details": [
{
"task_id": 1234,
"error_for_user": "string"
}
],
"success_count": 1,
"fail_count": 1
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&DiscountRequestParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Promotions().ApproveDiscountRequest(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestDeclineDiscountRequest(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *DiscountRequestParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&DiscountRequestParams{
Tasks: []DiscountRequestTask{
{
Id: 123,
ApprovedPrice: 11,
SellerComment: "string",
ApprovedQuantityMin: 1,
ApprovedQuantityMax: 2,
},
},
},
`{
"result": {
"fail_details": [
{
"task_id": 1234,
"error_for_user": "string"
}
],
"success_count": 1,
"fail_count": 1
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&DiscountRequestParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Promotions().DeclineDiscountRequest(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -15,73 +15,79 @@ type GetCurrentSellerRatingInfoResponse struct {
core.CommonResponse core.CommonResponse
// Rating groups list // Rating groups list
Groups []struct { Groups []GetCurrentSellerRatingInfoGroup `json:"groups"`
// Ratings group name }
GroupName string `json:"group_name"`
// Ratings list type GetCurrentSellerRatingInfoGroup struct {
Items []struct { // Ratings group name
GroupName string `json:"group_name"`
// Rating change: the ratio of the previous value to the current one // Ratings list
Change struct { Items []GetCurrentSellerRatingInfoGroupItem `json:"items"`
// How the rating value has changed:
// - DIRECTION_UNKNOWN — unknown.
// - DIRECTION_NONE — has not changed.
// - DIRECTION_RISE — has increased.
// - DIRECTION_FALL — has dropped.
Direction string `json:"direction"`
// What the change means: // An indication that the penalty points balance is exceeded
// - MEANING_UNKNOWN — unknown. PenaltyScoreExceeded bool `json:"penalty_score_exceeded"`
// - MEANING_NONE — neutral.
// - MEANING_GOOD — the indicator is improving, everything is good.
// - MEANING_BAD — the indicator is dropping, you should do something.
Meaning string `json:"meaning"`
} `json:"change"`
// Current rating value // An indication that you participate in the Premium program
CurrentValue float64 `json:"current_value"` Premium bool `json:"premium"`
}
// Rating name type GetCurrentSellerRatingInfoGroupItem struct {
Name string `json:"name"`
// Previous rating value // Rating change: the ratio of the previous value to the current one
PastValue float64 `json:"past_value"` Change GetCurrentSellerRatingInfoGroupItemChange `json:"change"`
// System rating name // Current rating value
Rating string `json:"rating"` CurrentValue float64 `json:"current_value"`
// What should be the rating value to be considered good: // Rating name
// - UNKNOWN_DIRECTION — unknown. Name string `json:"name"`
// - NEUTRAL — doesn't matter.
// - HIGHER_IS_BETTER — the higher the better.
// - LOWER_IS_BETTER — the lower the better.
RatingDirection string `json:"rating_direction"`
// Rating status: // Previous rating value
// - UNKNOWN_STATUS — unknown status. PastValue float64 `json:"past_value"`
// - OK — everything is OK.
// - WARNING — indicators require attention.
// - CRITICAL — critical rating
Status string `json:"status"`
// Value type: // System rating name
// - UNKNOWN_VALUE — unknown, Rating string `json:"rating"`
// - INDEX,
// - PERCENT,
// - TIME,
// - RATIO — coefficient,
// - REVIEW_SCORE — score,
// - COUNT
ValueType string `json:"value_type"`
} `json:"items"`
// An indication that the penalty points balance is exceeded // What should be the rating value to be considered good:
PenaltyScoreExceeded bool `json:"penalty_score_exceeded"` // - UNKNOWN_DIRECTION — unknown.
// - NEUTRAL — doesn't matter.
// - HIGHER_IS_BETTER — the higher the better.
// - LOWER_IS_BETTER — the lower the better.
RatingDirection string `json:"rating_direction"`
// An indication that you participate in the Premium program // Rating status:
Premium bool `json:"premium"` // - UNKNOWN_STATUS — unknown status.
} `json:"groups"` // - OK — everything is OK.
// - WARNING — indicators require attention.
// - CRITICAL — critical rating
Status string `json:"status"`
// Value type:
// - UNKNOWN_VALUE — unknown,
// - INDEX,
// - PERCENT,
// - TIME,
// - RATIO — coefficient,
// - REVIEW_SCORE — score,
// - COUNT
ValueType string `json:"value_type"`
}
type GetCurrentSellerRatingInfoGroupItemChange struct {
// How the rating value has changed:
// - DIRECTION_UNKNOWN — unknown.
// - DIRECTION_NONE — has not changed.
// - DIRECTION_RISE — has increased.
// - DIRECTION_FALL — has dropped.
Direction string `json:"direction"`
// What the change means:
// - MEANING_UNKNOWN — unknown.
// - MEANING_NONE — neutral.
// - MEANING_GOOD — the indicator is improving, everything is good.
// - MEANING_BAD — the indicator is dropping, you should do something.
Meaning string `json:"meaning"`
} }
func (c Rating) GetCurrentSellerRatingInfo() (*GetCurrentSellerRatingInfoResponse, error) { func (c Rating) GetCurrentSellerRatingInfo() (*GetCurrentSellerRatingInfoResponse, error) {
@@ -89,7 +95,7 @@ func (c Rating) GetCurrentSellerRatingInfo() (*GetCurrentSellerRatingInfoRespons
resp := &GetCurrentSellerRatingInfoResponse{} resp := &GetCurrentSellerRatingInfoResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp) response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -116,61 +122,71 @@ type GetSellerRatingInfoPeriodResponse struct {
core.CommonResponse core.CommonResponse
// Information on the Premium program penalty points // Information on the Premium program penalty points
PremiumScores []struct { PremiumScores []GetSellerRatingInfoPeriodPremiumScores `json:"premium_scores"`
// Rating name
Rating string `json:"rating"`
// Information on penalty points
Scores []struct {
// Date when the penalty points were received
Date time.Time `json:"date"`
// Rating value for which the penalty points were received
RatingValue float64 `json:"rating_value"`
// Number of received penalty points
Value int32 `json:"value"`
} `json:"scores"`
} `json:"premium_scores"`
// Information on the seller ratings // Information on the seller ratings
Ratings []struct { Ratings []GetSellerRatingInfoPeriodRating `json:"ratings"`
// Rating threshold, after which sales will be blocked }
DangerThreshold float64 `json:"danger_threshold"`
// Rating threshold for participation in the Premium program type GetSellerRatingInfoPeriodPremiumScores struct {
PremiumThreshold float64 `json:"premium_threshold"` // Rating name
Rating string `json:"rating"`
// Rating system name // Information on penalty points
Rating string `json:"rating"` Scores []GetSellerRatingInfoPeriodPremiumScore `json:"scores"`
}
// Rating values list type GetSellerRatingInfoPeriodPremiumScore struct {
Values []struct { // Date when the penalty points were received
// Rating calculation start date Date time.Time `json:"date"`
DateFrom time.Time `json:"date_from"`
// Rating calculation end date // Rating value for which the penalty points were received
DateTo time.Time `json:"date_to"` RatingValue float64 `json:"rating_value"`
// Rating status // Number of received penalty points
Status struct { Value int32 `json:"value"`
// Indication if the rating threshold for blocking is exceeded }
Danger bool `json:"danger"`
// Indication whether the threshold for participation in the Premium program has been reached type GetSellerRatingInfoPeriodRating struct {
Premium bool `json:"premium"` // Rating threshold, after which sales will be blocked
DangerThreshold float64 `json:"danger_threshold"`
// Indication of a warning that the threshold for blocking may be exceeded // Rating threshold for participation in the Premium program
Warning bool `json:"warning"` PremiumThreshold float64 `json:"premium_threshold"`
} `json:"status"`
// Rating value // Rating system name
Value float64 `json:"value"` Rating string `json:"rating"`
} `json:"values"`
// Rating threshold, after which a warning about possible blocking appears // Rating values list
WarningThreshold float64 `json:"warning_threshold"` Values []GetSellerRatingInfoPeriodRatingValue `json:"values"`
} `json:"ratings"`
// Rating threshold, after which a warning about possible blocking appears
WarningThreshold float64 `json:"warning_threshold"`
}
type GetSellerRatingInfoPeriodRatingValue struct {
// Rating calculation start date
DateFrom time.Time `json:"date_from"`
// Rating calculation end date
DateTo time.Time `json:"date_to"`
// Rating status
Status GetSellerRatingInfoPeriodRatingValueStatus `json:"status"`
// Rating value
Value float64 `json:"value"`
}
type GetSellerRatingInfoPeriodRatingValueStatus struct {
// Indication if the rating threshold for blocking is exceeded
Danger bool `json:"danger"`
// Indication whether the threshold for participation in the Premium program has been reached
Premium bool `json:"premium"`
// Indication of a warning that the threshold for blocking may be exceeded
Warning bool `json:"warning"`
} }
func (c Rating) GetSellerRatingInfoForPeriod(params *GetSellerRatingInfoForPeriodParams) (*GetSellerRatingInfoPeriodResponse, error) { func (c Rating) GetSellerRatingInfoForPeriod(params *GetSellerRatingInfoForPeriodParams) (*GetSellerRatingInfoPeriodResponse, error) {
@@ -178,7 +194,7 @@ func (c Rating) GetSellerRatingInfoForPeriod(params *GetSellerRatingInfoForPerio
resp := &GetSellerRatingInfoPeriodResponse{} resp := &GetSellerRatingInfoPeriodResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -38,45 +38,48 @@ type GetReportsListResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetReportsListResult `json:"result"`
// Array with generated reports }
Reports []struct {
// Unique report identifier
Code string `json:"code"`
// Report creation date type GetReportsListResult struct {
CreatedAt time.Time `json:"created_at"` // Array with generated reports
Reports []GetReportsListResultReport `json:"reports"`
// Error code when generating the report // Total number of reports
Error string `json:"error"` Total int32 `json:"total"`
}
// Link to CSV file type GetReportsListResultReport struct {
File string `json:"file"` // Unique report identifier
Code string `json:"code"`
// Array with the filters specified when the seller created the report // Report creation date
Params struct { CreatedAt time.Time `json:"created_at"`
} `json:"params"`
// Report type: // Error code when generating the report
// - SELLER_PRODUCTS — products report, Error string `json:"error"`
// - SELLER_TRANSACTIONS — transactions report,
// - SELLER_PRODUCT_PRICES — product prices report,
// - SELLER_STOCK — stocks report,
// - SELLER_PRODUCT_MOVEMENT — products movement report,
// - SELLER_RETURNS — returns report,
// - SELLER_POSTINGS — shipments report,
// - SELLER_FINANCE — financial report
ReportType string `json:"report_type"`
// Report generation status // Link to CSV file
// - `success` File string `json:"file"`
// - `failed`
Status string `json:"status"`
} `json:"reports"`
// Total number of reports // Array with the filters specified when the seller created the report
Total int32 `json:"total"` Params map[string]string `json:"params"`
} `json:"result"`
// Report type:
// - SELLER_PRODUCTS — products report,
// - SELLER_TRANSACTIONS — transactions report,
// - SELLER_PRODUCT_PRICES — product prices report,
// - SELLER_STOCK — stocks report,
// - SELLER_PRODUCT_MOVEMENT — products movement report,
// - SELLER_RETURNS — returns report,
// - SELLER_POSTINGS — shipments report,
// - SELLER_FINANCE — financial report
ReportType string `json:"report_type"`
// Report generation status
// - `success`
// - `failed`
Status string `json:"status"`
} }
// Returns the list of reports that have been generated before // Returns the list of reports that have been generated before
@@ -85,7 +88,7 @@ func (c Reports) GetList(params *GetReportsListParams) (*GetReportsListResponse,
resp := &GetReportsListResponse{} resp := &GetReportsListResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -103,38 +106,40 @@ type GetReportDetailsResponse struct {
core.CommonResponse core.CommonResponse
// Report details // Report details
Result struct { Result GetReportDetailResult `json:"result"`
// Unique report identifier }
Code string `json:"code"`
// Report creation date type GetReportDetailResult struct {
CreatedAt time.Time `json:"created_at"` // Unique report identifier
Code string `json:"code"`
// Error code when generating the report // Report creation date
Error string `json:"error"` CreatedAt time.Time `json:"created_at"`
// Link to CSV file // Error code when generating the report
File string `json:"file"` Error string `json:"error"`
// Array with the filters specified when the seller created the report // Link to CSV file
Params map[string]string `json:"params"` File string `json:"file"`
// Report type: // Array with the filters specified when the seller created the report
// - SELLER_PRODUCTS — products report, Params map[string]string `json:"params"`
// - SELLER_TRANSACTIONS — transactions report,
// - SELLER_PRODUCT_PRICES — product prices report,
// - SELLER_STOCK — stocks report,
// - SELLER_PRODUCT_MOVEMENT — products movement report,
// - SELLER_RETURNS — returns report,
// - SELLER_POSTINGS — shipments report,
// - SELLER_FINANCE — financial report
ReportType string `json:"report_type"`
// Report generation status: // Report type:
// - success // - SELLER_PRODUCTS — products report,
// - failed // - SELLER_TRANSACTIONS — transactions report,
Status string `json:"status"` // - SELLER_PRODUCT_PRICES — product prices report,
} `json:"result"` // - SELLER_STOCK — stocks report,
// - SELLER_PRODUCT_MOVEMENT — products movement report,
// - SELLER_RETURNS — returns report,
// - SELLER_POSTINGS — shipments report,
// - SELLER_FINANCE — financial report
ReportType string `json:"report_type"`
// Report generation status:
// - success
// - failed
Status string `json:"status"`
} }
// Returns information about a created report by its identifier // Returns information about a created report by its identifier
@@ -143,7 +148,7 @@ func (c Reports) GetReportDetails(params *GetReportDetailsParams) (*GetReportDet
resp := &GetReportDetailsResponse{} resp := &GetReportDetailsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -159,6 +164,9 @@ type GetFinancialReportParams struct {
// Number of the page returned in the request // Number of the page returned in the request
Page int64 `json:"page"` Page int64 `json:"page"`
// true, если нужно добавить дополнительные параметры в ответ
WithDetails bool `json:"with_details"`
// Number of items on the page // Number of items on the page
PageSize int64 `json:"page_size"` PageSize int64 `json:"page_size"`
} }
@@ -175,43 +183,212 @@ type GetFinancialReportResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetFinancialResultResult `json:"result"`
// Reports list }
CashFlows []struct {
// Period data
Period struct {
// Period identifier
Id int64 `json:"id"`
// Period start type GetFinancialResultResult struct {
Begin time.Time `json:"begin"` // Reports list
CashFlows []GetFinancialResultResultCashflow `json:"cash_flows"`
// Period end // Detailed info
End time.Time `json:"end"` Details GetFinancialResultResultDetail `json:"details"`
} `json:"period"`
// Sum of sold products prices // Number of pages with reports
OrdersAmount float64 `json:"order_amount"` PageCount int64 `json:"page_count"`
}
// Sum of returned products prices type GetFinancialResultResultCashflow struct {
ReturnsAmount float64 `json:"returns_amount"` // Period data
Period GetFinancialResultResultCashflowPeriod `json:"period"`
// Ozon sales commission // Sum of sold products prices
CommissionAmount float64 `json:"commission_amount"` OrdersAmount float64 `json:"order_amount"`
// Additional services cost // Sum of returned products prices
ServicesAmount float64 `json:"services_amount"` ReturnsAmount float64 `json:"returns_amount"`
// Logistic services cost // Ozon sales commission
ItemDeliveryAndReturnAmount float64 `json:"item_delivery_and_return_amount"` CommissionAmount float64 `json:"commission_amount"`
// Code of the currency used to calculate the commissions // Additional services cost
CurrencyCode string `json:"currency_code"` ServicesAmount float64 `json:"services_amount"`
} `json:"cash_flows"`
// Number of pages with reports // Logistic services cost
PageCount int64 `json:"page_count"` ItemDeliveryAndReturnAmount float64 `json:"item_delivery_and_return_amount"`
} `json:"result"`
// Code of the currency used to calculate the commissions
CurrencyCode string `json:"currency_code"`
}
type GetFinancialResultResultCashflowPeriod struct {
// Period identifier
Id int64 `json:"id"`
// Period start
Begin time.Time `json:"begin"`
// Period end
End time.Time `json:"end"`
}
type GetFinancialResultResultDetail struct {
// Balance on the beginning of period
BeginBalanceAmount float64 `json:"begin_balance_amount"`
// Orders
Delivery GetFinancialResultResultDetailDelivery `json:"delivery"`
// Amount to be paid for the period
InvoiceTransfer float64 `json:"invoice_transfer"`
// Transfer under loan agreements
Loan float64 `json:"loan"`
// Paid for the period
Payments []GetFinancialResultResultDetailPayment `json:"payments"`
// Period data
Period GetFinancialResultResultDetailPeriod `json:"period"`
// Returns and cancellations
Return GetFinancialResultResultDetailReturn `json:"return"`
// rFBS transfers
RFBS GetFinancialResultResultDetailRFBS `json:"rfbs"`
// Services
Services GetFinancialResultResultDetailService `json:"services"`
// Compensation and other accruals
Others GetFinancialResultResultDetailOthers `json:"others"`
// Balance at the end of the period
EndBalanceAmount float64 `json:"end_balance_amount"`
}
type GetFinancialResultResultDetailDelivery struct {
// Total amount
Total float64 `json:"total"`
// Amount for which products were purchased, including commission fees
Amount float64 `json:"amount"`
// Processing and delivery fees
DeliveryServices GetFinancialResultResultDetailDeliveryServices `json:"delivery_services"`
}
type GetFinancialResultResultDetailDeliveryServices struct {
// Total amount
Total float64 `json:"total"`
// Details
Items []GetFinancialResultResultDetailDeliveryServicesItem `json:"items"`
}
type GetFinancialResultResultDetailDeliveryServicesItem struct {
// Operation name
Name DetailsDeliveryItemName `json:"name"`
// Amount by operation
Price float64 `json:"price"`
}
type GetFinancialResultResultDetailPayment struct {
// Currency
CurrencyCode string `json:"currency_code"`
// Payment amount
Payment float64 `json:"payment"`
}
type GetFinancialResultResultDetailPeriod struct {
// Period start
Begin time.Time `json:"begin"`
// Period end
End time.Time `json:"end"`
// Period identifier
Id int64 `json:"id"`
}
type GetFinancialResultResultDetailReturn struct {
// Total amount
Total float64 `json:"total"`
// Amount of returns received, including commission fees
Amount float64 `json:"amount"`
// Returns and cancellation fees
ReturnServices GetFinancialResultResultDetailReturnServices `json:"return_services"`
}
type GetFinancialResultResultDetailReturnServices struct {
// Total amount
Total float64 `json:"total"`
// Details
Items []GetFinancialResultResultDetailReturnServicesItem `json:"items"`
}
type GetFinancialResultResultDetailReturnServicesItem struct {
// Operation name
Name DetailsReturnServiceName `json:"name"`
// Amount by operation
Price float64 `json:"price"`
}
type GetFinancialResultResultDetailRFBS struct {
// Total amount
Total float64 `json:"total"`
// Transfers from customers
TransferDelivery float64 `json:"transfer_delivery"`
// Return of transfers to customers
TransferDeliveryReturn float64 `json:"transfer_delivery_return"`
// Compensation of delivery fees
CompensationDeliveryReturn float64 `json:"compensation_delivery_return"`
// Transfers of partial refunds to customers
PartialCompensation float64 `json:"partial_compensation"`
// Compensation of partial refunds
PartialCompensationReturn float64 `json:"partial_compensation_return"`
}
type GetFinancialResultResultDetailService struct {
// Total amount
Total float64 `json:"total"`
// Details
Items []GetFinancialResultResultDetailServiceItem `json:"items"`
}
type GetFinancialResultResultDetailServiceItem struct {
// Operation name
Name DetailsServiceItemName `json:"name"`
// Amount by operation
Price float64 `json:"price"`
}
type GetFinancialResultResultDetailOthers struct {
// Total amount
Total float64 `json:"total"`
// Details
Items []GetFinancialResultResultDetailOthersItem `json:"items"`
}
type GetFinancialResultResultDetailOthersItem struct {
// Operation name
Name DetailsOtherItemName `json:"name"`
// Amount by operation
Price float64 `json:"price"`
} }
// Returns information about a created report by its identifier // Returns information about a created report by its identifier
@@ -220,7 +397,7 @@ func (c Reports) GetFinancial(params *GetFinancialReportParams) (*GetFinancialRe
resp := &GetFinancialReportResponse{} resp := &GetFinancialReportResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -254,10 +431,12 @@ type GetProductsReportResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetProductsReportResult `json:"result"`
// Unique report identifier }
Code string `json:"code"`
} `json:"result"` type GetProductsReportResult struct {
// Unique report identifier
Code string `json:"code"`
} }
// Method for getting a report with products data. For example, Ozon ID, number of products, prices, status // Method for getting a report with products data. For example, Ozon ID, number of products, prices, status
@@ -266,7 +445,7 @@ func (c Reports) GetProducts(params *GetProductsReportParams) (*GetProductsRepor
resp := &GetProductsReportResponse{} resp := &GetProductsReportResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -287,10 +466,12 @@ type GetStocksReportResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetStocksReportResult `json:"result"`
// Unique report identifier }
Code string `json:"code"`
} `json:"result"` type GetStocksReportResult struct {
// Unique report identifier
Code string `json:"code"`
} }
// Report with information about the number of available and reserved products in stock // Report with information about the number of available and reserved products in stock
@@ -299,7 +480,7 @@ func (c Reports) GetStocks(params *GetStocksReportParams) (*GetStocksReportRespo
resp := &GetStocksReportResponse{} resp := &GetStocksReportResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -326,10 +507,12 @@ type GetProductsMovementReportResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetProductsMovementReportResult `json:"result"`
// Unique report identifier }
Code string `json:"code"`
} `json:"result"` type GetProductsMovementReportResult struct {
// Unique report identifier
Code string `json:"code"`
} }
// Report with complete information on products, as well as the number of products with statuses: // Report with complete information on products, as well as the number of products with statuses:
@@ -342,7 +525,7 @@ func (c Reports) GetProductsMovement(params *GetProductsMovementReportParams) (*
resp := &GetProductsMovementReportResponse{} resp := &GetProductsMovementReportResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -377,10 +560,12 @@ type GetReturnsReportResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetReturnReportResult `json:"result"`
// Unique report identifier }
Code string `json:"code"`
} `json:"result"` type GetReturnReportResult struct {
// Unique report identifier
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 report contains information about returned products that were accepted from the customer, ready for pickup, or delivered to the seller.
@@ -391,7 +576,7 @@ func (c Reports) GetReturns(params *GetReturnsReportParams) (*GetReturnsReportRe
resp := &GetReturnsReportResponse{} resp := &GetReturnsReportResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -446,10 +631,12 @@ type GetShipmentReportResponse struct {
core.CommonResponse core.CommonResponse
// Method result // Method result
Result struct { Result GetShipmentReportResult `json:"result"`
// Unique report identifier }
Code string `json:"code"`
} `json:"result"` type GetShipmentReportResult struct {
// Unique report identifier
Code string `json:"code"`
} }
// Shipment report with orders details: // Shipment report with orders details:
@@ -464,7 +651,92 @@ func (c Reports) GetShipment(params *GetShipmentReportParams) (*GetShipmentRepor
resp := &GetShipmentReportResponse{} resp := &GetShipmentReportResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type IssueOnDiscountedProductsResponse struct {
core.CommonResponse
// Unique report identifier
Code string `json:"code"`
}
// Generates a report on discounted products in Ozon warehouses.
// For example, Ozon can discount a product due to damage when delivering.
//
// Returns report identifier. To get the report, send the identifier in the request body of a method `/v1/report/discounted/info`
func (c Reports) IssueOnDiscountedProducts() (*IssueOnDiscountedProductsResponse, error) {
url := "/v1/report/discounted/create"
resp := &IssueOnDiscountedProductsResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ReportOnDiscountedProductsParams struct {
// Unique report identifier
Code string `json:"code"`
}
type ReportOnDiscountedProductsResponse struct {
core.CommonResponse
// Report information
Report ReportonDiscountedProductsReport `json:"report"`
}
type ReportonDiscountedProductsReport struct {
// Report creation date
CreatedAt time.Time `json:"created_at"`
// Link to report file
File string `json:"file"`
// Report status:
// - success — created
// - pending — waiting to be processed
// - processing — processed
// - failed — generation error
Status string `json:"status"`
// Report generation error code
Error string `json:"error"`
}
// By report identifier, returns information about the report generated earlier
func (c Reports) ReportOnDiscountedProducts(params *ReportOnDiscountedProductsParams) (*ReportOnDiscountedProductsResponse, error) {
url := "/v1/report/discounted/info"
resp := &ReportOnDiscountedProductsResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
// By report identifier, returns information about the report generated earlier
func (c Reports) ListReportsOnDiscountedProducts() (*ReportOnDiscountedProductsResponse, error) {
url := "/v1/report/discounted/list"
resp := &ReportOnDiscountedProductsResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -165,28 +165,98 @@ func TestGetFinancialReport(t *testing.T) {
From: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-01-01T00:00:00.000Z"), From: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-01-01T00:00:00.000Z"),
To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-12-31T00:00:00.000Z"), To: core.TimeFromString(t, "2006-01-02T15:04:05Z", "2022-12-31T00:00:00.000Z"),
}, },
Page: 1, WithDetails: true,
PageSize: 1, Page: 1,
PageSize: 1,
}, },
`{ `{
"result": { "result": {
"cash_flows": [ "cash_flows": [
{ {
"period": {
"id": 11567022278500,
"begin": "2022-08-01T00:00:00Z",
"end": "2022-08-15T00:00:00Z"
},
"orders_amount": 1000,
"returns_amount": -3000,
"commission_amount": 1437, "commission_amount": 1437,
"services_amount": 8471.28, "currency_code": "string",
"item_delivery_and_return_amount": 1991, "item_delivery_and_return_amount": 1991,
"currency_code": "RUB" "orders_amount": 1000,
"period": {
"begin": "2023-04-03T09:12:10.239Z",
"end": "2023-04-03T09:12:10.239Z",
"id": 11567022278500
},
"returns_amount": -3000,
"services_amount": 8471.28
} }
], ],
"page_count": 15 "details": {
} "period": {
"begin": "2023-04-03T09:12:10.239Z",
"end": "2023-04-03T09:12:10.239Z",
"id": 11567022278500
},
"payments": [
{
"payment": 0,
"currency_code": "string"
}
],
"begin_balance_amount": 0,
"delivery": {
"total": 0,
"amount": 0,
"delivery_services": {
"total": 0,
"items": [
{
"name": "string",
"price": 0
}
]
}
},
"return": {
"total": 0,
"amount": 0,
"return_services": {
"total": 0,
"items": [
{
"name": "string",
"price": 0
}
]
}
},
"loan": 0,
"invoice_transfer": 0,
"rfbs": {
"total": 0,
"transfer_delivery": 0,
"transfer_delivery_return": 0,
"compensation_delivery_return": 0,
"partial_compensation": 0,
"partial_compensation_return": 0
},
"services": {
"total": 0,
"items": [
{
"name": "string",
"price": 0
}
]
},
"others": {
"total": 0,
"items": [
{
"name": "string",
"price": 0
}
]
},
"end_balance_amount": 0
}
},
"page_count": 15
}`, }`,
}, },
// Test No Client-Id or Api-Key // Test No Client-Id or Api-Key
@@ -373,7 +443,7 @@ func TestGetProductsMovementReport(t *testing.T) {
if resp.StatusCode != test.statusCode { if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
} }
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
if resp.Result.Code == "" { if resp.Result.Code == "" {
t.Errorf("Code cannot be empty") t.Errorf("Code cannot be empty")
@@ -429,7 +499,7 @@ func TestGetReturnsReport(t *testing.T) {
if resp.StatusCode != test.statusCode { if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
} }
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
if resp.Result.Code == "" { if resp.Result.Code == "" {
t.Errorf("Code cannot be empty") t.Errorf("Code cannot be empty")
@@ -487,7 +557,7 @@ func TestGetShipmentReport(t *testing.T) {
if resp.StatusCode != test.statusCode { if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
} }
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
if resp.Result.Code == "" { if resp.Result.Code == "" {
t.Errorf("Code cannot be empty") t.Errorf("Code cannot be empty")
@@ -495,3 +565,149 @@ func TestGetShipmentReport(t *testing.T) {
} }
} }
} }
func TestIssueOnDiscountedProducts(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"},
`{
"code": "d55f4517-8347-4e24-9d93-d6e736c1c07c"
}`,
},
// 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))
resp, err := c.Reports().IssueOnDiscountedProducts()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if resp.Code == "" {
t.Errorf("Code cannot be empty")
}
}
}
}
func TestReportOnDiscountedProducts(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ReportOnDiscountedProductsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ReportOnDiscountedProductsParams{
Code: "d55f4517-8347-4e24-9d93-d6e736c1c07c",
},
`{
"report": {
"created_at": "2022-10-04T10:07:08.146Z",
"error": "string",
"file": "string",
"status": "string"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ReportOnDiscountedProductsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Reports().ReportOnDiscountedProducts(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestListReportsOnDiscountedProducts(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"},
`{
"reports": [
{
"created_at": "2022-10-04T10:07:08.146Z",
"error": "string",
"file": "string",
"status": "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))
resp, err := c.Reports().ListReportsOnDiscountedProducts()
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -28,7 +28,8 @@ type GetFBOReturnsFilter struct {
// Shipment number // Shipment number
PostingNumber string `json:"posting_number"` PostingNumber string `json:"posting_number"`
Status []string `json:"status"` // Return status
Status []GetFBOReturnsFilterStatus `json:"status"`
} }
type GetFBOReturnsResponse struct { type GetFBOReturnsResponse struct {
@@ -38,40 +39,42 @@ type GetFBOReturnsResponse struct {
LastId int64 `json:"last_id"` LastId int64 `json:"last_id"`
// Returns information // Returns information
Returns []struct { Returns []GetFBOReturnsReturn `json:"returns"`
// Time when a return was received from the customer }
AcceptedFromCustomerMoment time.Time `json:"accepted_from_customer_moment"`
// Seller identifier type GetFBOReturnsReturn struct {
CompanyId int64 `json:"company_id"` // Time when a return was received from the customer
AcceptedFromCustomerMoment time.Time `json:"accepted_from_customer_moment"`
// Current return location // Seller identifier
CurrentPlaceName string `json:"current_place_name"` CompanyId int64 `json:"company_id"`
// Return destination // Current return location
DestinationPlaceName string `json:"dst_place_name"` CurrentPlaceName string `json:"current_place_name"`
// Return identifier // Return destination
Id int64 `json:"id"` DestinationPlaceName string `json:"dst_place_name"`
// Indication that the package has been opened. true, if it has been // Return identifier
IsOpened bool `json:"is_opened"` Id int64 `json:"id"`
// Shipment number // Indication that the package has been opened. true, if it has been
PostingNumber string `json:"posting_number"` IsOpened bool `json:"is_opened"`
// Return reason // Shipment number
ReturnReasonName string `json:"return_reason_name"` PostingNumber string `json:"posting_number"`
// Return delivery time to the Ozon warehouse // Return reason
ReturnedToOzonMoment time.Time `json:"returned_to_ozon_moment"` ReturnReasonName string `json:"return_reason_name"`
// Product identifier in the Ozon system, SKU // Return delivery time to the Ozon warehouse
SKU int64 `json:"sku"` ReturnedToOzonMoment time.Time `json:"returned_to_ozon_moment"`
// Return status // Product identifier in the Ozon system, SKU
Status string `json:"status_name"` SKU int64 `json:"sku"`
} `json:"returns"`
// Return status
Status GetFBOReturnsReturnStatus `json:"status_name"`
} }
// Method for getting information on returned products that are sold from the Ozon warehouse // Method for getting information on returned products that are sold from the Ozon warehouse
@@ -80,7 +83,7 @@ func (c Returns) GetFBOReturns(params *GetFBOReturnsParams) (*GetFBOReturnsRespo
resp := &GetFBOReturnsResponse{} resp := &GetFBOReturnsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -98,9 +101,10 @@ type GetFBSReturnsParams struct {
// - minimum — 1 // - minimum — 1
Limit int64 `json:"limit"` Limit int64 `json:"limit"`
// Number of elements that will be skipped in the response. // Return identifier that was loaded the last time.
// For example, if offset=10, the response will start with the 11th element found // Return identifiers with the higher value than `last_id`
Offset int64 `json:"offset"` // will be returned in the response.
LastId int64 `json:"offset"`
} }
type GetFBSReturnsFilter struct { type GetFBSReturnsFilter struct {
@@ -108,7 +112,7 @@ type GetFBSReturnsFilter struct {
AcceptedFromCustomerMoment GetFBSReturnsFilterTimeRange `json:"accepted_from_customer_moment"` AcceptedFromCustomerMoment GetFBSReturnsFilterTimeRange `json:"accepted_from_customer_moment"`
// Last day of free storage // Last day of free storage
LastFreeWaitingDay []GetFBSReturnsFilterTimeRange `json:"last_free_waiting_dat"` LastFreeWaitingDay GetFBSReturnsFilterTimeRange `json:"last_free_waiting_dat"`
// Order ID // Order ID
OrderId int64 `json:"order_id"` OrderId int64 `json:"order_id"`
@@ -122,13 +126,8 @@ type GetFBSReturnsFilter struct {
// Product ID // Product ID
ProductOfferId string `json:"product_offer_id"` ProductOfferId string `json:"product_offer_id"`
// Return status: // Return status
// - returned_to_seller — returned to seller, Status GetFBSReturnsFilterStatus `json:"status"`
// - waiting_for_seller — waiting for seller,
// - accepted_from_customer — accepted from customer,
// - cancelled_with_compensation — cancelled with compensation,
// - ready_for_shipment — ready for shipment
Status string `json:"status"`
} }
type GetFBSReturnsFilterTimeRange struct { type GetFBSReturnsFilterTimeRange struct {
@@ -150,97 +149,110 @@ type GetFBSReturnsFilterTimeRange struct {
type GetFBSReturnsResponse struct { type GetFBSReturnsResponse struct {
core.CommonResponse core.CommonResponse
Result struct { // Return identifier that was loaded the last time.
// Elements counter in the response // Return identifiers with the higher value than `last_id`
Count int64 `json:"count"` // will be returned in the response
LastId int64 `json:"last_id"`
// Returns information // Returns information
Returns []struct { Returns []GetFBSReturnResultReturn `json:"returns"`
// Time of receiving the return from the customer }
AcceptedFromCustomerAmount string `json:"accepted_from_customer_amount"`
// Bottom barcode on the product label type GetFBSReturnResultReturn struct {
ClearingId int64 `json:"clearing_id"` // Time of receiving the return from the customer
AcceptedFromCustomerAmount string `json:"accepted_from_customer_amount"`
// Commission fee // Bottom barcode on the product label
Commission float64 `json:"commission"` ClearingId int64 `json:"clearing_id"`
// Commission percentage // Commission fee
CommissionPercent float64 `json:"commission_percent"` Commission float64 `json:"commission"`
// Return identifier // Commission percentage
Id int64 `json:"id"` CommissionPercent float64 `json:"commission_percent"`
// If the product is in transit — true // Product item identifier in the Ozon logistics system
IsMoving bool `json:"is_moving"` ExemplarId int64 `json:"exemplar_id"`
// Indication that the package has been opened. true, if it has been // Return identifier in the Ozon accounting system
IsOpened bool `json:"is_opened"` Id int64 `json:"id"`
// Last day of free storage // If the product is in transit — true
LastFreeWaitingDay string `json:"last_free_waiting_day"` IsMoving bool `json:"is_moving"`
// ID of the warehouse the product is being transported to // Indication that the package has been opened. true, if it has been
PlaceId int64 `json:"place_id"` IsOpened bool `json:"is_opened"`
// Name of the warehouse the product is being transported to // Last day of free storage
MovingToPlaceName string `json:"moving_to_place_name"` LastFreeWaitingDay string `json:"last_free_waiting_day"`
// Delivery cost // ID of the warehouse the product is being transported to
PickingAmount float64 `json:"picking_amount"` PlaceId int64 `json:"place_id"`
// Shipment number // Name of the warehouse the product is being transported to
PostingNumber string `json:"posting_number"` MovingToPlaceName string `json:"moving_to_place_name"`
// Current product price without a discount // Delivery cost
Price float64 `json:"price"` PickingAmount float64 `json:"picking_amount"`
// Product price without commission // Shipment number
PriceWithoutCommission float64 `json:"price_without_commission"` PostingNumber string `json:"posting_number"`
// Product identifier PickingTag string `json:"picking_tag"`
ProductId int64 `json:"product_id"`
// Product name // Current product price without a discount
ProductName string `json:"product_name"` Price float64 `json:"price"`
// Product quantity // Product price without commission
Quantity int64 `json:"quantity"` PriceWithoutCommission float64 `json:"price_without_commission"`
// Product return date // Product identifier
ReturnDate string `json:"return_date"` ProductId int64 `json:"product_id"`
// Return reason // Product name
ReturnReasonName string `json:"return_reason_name"` ProductName string `json:"product_name"`
// Date when the product is ready to be handed over to the seller // Product quantity
WaitingForSellerDate string `json:"waiting_for_seller_date_time"` Quantity int64 `json:"quantity"`
// Date of handing over the product to the seller // Barcode on the return label. Use this parameter value to work with the return label
ReturnedToSellerDate string `json:"returned_to_seller_date_time"` ReturnBarcode string `json:"return_barcode"`
// Return storage period in days // Package unit identifier in the Ozon logistics system
WaitingForSellerDays int64 `json:"waiting_for_seller_days"` ReturnClearingId int64 `json:"return_clearing_id"`
// Return storage cost // Product return date
ReturnsKeepingCost float64 `json:"returns_keeping_cost"` ReturnDate string `json:"return_date"`
// Product identifier in the Ozon system, SKU // Return reason
SKU int64 `json:"sku"` ReturnReasonName string `json:"return_reason_name"`
// Return status // Date when the product is ready to be handed over to the seller
Status string `json:"status"` WaitingForSellerDate string `json:"waiting_for_seller_date_time"`
} `json:"returns"`
} `json:"result"` // 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 // Method for getting information on returned products that are sold from the seller's warehouse
func (c Returns) GetFBSReturns(params *GetFBSReturnsParams) (*GetFBSReturnsResponse, error) { func (c Returns) GetFBSReturns(params *GetFBSReturnsParams) (*GetFBSReturnsResponse, error) {
url := "/v2/returns/company/fbs" url := "/v3/returns/company/fbs"
resp := &GetFBSReturnsResponse{} resp := &GetFBSReturnsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp) response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -105,42 +105,43 @@ func TestGetFBSReturns(t *testing.T) {
Status: "returned_to_seller", Status: "returned_to_seller",
}, },
Limit: 1000, Limit: 1000,
Offset: 0, LastId: 0,
}, },
`{ `{
"result": { "last_id": 0,
"returns": [ "returns": [
{ {
"id": 19166541735000, "accepted_from_customer_moment": "string",
"clearing_id": 19166541725000, "clearing_id": 23,
"posting_number": "07402477-0022-2", "commission": 21,
"product_id": 172423678, "commission_percent": 0,
"sku": 172423678, "exemplar_id": 42,
"status": "returned_to_seller", "id": 123,
"returns_keeping_cost": 0, "is_moving": true,
"return_reason_name": "5.12 Заказ более не актуален: долгие сроки доставки", "is_opened": true,
"return_date": "2020-08-12T17:27:50+00:00", "last_free_waiting_day": "string",
"quantity": 1, "place_id": 122,
"product_name": "Кофе ароматизированный \"Лесной орех\" 250 гр", "moving_to_place_name": "string",
"price": 294, "picking_amount": 0,
"waiting_for_seller_date_time": "2020-08-16T02:50:35+00:00", "posting_number": "string",
"returned_to_seller_date_time": "2020-08-21T10:07:13+00:00", "picking_tag": "string",
"last_free_waiting_day": "2020-08-19T23:59:59+00:00", "price": 0,
"is_opened": false, "price_without_commission": 0,
"place_id": 0, "product_id": 2222,
"commission_percent": 0, "product_name": "string",
"commission": 0, "quantity": 0,
"price_without_commission": 0, "return_barcode": "string",
"is_moving": false, "return_clearing_id": 0,
"moving_to_place_name": "МОСКВА_ХАБ", "return_date": "string",
"waiting_for_seller_days": 2, "return_reason_name": "string",
"picking_amount": null, "waiting_for_seller_date_time": "string",
"accepted_from_customer_moment": null, "returned_to_seller_date_time": "string",
"picking_tag": null "waiting_for_seller_days": 0,
} "returns_keeping_cost": 0,
], "sku": 33332,
"count": 1 "status": "string"
} }
]
}`, }`,
}, },
// Test No Client-Id or Api-Key // Test No Client-Id or Api-Key
@@ -168,20 +169,17 @@ func TestGetFBSReturns(t *testing.T) {
} }
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
if int(resp.Result.Count) != len(resp.Result.Returns) { if len(resp.Returns) > 0 {
t.Errorf("Count must equal to length of returns") if resp.Returns[0].Id == 0 {
}
if len(resp.Result.Returns) > 0 {
if resp.Result.Returns[0].Id == 0 {
t.Errorf("Id cannot be 0") t.Errorf("Id cannot be 0")
} }
if resp.Result.Returns[0].ProductId == 0 { if resp.Returns[0].ProductId == 0 {
t.Errorf("Product id cannot be 0") t.Errorf("Product id cannot be 0")
} }
if resp.Result.Returns[0].SKU == 0 { if resp.Returns[0].SKU == 0 {
t.Errorf("SKU cannot be 0") t.Errorf("SKU cannot be 0")
} }
if resp.Result.Returns[0].Status == "" { if resp.Returns[0].Status == "" {
t.Errorf("Status cannot be empty") t.Errorf("Status cannot be empty")
} }
} }

471
ozon/strategies.go Normal file
View File

@@ -0,0 +1,471 @@
package ozon
import (
"net/http"
core "github.com/diphantxm/ozon-api-client"
)
type Strategies struct {
client *core.Client
}
type ListCompetitorsParams struct {
// Page number from which you want to download the list of competitors.
// The minimum value is 1
Page int64 `json:"page"`
// Maximum number of competitors on the page. Allowed values: 150
Limit int64 `json:"limit"`
}
type ListCompetitorsResponse struct {
core.CommonResponse
// List of competitors
Competitors []ListCompetitorsCompetitor `json:"competitors"`
// Total number of competitors
Total int32 `json:"total"`
}
type ListCompetitorsCompetitor struct {
// Competitor's name
Name string `json:"name"`
// Competitor identifier
Id int64 `json:"id"`
}
// Method for getting a list of competitors—sellers with similar products in other online stores and marketplaces
func (c Strategies) ListCompetitors(params *ListCompetitorsParams) (*ListCompetitorsResponse, error) {
url := "/v1/pricing-strategy/competitors/list"
resp := &ListCompetitorsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListStrategiesParams struct {
// Page number from which you want to download the list of competitors.
// The minimum value is 1
Page int64 `json:"page"`
// Maximum number of competitors on the page. Allowed values: 150
Limit int64 `json:"limit"`
}
type ListStrategiesResponse struct {
core.CommonResponse
// List of strategies
Strategies []ListStrategiesStrategy `json:"strategies"`
// Total number of strategies
Total int32 `json:"total"`
}
type ListStrategiesStrategy struct {
// Strategy identifier
Id string `json:"id"`
// Strategy name
Name string `json:"name"`
// Strategy type
Type StrategyType `json:"type"`
// Type of the last strategy change
UpdateType StrategyUpdateType `json:"update_type"`
// Date of last change
UpdatedAt string `json:"updated_at"`
// Number of products in the strategy
ProductsCount int64 `json:"products_count"`
// Number of selected competitors
CompetitorsCount int64 `json:"competitors_count"`
// Strategy status
Enabled bool `json:"enabled"`
}
func (c Strategies) List(params *ListStrategiesParams) (*ListStrategiesResponse, error) {
url := "/v1/pricing-strategy/list"
resp := &ListStrategiesResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type CreateStrategyParams struct {
// List of competitors
Competitors []CreateStrategyCompetitor `json:"competitors"`
// Strategy name
StrategyName string `json:"strategy_name"`
}
type CreateStrategyCompetitor struct {
// Coefficient by which the minimum price among competitors will be multiplied.
// The allowed range is from 0.5 to 1.2
Coefficient float32 `json:"coefficient"`
// Competitor identifier
CompetitorId int64 `json:"competitor_id"`
}
type CreateStrategyResponse struct {
core.CommonResponse
// Method result
Result CreateStrategyResult `json:"result"`
}
type CreateStrategyResult struct {
// Strategy identifier
StrategyId string `json:"strategy_id"`
}
func (c Strategies) Create(params *CreateStrategyParams) (*CreateStrategyResponse, error) {
url := "/v1/pricing-strategy/create"
resp := &CreateStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type InfoStrategyParams struct {
// Strategy identifier
StrategyId string `json:"strategy_id"`
}
type InfoStrategyResponse struct {
core.CommonResponse
// Method result
Result InfoStrategyResult `json:"result"`
}
type InfoStrategyResult struct {
// List of competitors
Competitors []CreateStrategyCompetitor `json:"competitors"`
// Strategy status
Enabled bool `json:"enabled"`
// Strategy name
Name string `json:"name"`
// Strategy type
Type StrategyType `json:"type"`
// Type of the last strategy change
UpdateType StrategyUpdateType `json:"update_type"`
}
func (c Strategies) Info(params *InfoStrategyParams) (*InfoStrategyResponse, error) {
url := "/v1/pricing-strategy/info"
resp := &InfoStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type UpdateStrategyParams struct {
// List of competitors
Competitors []CreateStrategyCompetitor `json:"competitors"`
// Product identifier
StrategyId string `json:"strategy_id"`
// Strategy name
StrategyName string `json:"strategy_name"`
}
type UpdateStrategyResponse struct {
core.CommonResponse
}
func (c Strategies) Update(params *UpdateStrategyParams) (*UpdateStrategyResponse, error) {
url := "/v1/pricing-strategy/update"
resp := &UpdateStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type AddProductsToStrategyParams struct {
// List of product identifiers. The maximum number is 50
ProductId []int64 `json:"product_id"`
// Product identifier
StrategyId string `json:"strategy_id"`
}
type AddProductsToStrategyResponse struct {
core.CommonResponse
// Method result
Result AddProductsToStrategyResult `json:"result"`
}
type AddProductsToStrategyResult struct {
// Products with errors
Errors []AddProductsToStrategyResultError `json:"errors"`
// Number of products with errors
FailedProductCount int32 `json:"failed_product_count"`
}
type AddProductsToStrategyResultError struct {
// Error code
Code string `json:"code"`
// Error message
Error string `json:"error"`
// Product identifier
ProductId int64 `json:"product_id"`
}
func (c Strategies) AddProducts(params *AddProductsToStrategyParams) (*AddProductsToStrategyResponse, error) {
url := "/v1/pricing-strategy/products/add"
resp := &AddProductsToStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetStrategiesByProductIdsParams struct {
// List of product identifiers. The maximum number is 50
ProductId []int64 `json:"product_id"`
}
type GetStrategiesByProductIdsResponse struct {
core.CommonResponse
// Method result
Result GetStrategiesByProductIdsResult `json:"result"`
}
type GetStrategiesByProductIdsResult struct {
// Product information
ProductsInfo []GetStrategiesByProductIdsResultProductInfo `json:"products_info"`
}
type GetStrategiesByProductIdsResultProductInfo struct {
// Product identifier
ProductId int64 `json:"product_id"`
// Strategy identifier to which the product is bounded
StrategyId string `json:"strategy_id"`
}
func (c Strategies) GetByProductIds(params *GetStrategiesByProductIdsParams) (*GetStrategiesByProductIdsResponse, error) {
url := "/v1/pricing-strategy/strategy-ids-by-product-ids"
resp := &GetStrategiesByProductIdsResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ListProductsInStrategyParams struct {
// Strategy identifier
StrategyId string `json:"strategy_id"`
}
type ListProductsInStrategyResponse struct {
core.CommonResponse
// Method result
Result ListProductsInStrategyResult `json:"result"`
}
type ListProductsInStrategyResult struct {
// Product identifier
ProductId []string `json:"product_id"`
}
func (c Strategies) ListProducts(params *ListProductsInStrategyParams) (*ListProductsInStrategyResponse, error) {
url := "/v1/pricing-strategy/products/list"
resp := &ListProductsInStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type GetCompetitorPriceParams struct {
// Product identifier
ProductId int64 `json:"product_id"`
}
type GetCompetitorPriceResponse struct {
core.CommonResponse
// Method result
Result GetCompetitorPriceResult `json:"result"`
}
type GetCompetitorPriceResult struct {
// Product identifier
StrategyId string `json:"strategy_id"`
// true if the product is in the pricing strategy
IsEnabled bool `json:"is_enabled"`
// Price of product in the strategy
StrategyProductPrice int32 `json:"strategy_product_price"`
// Price setting date
PriceDownloadedAt string `json:"price_downloaded_at"`
// Competitor identifier
StrategyCompetitorId int64 `json:"strategy_competitor_id"`
// Link to a competitor's product
StrategyCompetitorProductURL string `json:"strategy_competitor_product_url"`
}
func (c Strategies) GetCompetitorPrice(params *GetCompetitorPriceParams) (*GetCompetitorPriceResponse, error) {
url := "/v1/pricing-strategy/product/info"
resp := &GetCompetitorPriceResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type RemoveProductsFromStrategyParams struct {
// List of product identifiers. The maximum number is 50
ProductId []int64 `json:"product_id"`
}
type RemoveProductsFromStrategyResponse struct {
core.CommonResponse
// Method result
Result RemoveProductsFromStrategyResult `json:"result"`
}
type RemoveProductsFromStrategyResult struct {
// Number of products with errors
FailedProductCount int32 `json:"failed_product_count"`
}
func (c Strategies) RemoveProducts(params *RemoveProductsFromStrategyParams) (*RemoveProductsFromStrategyResponse, error) {
url := "/v1/pricing-strategy/products/delete"
resp := &RemoveProductsFromStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type ChangeStrategyStatusParams struct {
// Strategy status
Enabled bool `json:"enabled"`
// Product identifier
StrategyId string `json:"strategy_id"`
}
type ChangeStrategyStatusResponse struct {
core.CommonResponse
}
func (c Strategies) ChangeStatus(params *ChangeStrategyStatusParams) (*ChangeStrategyStatusResponse, error) {
url := "/v1/pricing-strategy/status"
resp := &ChangeStrategyStatusResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}
type RemoveStrategyParams struct {
// Strategy identifier
StrategyId string `json:"strategy_id"`
}
type RemoveStrategyResponse struct {
core.CommonResponse
}
func (c Strategies) Remove(params *RemoveStrategyParams) (*RemoveStrategyResponse, error) {
url := "/v1/pricing-strategy/delete"
resp := &RemoveStrategyResponse{}
response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
if err != nil {
return nil, err
}
response.CopyCommonResponse(&resp.CommonResponse)
return resp, nil
}

668
ozon/strategies_test.go Normal file
View File

@@ -0,0 +1,668 @@
package ozon
import (
"net/http"
"testing"
core "github.com/diphantxm/ozon-api-client"
)
func TestListCompetitors(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListCompetitorsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListCompetitorsParams{
Page: 1,
Limit: 20,
},
`{
"competitor": [
{
"competitor_name": "string",
"competitor_id": 0
}
],
"total": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListCompetitorsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().ListCompetitors(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestListStrategies(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListStrategiesParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListStrategiesParams{
Page: 1,
Limit: 20,
},
`{
"strategies": [
{
"strategy_id": "string",
"strategy_name": "string",
"type": "string",
"update_type": "string",
"updated_at": "string",
"products_count": 0,
"competitors_count": 0,
"enabled": true
}
],
"total": 0
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListStrategiesParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().List(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestCreateStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *CreateStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&CreateStrategyParams{
StrategyName: "New strategy",
Competitors: []CreateStrategyCompetitor{
{
CompetitorId: 1008426,
Coefficient: 1,
},
{
CompetitorId: 204,
Coefficient: 1,
},
{
CompetitorId: 91,
Coefficient: 1,
},
{
CompetitorId: 48,
Coefficient: 1,
},
},
},
`{
"result": {
"id": "4f3a1d4c-5833-4f04-b69b-495cbc1f6f1c"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&CreateStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().Create(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestInfoStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *InfoStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&InfoStrategyParams{
StrategyId: "1",
},
`{
"result": {
"strategy_name": "test1",
"enabled": true,
"update_type": "strategyItemsListChanged",
"type": "COMP_PRICE",
"competitors": [
{
"competitor_id": 204,
"coefficient": 1
},
{
"competitor_id": 1008426,
"coefficient": 1
}
]
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&InfoStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().Info(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestUpdateStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *UpdateStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&UpdateStrategyParams{
StrategyId: "a3de1826-9c54-40f1-bb6d-1a9e2638b058",
StrategyName: "New Strategy",
Competitors: []CreateStrategyCompetitor{
{
CompetitorId: 1008426,
Coefficient: 1,
},
{
CompetitorId: 204,
Coefficient: 1,
},
{
CompetitorId: 91,
Coefficient: 1,
},
{
CompetitorId: 48,
Coefficient: 1,
},
{
CompetitorId: 45,
Coefficient: 1,
},
},
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&UpdateStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().Update(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestAddProductsToStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *AddProductsToStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&AddProductsToStrategyParams{
ProductId: []int64{29209},
StrategyId: "e29114f0-177d-4160-8d06-2bc528470dda",
},
`{
"result": {
"failed_product_count": 0
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&AddProductsToStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().AddProducts(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetStrategiesByProductIds(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetStrategiesByProductIdsParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetStrategiesByProductIdsParams{
ProductId: []int64{29209},
},
`{
"result": {
"products_info": [
{
"product_id": 29209,
"strategy_id": "b7cd30e6-5667-424d-b105-fbec30a52477"
}
]
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetStrategiesByProductIdsParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().GetByProductIds(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
if resp.StatusCode == http.StatusOK {
if len(resp.Result.ProductsInfo) != len(test.params.ProductId) {
t.Errorf("Length of product ids in request and response are not equal")
}
if len(resp.Result.ProductsInfo) > 0 {
if resp.Result.ProductsInfo[0].ProductId != test.params.ProductId[0] {
t.Errorf("Product ids in request and response are not equal")
}
}
}
}
}
func TestListProductsInStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ListProductsInStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ListProductsInStrategyParams{
StrategyId: "string",
},
`{
"result": {
"product_id": [
"string"
]
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ListProductsInStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().ListProducts(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestGetCompetitorPrice(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *GetCompetitorPriceParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&GetCompetitorPriceParams{
ProductId: 0,
},
`{
"result": {
"strategy_id": "string",
"is_enabled": true,
"strategy_product_price": 0,
"price_downloaded_at": "2022-11-17T15:33:53.936Z",
"strategy_competitor_id": 0,
"strategy_competitor_product_url": "string"
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&GetCompetitorPriceParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().GetCompetitorPrice(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestRemoveProductsFromStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *RemoveProductsFromStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&RemoveProductsFromStrategyParams{
ProductId: []int64{0},
},
`{
"result": {
"failed_product_count": 0
}
}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&RemoveProductsFromStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().RemoveProducts(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestChangeStrategyStatus(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *ChangeStrategyStatusParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&ChangeStrategyStatusParams{
Enabled: true,
StrategyId: "c7516438-7124-4e2c-85d3-ccd92b6b9b65",
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&ChangeStrategyStatusParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().ChangeStatus(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}
func TestRemoveStrategy(t *testing.T) {
t.Parallel()
tests := []struct {
statusCode int
headers map[string]string
params *RemoveStrategyParams
response string
}{
// Test Ok
{
http.StatusOK,
map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"},
&RemoveStrategyParams{
StrategyId: "strategy",
},
`{}`,
},
// Test No Client-Id or Api-Key
{
http.StatusUnauthorized,
map[string]string{},
&RemoveStrategyParams{},
`{
"code": 16,
"message": "Client-Id and Api-Key headers are required"
}`,
},
}
for _, test := range tests {
c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers))
resp, err := c.Strategies().Remove(test.params)
if err != nil {
t.Error(err)
}
if resp.StatusCode != test.statusCode {
t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode)
}
}
}

View File

@@ -14,70 +14,74 @@ type Warehouses struct {
type GetListOfWarehousesResponse struct { type GetListOfWarehousesResponse struct {
core.CommonResponse core.CommonResponse
Result []struct { Result []GetListOfWarehousesResult `json:"result"`
// Trusted acceptance attribute. `true` if trusted acceptance is enabled in the warehouse }
HasEntrustedAcceptance bool `json:"has_entrusted_acceptance"`
// Indication that the warehouse works under the rFBS scheme: type GetListOfWarehousesResult struct {
// - true — the warehouse works under the rFBS scheme; // Trusted acceptance attribute. `true` if trusted acceptance is enabled in the warehouse
// - false — the warehouse does not work under the rFBS scheme. HasEntrustedAcceptance bool `json:"has_entrusted_acceptance"`
IsRFBS bool `json:"is_rfbs"`
// Warehouse name // Indication that the warehouse works under the rFBS scheme:
Name string `json:"name"` // - true — the warehouse works under the rFBS scheme;
// - false — the warehouse does not work under the rFBS scheme.
IsRFBS bool `json:"is_rfbs"`
// Warehouse identifier // Warehouse name
WarehouseId int64 `json:"warehouse_id"` Name string `json:"name"`
// Possibility to print an acceptance certificate in advance. `true` if printing in advance is possible // Warehouse identifier
CanPrintActInAdvance bool `json:"can_print_act_in_advance"` WarehouseId int64 `json:"warehouse_id"`
// FBS first mile // Possibility to print an acceptance certificate in advance. `true` if printing in advance is possible
FirstMileType struct { CanPrintActInAdvance bool `json:"can_print_act_in_advance"`
// DropOff point identifier
DropoffPointId string `json:"dropoff_point_id"`
// DropOff timeslot identifier // FBS first mile
DropoffTimeslotId int64 `json:"dropoff_timeslot_id"` FirstMileType GetListOfWarehousesResultFirstMile `json:"first_mile_type"`
// Indication that the warehouse settings are being updated // Indication if there is a limit on the minimum number of orders. `true` if there is such a limit
FirstMileIsChanging bool `json:"first_mile_is_changing"` HasPostingsLimit bool `json:"has_postings_limit"`
// First mile type: // Indication that the warehouse is not working due to quarantine
// IsKarantin bool `json:"is_karantin"`
// Enum: "DropOff" "Pickup"
// - DropOff
// - Pickup
FirstMileType string `json:"first_mile_type"`
} `json:"first_mile_type"`
// Indication if there is a limit on the minimum number of orders. `true` if there is such a limit // Indication that the warehouse accepts bulky products
HasPostingsLimit bool `json:"has_postings_limit"` IsKGT bool `json:"is_kgt"`
// Indication that the warehouse is not working due to quarantine // Indication that warehouse schedule can be changed
IsKarantin bool `json:"is_karantin"` IsTimetableEditable bool `json:"is_timetable_editable"`
// Indication that the warehouse accepts bulky products // Minimum limit value: the number of orders that can be brought in one shipment
IsKGT bool `json:"is_kgt"` MinPostingsLimit int32 `json:"min_postings_limit"`
// Indication that warehouse schedule can be changed // Limit value. -1 if there is no limit
IsTimetableEditable bool `json:"is_timetable_editable"` PostingsLimit int32 `json:"postings_limit"`
// Minimum limit value: the number of orders that can be brought in one shipment // Number of warehouse working days
MinPostingsLimit int32 `json:"min_postings_limit"` MinWorkingDays int64 `json:"min_working_days"`
// Limit value. -1 if there is no limit // Warehouse status
PostingsLimit int32 `json:"postings_limit"` Status string `json:"status"`
// Number of warehouse working days // Warehouse working days
MinWorkingDays int64 `json:"min_working_days"` WorkingDays []WorkingDay `json:"working_days"`
}
// Warehouse status type GetListOfWarehousesResultFirstMile struct {
Status string `json:"status"` // DropOff point identifier
DropoffPointId string `json:"dropoff_point_id"`
// Warehouse working days // DropOff timeslot identifier
WorkingDays []string `json:"working_days"` DropoffTimeslotId int64 `json:"dropoff_timeslot_id"`
} `json:"resulCommonResponse"`
// Indication that the warehouse settings are being updated
FirstMileIsChanging bool `json:"first_mile_is_changing"`
// First mile type:
//
// Enum: "DropOff" "Pickup"
// - DropOff
// - Pickup
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 // You do not need to specify any parameters in the request. Your company will be identified by the Warehouses ID
@@ -86,7 +90,7 @@ func (c Warehouses) GetListOfWarehouses() (*GetListOfWarehousesResponse, error)
resp := &GetListOfWarehousesResponse{} resp := &GetListOfWarehousesResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp) response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -131,41 +135,43 @@ type GetListOfDeliveryMethodsResponse struct {
HasNext bool `json:"has_next"` HasNext bool `json:"has_next"`
// Method result // Method result
Result []struct { Result []GetListOfDeliveryMethodsResult `json:"result"`
// Company identifier }
CompanyId int64 `json:"company_id"`
// Date and time of delivery method creation type GetListOfDeliveryMethodsResult struct {
CreatedAt time.Time `json:"created_at"` // Company identifier
CompanyId int64 `json:"company_id"`
// Time before an order must be packaged // Date and time of delivery method creation
Cutoff string `json:"cutoff"` CreatedAt time.Time `json:"created_at"`
// Delivery method identifier // Time before an order must be packaged
Id int64 `json:"id"` Cutoff string `json:"cutoff"`
// Delivery method name // Delivery method identifier
Name string `json:"name"` Id int64 `json:"id"`
// Delivery service identifier // Delivery method name
ProviderId int64 `json:"provider_id"` Name string `json:"name"`
// Delivery method status: // Delivery service identifier
// - NEW—created, ProviderId int64 `json:"provider_id"`
// - EDITED—being edited,
// - ACTIVE—active,
// - DISABLED—inactive
Status string `json:"status"`
// Order delivery service identifier // Delivery method status:
TemplateId int64 `json:"template_id"` // - NEW—created,
// - EDITED—being edited,
// - ACTIVE—active,
// - DISABLED—inactive
Status string `json:"status"`
// Date and time when the delivery method was last updated // Order delivery service identifier
UpdatedAt time.Time `json:"updated_at"` TemplateId int64 `json:"template_id"`
// Warehouse identifier // Date and time when the delivery method was last updated
WarehouseId int64 `json:"warehouse_id"` UpdatedAt time.Time `json:"updated_at"`
} `json:"result"`
// Warehouse identifier
WarehouseId int64 `json:"warehouse_id"`
} }
// This methods allows you to get list of all delivery methods that can be applied for this warehouse // This methods allows you to get list of all delivery methods that can be applied for this warehouse
@@ -174,7 +180,7 @@ func (c Warehouses) GetListOfDeliveryMethods(params *GetListOfDeliveryMethodsPar
resp := &GetListOfDeliveryMethodsResponse{} resp := &GetListOfDeliveryMethodsResponse{}
response, err := c.client.Request(http.MethodPost, url, nil, resp) response, err := c.client.Request(http.MethodPost, url, nil, resp, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }