Compare commits
	
		
			14 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					83fd8cf825 | ||
| 
						 | 
					ebbc21b618 | ||
| 
						 | 
					f53b573d62 | ||
| 
						 | 
					eb0ce6feb6 | ||
| 
						 | 
					9a41bb1196 | ||
| 
						 | 
					e76b9f3961 | ||
| 
						 | 
					add4202b3e | ||
| 
						 | 
					7beee39eb2 | ||
| 
						 | 
					baeeef9b46 | ||
| 
						 | 
					336c49baa4 | ||
| 
						 | 
					f11ccb4714 | ||
| 
						 | 
					1958dfb94e | ||
| 
						 | 
					2ca20d9b12 | ||
| 
						 | 
					eea0f99066 | 
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								README.md
									
									
									
									
									
								
							@@ -11,6 +11,7 @@ Read full [documentation](https://docs.ozon.ru/api/seller/en/#tag/Introduction)
 | 
				
			|||||||
You can check [list of supported endpoints](ENDPOINTS.md)
 | 
					You can check [list of supported endpoints](ENDPOINTS.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## How to start
 | 
					## How to start
 | 
				
			||||||
 | 
					### API
 | 
				
			||||||
Get Client-Id and Api-Key in your seller profile [here](https://seller.ozon.ru/app/settings/api-keys?locale=en)
 | 
					Get Client-Id and Api-Key in your seller profile [here](https://seller.ozon.ru/app/settings/api-keys?locale=en)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Just add dependency to your project and you're ready to go.
 | 
					Just add dependency to your project and you're ready to go.
 | 
				
			||||||
@@ -49,6 +50,43 @@ func main() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Notifications
 | 
				
			||||||
 | 
					Ozon can send push-notifications to your REST server. There is an implementation of REST server that handles notifications in this library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Official documentation](https://docs.ozon.ru/api/seller/en/#tag/push_intro)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					How to use:
 | 
				
			||||||
 | 
					```Golang
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/diphantxm/ozon-api-client/ozon/notifications"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						// Create server
 | 
				
			||||||
 | 
						port := 5000
 | 
				
			||||||
 | 
						server := notifications.NewNotificationServer(port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Register handlers passing message type and handler itself
 | 
				
			||||||
 | 
						server.Register(notifications.ChatClosedType, func(req interface{}) error {
 | 
				
			||||||
 | 
							notification := req.(*notifications.ChatClosed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Do something with the notification here...
 | 
				
			||||||
 | 
							log.Printf("chat %s has been closed\n", notification.ChatId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Run server
 | 
				
			||||||
 | 
						if err := server.Run(); err != nil {
 | 
				
			||||||
 | 
							log.Printf("error while running notification server: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Contribution
 | 
					## Contribution
 | 
				
			||||||
If you need some endpoints ASAP, create an issue and list all the endpoints. I will add them to library soon.
 | 
					If you need some endpoints ASAP, create an issue and list all the endpoints. I will add them to library soon.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -479,3 +479,29 @@ const (
 | 
				
			|||||||
	GetFBOReturnsReturnStatusAcceptedFromCustomer GetFBOReturnsReturnStatus = "Принят от покупателя"
 | 
						GetFBOReturnsReturnStatusAcceptedFromCustomer GetFBOReturnsReturnStatus = "Принят от покупателя"
 | 
				
			||||||
	GetFBOReturnsReturnStatusReceivedAtOzon       GetFBOReturnsReturnStatus = "Получен в Ozon"
 | 
						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"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										128
									
								
								ozon/fbs.go
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								ozon/fbs.go
									
									
									
									
									
								
							@@ -340,7 +340,7 @@ type PostingProduct struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type FBSCustomer struct {
 | 
					type FBSCustomer struct {
 | 
				
			||||||
	// Delivery address details
 | 
						// Delivery address details
 | 
				
			||||||
	Address FBSCustomerAddress `json:"customer"`
 | 
						Address FBSCustomerAddress `json:"address"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Customer e-mail
 | 
						// Customer e-mail
 | 
				
			||||||
	CustomerEmail string `json:"customer_email"`
 | 
						CustomerEmail string `json:"customer_email"`
 | 
				
			||||||
@@ -1594,7 +1594,13 @@ type PrintLabelingResponse struct {
 | 
				
			|||||||
	core.CommonResponse
 | 
						core.CommonResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Order content
 | 
						// Order content
 | 
				
			||||||
	Content string `json:"content"`
 | 
						Content string `json:"file_content"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File name
 | 
				
			||||||
 | 
						FileName string `json:"file_name"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File type
 | 
				
			||||||
 | 
						ContentType string `json:"content_type"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Generates a PDF file with a labeling for the specified shipments. You can pass a maximum of 20 identifiers in one request.
 | 
					// Generates a PDF file with a labeling for the specified shipments. You can pass a maximum of 20 identifiers in one request.
 | 
				
			||||||
@@ -2139,20 +2145,20 @@ type GetDigitalActParams struct {
 | 
				
			|||||||
	//   - act_of_acceptance — acceptance certificate,
 | 
						//   - act_of_acceptance — acceptance certificate,
 | 
				
			||||||
	//   - act_of_mismatch — discrepancy certificate,
 | 
						//   - act_of_mismatch — discrepancy certificate,
 | 
				
			||||||
	//   - act_of_excess — surplus certificate
 | 
						//   - act_of_excess — surplus certificate
 | 
				
			||||||
	DocType string `json:"doc_type"`
 | 
						DocType DigitalActType `json:"doc_type"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type GetDigitalActResponse struct {
 | 
					type GetDigitalActResponse struct {
 | 
				
			||||||
	core.CommonResponse
 | 
						core.CommonResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// File content in binary format
 | 
						// File content in binary format
 | 
				
			||||||
	Content string `json:"content"`
 | 
						Content string `json:"file_content"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// File name
 | 
						// File name
 | 
				
			||||||
	Name string `json:"name"`
 | 
						Name string `json:"file_name"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// File type
 | 
						// File type
 | 
				
			||||||
	Type string `json:"type"`
 | 
						Type string `json:"content_type"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Specify the type of a certificate in the doc_type parameter: `act_of_acceptance`, `act_of_mismatch`, `act_of_excess`
 | 
					// Specify the type of a certificate in the doc_type parameter: `act_of_acceptance`, `act_of_mismatch`, `act_of_excess`
 | 
				
			||||||
@@ -2161,7 +2167,9 @@ func (c FBS) GetDigitalAct(params *GetDigitalActParams) (*GetDigitalActResponse,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	resp := &GetDigitalActResponse{}
 | 
						resp := &GetDigitalActResponse{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
 | 
						response, err := c.client.Request(http.MethodPost, url, params, resp, map[string]string{
 | 
				
			||||||
 | 
							"Content-Type": "application/json",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2194,7 +2202,9 @@ func (c FBS) PackageUnitLabel(params *PackageUnitLabelsParams) (*PackageUnitLabe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	resp := &PackageUnitLabelsResponse{}
 | 
						resp := &PackageUnitLabelsResponse{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	response, err := c.client.Request(http.MethodPost, url, params, resp, nil)
 | 
						response, err := c.client.Request(http.MethodPost, url, params, resp, map[string]string{
 | 
				
			||||||
 | 
							"Content-Type": "application/json",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2645,3 +2655,105 @@ func (c FBS) ETGBCustomsDeclarations(params *ETGBCustomsDeclarationsParams) (*ET
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return resp, nil
 | 
						return resp, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BarcodeFromProductShipmentParams struct {
 | 
				
			||||||
 | 
						// Freight identifier
 | 
				
			||||||
 | 
						Id int64 `json:"id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BarcodeFromProductShipmentResponse struct {
 | 
				
			||||||
 | 
						core.CommonResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Link to barcode image
 | 
				
			||||||
 | 
						Content string `json:"file_content"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File name
 | 
				
			||||||
 | 
						Name string `json:"file_name"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File type
 | 
				
			||||||
 | 
						Type string `json:"content_type"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Method for getting a barcode to show at a pick-up point or sorting center during the shipment
 | 
				
			||||||
 | 
					func (c FBS) BarcodeFromProductShipment(params *BarcodeFromProductShipmentParams) (*BarcodeFromProductShipmentResponse, error) {
 | 
				
			||||||
 | 
						url := "/v2/posting/fbs/act/get-barcode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := &BarcodeFromProductShipmentResponse{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						response, err := c.client.Request(http.MethodPost, url, params, resp, map[string]string{
 | 
				
			||||||
 | 
							"Content-Type": "image/png",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						response.CopyCommonResponse(&resp.CommonResponse)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BarcodeValueFromProductShipmentParams struct {
 | 
				
			||||||
 | 
						// Freight identifier
 | 
				
			||||||
 | 
						Id int64 `json:"id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type BarcodeValueFromProductShipmentResponse struct {
 | 
				
			||||||
 | 
						core.CommonResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Barcode in text format
 | 
				
			||||||
 | 
						Result string `json:"result"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Use this method to get the barcode from the /v2/posting/fbs/act/get-barcode response in text format.
 | 
				
			||||||
 | 
					func (c FBS) BarcodeValueFromProductShipment(params *BarcodeValueFromProductShipmentParams) (*BarcodeValueFromProductShipmentResponse, error) {
 | 
				
			||||||
 | 
						url := "/v2/posting/fbs/act/get-barcode/text"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := &BarcodeValueFromProductShipmentResponse{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 GetActPDFParams struct {
 | 
				
			||||||
 | 
						// Document generation task number (freight identifier) received from
 | 
				
			||||||
 | 
						// the POST `/v2/posting/fbs/act/create` method
 | 
				
			||||||
 | 
						Id int64 `json:"id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetActPDFResponse struct {
 | 
				
			||||||
 | 
						core.CommonResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File content in binary format
 | 
				
			||||||
 | 
						FileContent string `json:"file_content"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File name
 | 
				
			||||||
 | 
						FileName string `json:"file_name"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// File type
 | 
				
			||||||
 | 
						ContentType string `json:"content_type"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get the generated transfer documents in PDF format: an acceptance and transfer certificate and a waybill.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If you are not connected to electronic document circulation (EDC), the method returns an acceptance and transfer certificate and a waybill.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// If you are connected to EDC, the method returns a waybill only.
 | 
				
			||||||
 | 
					func (c FBS) GetActPDF(params *GetActPDFParams) (*GetActPDFResponse, error) {
 | 
				
			||||||
 | 
						url := "/v2/posting/fbs/act/get-pdf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resp := &GetActPDFResponse{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						response, err := c.client.Request(http.MethodPost, url, params, resp, map[string]string{
 | 
				
			||||||
 | 
							"Content-Type": "application/pdf",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						response.CopyCommonResponse(&resp.CommonResponse)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										187
									
								
								ozon/fbs_test.go
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								ozon/fbs_test.go
									
									
									
									
									
								
							@@ -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")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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¹2wOWû\\Ϛ뫧×Ùö;ì|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")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								ozon/notifications/enums.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								ozon/notifications/enums.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					package notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MessageType string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						PingType                MessageType = "TYPE_PING"
 | 
				
			||||||
 | 
						NewPostingType          MessageType = "TYPE_NEW_POSTING"
 | 
				
			||||||
 | 
						PostingCancelledType    MessageType = "TYPE_POSTING_CANCELLED"
 | 
				
			||||||
 | 
						StateChangedType        MessageType = "TYPE_STATE_CHANGED"
 | 
				
			||||||
 | 
						CutoffDateChangedType   MessageType = "TYPE_CUTOFF_DATE_CHANGED"
 | 
				
			||||||
 | 
						DeliveryDateChangedType MessageType = "TYPE_DELIVERY_DATE_CHANGED"
 | 
				
			||||||
 | 
						CreateOrUpdateType      MessageType = "TYPE_CREATE_OR_UPDATE_ITEM"
 | 
				
			||||||
 | 
						PriceIndexChangedType   MessageType = "TYPE_PRICE_INDEX_CHANGED"
 | 
				
			||||||
 | 
						StocksChangedType       MessageType = "TYPE_STOCKS_CHANGED"
 | 
				
			||||||
 | 
						NewMessageType          MessageType = "TYPE_NEW_MESSAGE"
 | 
				
			||||||
 | 
						UpdateMessageType       MessageType = "TYPE_UPDATE_MESSAGE"
 | 
				
			||||||
 | 
						MessageReadType         MessageType = "TYPE_MESSAGE_READ"
 | 
				
			||||||
 | 
						ChatClosedType          MessageType = "TYPE_CHAT_CLOSED"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										165
									
								
								ozon/notifications/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								ozon/notifications/server.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					package notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Handler func(req interface{}) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NotificationServer struct {
 | 
				
			||||||
 | 
						port     int
 | 
				
			||||||
 | 
						handlers map[MessageType]Handler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewNotificationServer(port int) *NotificationServer {
 | 
				
			||||||
 | 
						return &NotificationServer{
 | 
				
			||||||
 | 
							port:     port,
 | 
				
			||||||
 | 
							handlers: map[MessageType]Handler{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns *NotificationServer) Run() error {
 | 
				
			||||||
 | 
						mux := http.NewServeMux()
 | 
				
			||||||
 | 
						mux.HandleFunc("/", ns.handler)
 | 
				
			||||||
 | 
						server := http.Server{
 | 
				
			||||||
 | 
							Addr:    fmt.Sprintf("0.0.0.0:%d", ns.port),
 | 
				
			||||||
 | 
							Handler: mux,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return server.ListenAndServe()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns *NotificationServer) handler(rw http.ResponseWriter, httpReq *http.Request) {
 | 
				
			||||||
 | 
						mt := &Common{}
 | 
				
			||||||
 | 
						content, err := ioutil.ReadAll(httpReq.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Print(err)
 | 
				
			||||||
 | 
							ns.error(rw, http.StatusBadRequest, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := json.Unmarshal(content, mt); err != nil {
 | 
				
			||||||
 | 
							log.Print(err)
 | 
				
			||||||
 | 
							ns.error(rw, http.StatusBadRequest, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if mt.MessageType == PingType {
 | 
				
			||||||
 | 
							resp := pingResponse{
 | 
				
			||||||
 | 
								Version: "1.0",
 | 
				
			||||||
 | 
								Name:    "Ozon Seller API",
 | 
				
			||||||
 | 
								Time:    time.Now(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							respJson, err := json.Marshal(resp)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Print(err)
 | 
				
			||||||
 | 
								ns.error(rw, http.StatusInternalServerError, err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rw.WriteHeader(http.StatusOK)
 | 
				
			||||||
 | 
							rw.Write(respJson)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req, err := ns.unmarshal(mt.MessageType, content)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Print(err)
 | 
				
			||||||
 | 
							ns.result(rw, false)
 | 
				
			||||||
 | 
							//ns.error(rw, http.StatusInternalServerError, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						h, ok := ns.handlers[mt.MessageType]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							ns.result(rw, true)
 | 
				
			||||||
 | 
							log.Printf("handler for %s is not registered", mt.MessageType)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := h(req); err != nil {
 | 
				
			||||||
 | 
							log.Print(err)
 | 
				
			||||||
 | 
							ns.result(rw, false)
 | 
				
			||||||
 | 
							//ns.error(rw, http.StatusInternalServerError, err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ns.result(rw, true)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns *NotificationServer) Register(mt MessageType, handler func(req interface{}) error) {
 | 
				
			||||||
 | 
						ns.handlers[mt] = handler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns *NotificationServer) unmarshal(messageType MessageType, content []byte) (interface{}, error) {
 | 
				
			||||||
 | 
						switch messageType {
 | 
				
			||||||
 | 
						case NewPostingType:
 | 
				
			||||||
 | 
							v := &NewPosting{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case PostingCancelledType:
 | 
				
			||||||
 | 
							v := &PostingCancelled{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case StateChangedType:
 | 
				
			||||||
 | 
							v := &StateChanged{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case CutoffDateChangedType:
 | 
				
			||||||
 | 
							v := &CutoffDateChanged{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case DeliveryDateChangedType:
 | 
				
			||||||
 | 
							v := &DeliveryDateChanged{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case CreateOrUpdateType:
 | 
				
			||||||
 | 
							v := &CreateOrUpdateItem{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case PriceIndexChangedType:
 | 
				
			||||||
 | 
							v := &PriceIndexChanged{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case StocksChangedType:
 | 
				
			||||||
 | 
							v := &StocksChanged{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case NewMessageType:
 | 
				
			||||||
 | 
							v := &NewMessage{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case UpdateMessageType:
 | 
				
			||||||
 | 
							v := &UpdateMessage{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case MessageReadType:
 | 
				
			||||||
 | 
							v := &MessageRead{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						case ChatClosedType:
 | 
				
			||||||
 | 
							v := &ChatClosed{}
 | 
				
			||||||
 | 
							err := json.Unmarshal(content, v)
 | 
				
			||||||
 | 
							return v, err
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("unsupported type: %s", messageType)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns *NotificationServer) error(rw http.ResponseWriter, statusCode int, err error) {
 | 
				
			||||||
 | 
						errResp := errorResponse{
 | 
				
			||||||
 | 
							Data: errorData{
 | 
				
			||||||
 | 
								Code:    fmt.Sprintf("%d", statusCode),
 | 
				
			||||||
 | 
								Message: err.Error(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						errJson, _ := json.Marshal(errResp)
 | 
				
			||||||
 | 
						rw.WriteHeader(statusCode)
 | 
				
			||||||
 | 
						rw.Write(errJson)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ns *NotificationServer) result(rw http.ResponseWriter, res bool) {
 | 
				
			||||||
 | 
						rw.WriteHeader(http.StatusOK)
 | 
				
			||||||
 | 
						rw.Write([]byte(fmt.Sprintf(`{"result": %t}`, res)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										133
									
								
								ozon/notifications/server_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								ozon/notifications/server_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					package notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNotificationServer(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							request  string
 | 
				
			||||||
 | 
							response string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							// PING
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								`{
 | 
				
			||||||
 | 
									"message_type": "TYPE_PING",
 | 
				
			||||||
 | 
									"time": "2019-08-24T14:15:22Z"
 | 
				
			||||||
 | 
								}`,
 | 
				
			||||||
 | 
								`{
 | 
				
			||||||
 | 
									"version": "1.0",
 | 
				
			||||||
 | 
									"name": "Ozon Seller API"
 | 
				
			||||||
 | 
								}`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							// REGISTERED HANDLER
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								`{  
 | 
				
			||||||
 | 
									"message_type": "TYPE_CHAT_CLOSED",
 | 
				
			||||||
 | 
									"chat_id": "b646d975-0c9c-4872-9f41-8b1e57181063",
 | 
				
			||||||
 | 
									"chat_type": "Buyer_Seller",
 | 
				
			||||||
 | 
									"user": {
 | 
				
			||||||
 | 
										"id": "115568",
 | 
				
			||||||
 | 
										"type": "Сustomer"
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									"seller_id": "7"
 | 
				
			||||||
 | 
								}`,
 | 
				
			||||||
 | 
								`{
 | 
				
			||||||
 | 
									"result": true
 | 
				
			||||||
 | 
								}`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							// UNREGISTERED HANDLER
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								`{  
 | 
				
			||||||
 | 
									"message_type": "TYPE_MESSAGE_READ",
 | 
				
			||||||
 | 
									"chat_id": "b646d975-0c9c-4872-9f41-8b1e57181063",
 | 
				
			||||||
 | 
									"chat_type": "Buyer_Seller",
 | 
				
			||||||
 | 
									"message_id": "3000000000817031942",
 | 
				
			||||||
 | 
									"created_at": "2022-07-18T20:58:04.528Z",    
 | 
				
			||||||
 | 
									"user": {
 | 
				
			||||||
 | 
										"id": "115568",
 | 
				
			||||||
 | 
										"type": "Сustomer"
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									"last_read_message_id": "3000000000817031942",
 | 
				
			||||||
 | 
									"seller_id": "7"
 | 
				
			||||||
 | 
								}`,
 | 
				
			||||||
 | 
								`{
 | 
				
			||||||
 | 
									"result": true
 | 
				
			||||||
 | 
								}`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						port := getFreePort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client := http.Client{}
 | 
				
			||||||
 | 
						server := NewNotificationServer(port)
 | 
				
			||||||
 | 
						server.Register(ChatClosedType, func(req interface{}) error {
 | 
				
			||||||
 | 
							_, ok := req.(*ChatClosed)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return fmt.Errorf("req is not of ChatClosed type")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							if err := server.Run(); err != nil {
 | 
				
			||||||
 | 
								t.Fatalf("notification server is down: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: get rid of it
 | 
				
			||||||
 | 
						// Needed to make sure server is running
 | 
				
			||||||
 | 
						time.Sleep(3 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
 | 
							httpResp, err := client.Post(fmt.Sprintf("http://0.0.0.0:%d/", port), "application/json", strings.NewReader(testCase.request))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Error(err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gotJson, err := ioutil.ReadAll(httpResp.Body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Error(err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							expected := map[string]interface{}{}
 | 
				
			||||||
 | 
							got := map[string]interface{}{}
 | 
				
			||||||
 | 
							err = json.Unmarshal(gotJson, &got)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Error(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = json.Unmarshal([]byte(testCase.response), &expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := compare(expected, got); err != nil {
 | 
				
			||||||
 | 
								t.Error(err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func compare(expected map[string]interface{}, got map[string]interface{}) error {
 | 
				
			||||||
 | 
						for k, v := range expected {
 | 
				
			||||||
 | 
							if gotValue, ok := got[k]; !ok {
 | 
				
			||||||
 | 
								return fmt.Errorf("key %s is expected to present", k)
 | 
				
			||||||
 | 
							} else if !reflect.DeepEqual(gotValue, v) {
 | 
				
			||||||
 | 
								return fmt.Errorf("key %s is not equal, got: %v, want: %v", k, gotValue, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getFreePort() int {
 | 
				
			||||||
 | 
						listener, _ := net.Listen("tcp", ":0")
 | 
				
			||||||
 | 
						defer listener.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return listener.Addr().(*net.TCPAddr).Port
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										327
									
								
								ozon/notifications/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								ozon/notifications/types.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,327 @@
 | 
				
			|||||||
 | 
					package notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Checking if the service is ready at initial connection and periodically after it
 | 
				
			||||||
 | 
					type pingRequest struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Date and time when the notification was sent in UTC format
 | 
				
			||||||
 | 
						Time time.Time `json:"time"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type pingResponse struct {
 | 
				
			||||||
 | 
						// Application version
 | 
				
			||||||
 | 
						Version string `json:"version"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Application name
 | 
				
			||||||
 | 
						Name string `json:"name"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Date and time when notification processing started in UTC format
 | 
				
			||||||
 | 
						Time time.Time `json:"time"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Common struct {
 | 
				
			||||||
 | 
						MessageType MessageType `json:"message_type"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New shipment
 | 
				
			||||||
 | 
					type NewPosting struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Shipment number
 | 
				
			||||||
 | 
						PostingNumber string `json:"posting_number"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Products information
 | 
				
			||||||
 | 
						Products []Product `json:"products"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Date and time when the shipment processing started in the UTC format
 | 
				
			||||||
 | 
						InProccessAt time.Time `json:"in_process_at"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Warehouse identifier where the products for this shipment are stored
 | 
				
			||||||
 | 
						WarehouseId int64 `json:"warehouse_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Product struct {
 | 
				
			||||||
 | 
						// Product SKU
 | 
				
			||||||
 | 
						SKU int64 `json:"sku"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product quantity
 | 
				
			||||||
 | 
						Quantity int64 `json:"quantity"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Shipment cancellation
 | 
				
			||||||
 | 
					type PostingCancelled struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Shipment number
 | 
				
			||||||
 | 
						PostingNumber string `json:"posting_number"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Products information
 | 
				
			||||||
 | 
						Products []Product `json:"products"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Previous shipment status
 | 
				
			||||||
 | 
						OldState string `json:"old_state"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// New shipment status: posting_canceled—canceled
 | 
				
			||||||
 | 
						NewState string `json:"new_state"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Date and time when the shipment status was changed in UTC format
 | 
				
			||||||
 | 
						ChangedStateDate time.Time `json:"changed_state_date"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Information about cancellation reason
 | 
				
			||||||
 | 
						Reason Reason `json:"reason"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Warehouse identifier where the products for this shipment are stored
 | 
				
			||||||
 | 
						WarehouseId int64 `json:"warehouse_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Reason struct {
 | 
				
			||||||
 | 
						// Cancellation reason identifier
 | 
				
			||||||
 | 
						Id int64 `json:"id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Cancellation reason
 | 
				
			||||||
 | 
						Message string `json:"message"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Shipment status change
 | 
				
			||||||
 | 
					type StateChanged struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Shipment number
 | 
				
			||||||
 | 
						PostingNumber string `json:"posting_number"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// New shipment status
 | 
				
			||||||
 | 
						NewState string `json:"new_state"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Date and time when the shipment status was changed in UTC format
 | 
				
			||||||
 | 
						ChangedStateDate time.Time `json:"chagned_state_date"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Warehouse identifier where the products for this shipment are stored
 | 
				
			||||||
 | 
						WarehouseId int64 `json:"warehouse_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Shipment shipping date change
 | 
				
			||||||
 | 
					type CutoffDateChanged struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Shipment number
 | 
				
			||||||
 | 
						PostingNumber string `json:"posting_number"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// New shipping date and time in UTC format
 | 
				
			||||||
 | 
						NewCutoffDate time.Time `json:"new_cutoff_date"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Previous shipping date and time in UTC format
 | 
				
			||||||
 | 
						OldCutoffDate time.Time `json:"old_cutoff_date"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Warehouse identifier where the products for this shipment are stored
 | 
				
			||||||
 | 
						WarehouseId int64 `json:"warehouse_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Shipment delivery date change
 | 
				
			||||||
 | 
					type DeliveryDateChanged struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Shipment number
 | 
				
			||||||
 | 
						PostingNumber string `json:"posting_number"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// New delivery start date and time in UTC format
 | 
				
			||||||
 | 
						NewDeliveryDateBegin time.Time `json:"new_delivery_date_begin"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// New delivery end date and time in UTC format
 | 
				
			||||||
 | 
						NewDeliveryDateEnd time.Time `json:"new_delivery_date_end"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Previous delivery start date and time in UTC format
 | 
				
			||||||
 | 
						OldDeliveryDateBegin time.Time `json:"old_delivery_date_begin"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Previous delivery end date and time in UTC format
 | 
				
			||||||
 | 
						OldDeliveryDateEnd time.Time `json:"old_delivery_date_end"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Warehouse identifier where the products for this shipment are stored
 | 
				
			||||||
 | 
						WarehouseId int64 `json:"warehouse_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Product creation and update or processing error
 | 
				
			||||||
 | 
					type CreateOrUpdateItem struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product identifier in the seller's system
 | 
				
			||||||
 | 
						OfferId string `json:"offer_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product identifier
 | 
				
			||||||
 | 
						ProductId int64 `json:"product_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// An indication that errors occurred during the product creation or update
 | 
				
			||||||
 | 
						IsError bool `json:"is_error"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update date and time
 | 
				
			||||||
 | 
						ChangedAt time.Time `json:"changed_at"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Product price index change
 | 
				
			||||||
 | 
					type PriceIndexChanged struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Date and time of price index change
 | 
				
			||||||
 | 
						UpdatedAt time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product SKU
 | 
				
			||||||
 | 
						SKU int64 `json:"sku"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product identifier
 | 
				
			||||||
 | 
						ProductId int64 `json:"product_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Price index
 | 
				
			||||||
 | 
						PriceIndex int64 `json:"price_index"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stock change at the seller's warehouse
 | 
				
			||||||
 | 
					type StocksChanged struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Array with products data
 | 
				
			||||||
 | 
						Items []Item `json:"items"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Item struct {
 | 
				
			||||||
 | 
						// Update date and time
 | 
				
			||||||
 | 
						UpdatedAt time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product SKU when working under the FBS or rFBS schemes
 | 
				
			||||||
 | 
						SKU int64 `json:"sku"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Product identifier
 | 
				
			||||||
 | 
						ProductId int64 `json:"product_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Array with product stocks data
 | 
				
			||||||
 | 
						Stocks []Stock `json:"stocks"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Stock struct {
 | 
				
			||||||
 | 
						// Warehouse identifier
 | 
				
			||||||
 | 
						WarehouseId int64 `json:"warehouse_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Total product stocks at the warehouse
 | 
				
			||||||
 | 
						Present int64 `json:"present"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Number of reserved products at the warehouse
 | 
				
			||||||
 | 
						Reserved int64 `json:"reserved"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New message in chat
 | 
				
			||||||
 | 
					type NewMessage struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Chat identifier
 | 
				
			||||||
 | 
						ChatId string `json:"chat_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Chat type
 | 
				
			||||||
 | 
						ChatType string `json:"chat_type"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Message identifier
 | 
				
			||||||
 | 
						MessageId string `json:"message_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Message creation date
 | 
				
			||||||
 | 
						CreatedAt time.Time `json:"created_at"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Information about message sender
 | 
				
			||||||
 | 
						User User `json:"user"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Array with message content in Markdown format
 | 
				
			||||||
 | 
						Data []string `json:"data"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type User struct {
 | 
				
			||||||
 | 
						// Sender identifier
 | 
				
			||||||
 | 
						Id string `json:"id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sender type
 | 
				
			||||||
 | 
						Type string `json:"type"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Message in chat has changed
 | 
				
			||||||
 | 
					type UpdateMessage struct {
 | 
				
			||||||
 | 
						NewMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Message update date
 | 
				
			||||||
 | 
						UpdatedAt time.Time `json:"updated_at"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Customer or support read your message
 | 
				
			||||||
 | 
					type MessageRead struct {
 | 
				
			||||||
 | 
						NewMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Last read message identifier
 | 
				
			||||||
 | 
						LastReadMessageId string `json:"last_read_message_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Chat is closed
 | 
				
			||||||
 | 
					type ChatClosed struct {
 | 
				
			||||||
 | 
						Common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Chat identifier
 | 
				
			||||||
 | 
						ChatId string `json:"chat_id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Chat type
 | 
				
			||||||
 | 
						ChatType string `json:"chat_type"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Information about the user who closed the chat
 | 
				
			||||||
 | 
						User User `json:"user"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// User identifier
 | 
				
			||||||
 | 
						Id string `json:"id"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// User type
 | 
				
			||||||
 | 
						Type string `json:"type"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Seller identifier
 | 
				
			||||||
 | 
						SellerId string `json:"seller_id"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Response struct {
 | 
				
			||||||
 | 
						// Notification is received
 | 
				
			||||||
 | 
						Result bool `json:"result"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type errorResponse struct {
 | 
				
			||||||
 | 
						// Information about the error
 | 
				
			||||||
 | 
						Data errorData `json:"error"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type errorData struct {
 | 
				
			||||||
 | 
						// Error code
 | 
				
			||||||
 | 
						Code string `json:"code"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Detailed error description
 | 
				
			||||||
 | 
						Message string `json:"message"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Additional information
 | 
				
			||||||
 | 
						Details string `json:"details"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -655,6 +655,15 @@ type UpdatePricesPrice struct {
 | 
				
			|||||||
	// If the current price of the product is from 400 to 10 000 rubles inclusive, the difference between the values of price and old_price fields should be more than 5%, but not less than 20 rubles.
 | 
						// If the current price of the product is from 400 to 10 000 rubles inclusive, the difference between the values of price and old_price fields should be more than 5%, but not less than 20 rubles.
 | 
				
			||||||
	Price string `json:"price"`
 | 
						Price string `json:"price"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Attribute for enabling and disabling pricing strategies auto-application
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If you've previously enabled automatic application of pricing strategies and don't want to disable it, pass UNKNOWN in the next requests.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If you pass `ENABLED` in this parameter, pass `strategy_id` in the `/v1/pricing-strategy/products/add` method request.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// If you pass `DISABLED` in this parameter, the product is removed from the strategy
 | 
				
			||||||
 | 
						PriceStrategyEnabled PriceStrategy `json:"price_strategy_enabled"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Product identifier
 | 
						// Product identifier
 | 
				
			||||||
	ProductId int64 `json:"product_id"`
 | 
						ProductId int64 `json:"product_id"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1903,6 +1912,9 @@ type GetProductPriceInfoResult struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type GetPRoductPriceInfoResultItem struct {
 | 
					type GetPRoductPriceInfoResultItem struct {
 | 
				
			||||||
 | 
						// Maximum acquiring fee
 | 
				
			||||||
 | 
						Acquiring int32 `json:"acquiring"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Commissions information
 | 
						// Commissions information
 | 
				
			||||||
	Commissions GetProductPriceInfoResultItemCommission `json:"commissions"`
 | 
						Commissions GetProductPriceInfoResultItemCommission `json:"commissions"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1961,10 +1973,10 @@ type GetProductPriceInfoResultItemCommission struct {
 | 
				
			|||||||
	// Pipeline from (FBS)
 | 
						// Pipeline from (FBS)
 | 
				
			||||||
	FBSPipelineFrom float64 `json:"fbs_direct_flow_trans_min_amount"`
 | 
						FBSPipelineFrom float64 `json:"fbs_direct_flow_trans_min_amount"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Shipment processing fee to (FBS)
 | 
						// Minimal shipment processing fee (FBS) — 0 rubles
 | 
				
			||||||
	FBSShipmentProcessingToFee float64 `json:"fbs_first_mile_min_amount"`
 | 
						FBSShipmentProcessingToFee float64 `json:"fbs_first_mile_min_amount"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Shipment processing fee from (FBS)
 | 
						// Maximal shipment processing fee (FBS) — 25 rubles
 | 
				
			||||||
	FBSShipmentProcessingFromFee float64 `json:"Shipment processing fee from (FBS)"`
 | 
						FBSShipmentProcessingFromFee float64 `json:"Shipment processing fee from (FBS)"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Return and cancellation fees, shipment processing (FBS)
 | 
						// Return and cancellation fees, shipment processing (FBS)
 | 
				
			||||||
@@ -1976,7 +1988,13 @@ type GetProductPriceInfoResultItemCommission struct {
 | 
				
			|||||||
	// Return and cancellation fees, pipeline from (FBS)
 | 
						// Return and cancellation fees, pipeline from (FBS)
 | 
				
			||||||
	FBSReturnCancellationFromFees float64 `json:"fbs_return_flow_trans_min_amount"`
 | 
						FBSReturnCancellationFromFees float64 `json:"fbs_return_flow_trans_min_amount"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Sales commission percentage (FBO and FBS)
 | 
						// Sales commission percentage (FBO)
 | 
				
			||||||
 | 
						SalesCommissionFBORate float64 `json:"sales_percent_fbo"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sales commission percentage (FBS)
 | 
				
			||||||
 | 
						SalesCommissionFBSRate float64 `json:"sales_percent_fbs"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Larger sales commission percentage among FBO and FBS
 | 
				
			||||||
	SalesCommissionRate float64 `json:"sales_percent"`
 | 
						SalesCommissionRate float64 `json:"sales_percent"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -454,6 +454,7 @@ func TestUpdatePrices(t *testing.T) {
 | 
				
			|||||||
						OldPrice:             "0",
 | 
											OldPrice:             "0",
 | 
				
			||||||
						Price:                "1448",
 | 
											Price:                "1448",
 | 
				
			||||||
						ProductId:            1386,
 | 
											ProductId:            1386,
 | 
				
			||||||
 | 
											PriceStrategyEnabled: PriceStrategyUnknown,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
@@ -2239,6 +2240,7 @@ func TestGetProductPriceInfo(t *testing.T) {
 | 
				
			|||||||
				"result": {
 | 
									"result": {
 | 
				
			||||||
				  "items": [
 | 
									  "items": [
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
 | 
										  "acquiring": 0,
 | 
				
			||||||
					  "product_id": 243686911,
 | 
										  "product_id": 243686911,
 | 
				
			||||||
					  "offer_id": "356792",
 | 
										  "offer_id": "356792",
 | 
				
			||||||
					  "price": {
 | 
										  "price": {
 | 
				
			||||||
@@ -2254,9 +2256,28 @@ func TestGetProductPriceInfo(t *testing.T) {
 | 
				
			|||||||
						"marketing_seller_price": "",
 | 
											"marketing_seller_price": "",
 | 
				
			||||||
						"auto_action_enabled": true
 | 
											"auto_action_enabled": true
 | 
				
			||||||
					  },
 | 
										  },
 | 
				
			||||||
					  "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": {
 | 
				
			||||||
						"sales_percent": 15,
 | 
											"sales_percent": 15,
 | 
				
			||||||
 | 
											"sales_percent_fbo": 15,
 | 
				
			||||||
 | 
											"sales_percent_fbs": 0,
 | 
				
			||||||
						"fbo_fulfillment_amount": 0,
 | 
											"fbo_fulfillment_amount": 0,
 | 
				
			||||||
						"fbo_direct_flow_trans_min_amount": 31,
 | 
											"fbo_direct_flow_trans_min_amount": 31,
 | 
				
			||||||
						"fbo_direct_flow_trans_max_amount": 46.5,
 | 
											"fbo_direct_flow_trans_max_amount": 46.5,
 | 
				
			||||||
@@ -2265,7 +2286,7 @@ func TestGetProductPriceInfo(t *testing.T) {
 | 
				
			|||||||
						"fbo_return_flow_trans_min_amount": 21.7,
 | 
											"fbo_return_flow_trans_min_amount": 21.7,
 | 
				
			||||||
						"fbo_return_flow_trans_max_amount": 21.7,
 | 
											"fbo_return_flow_trans_max_amount": 21.7,
 | 
				
			||||||
						"fbs_first_mile_min_amount": 0,
 | 
											"fbs_first_mile_min_amount": 0,
 | 
				
			||||||
						"fbs_first_mile_max_amount": 0,
 | 
											"fbs_first_mile_max_amount": 25,
 | 
				
			||||||
						"fbs_direct_flow_trans_min_amount": 41,
 | 
											"fbs_direct_flow_trans_min_amount": 41,
 | 
				
			||||||
						"fbs_direct_flow_trans_max_amount": 61.5,
 | 
											"fbs_direct_flow_trans_max_amount": 61.5,
 | 
				
			||||||
						"fbs_deliv_to_customer_amount": 60,
 | 
											"fbs_deliv_to_customer_amount": 60,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user