Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7739,6 +7739,35 @@ func (c *Client) EditMGChannelTemplate(req EditMGChannelTemplateRequest) (int, e
return code, nil
}

// StoreOffers returns list of offers
//
// For more information see https://docs.retailcrm.ru/Developers/API/APIMethods#get--api-v5-store-offers
//
// Example:
//
// var client = retailcrm.New("https://demo.url", "09jIJ")
//
// active := 1
// data, status, err := client.StoreOffers(retailcrm.OffersRequest{
// OffersFilter: retailcrm.OffersFilter{
// Ids: []int{76},
// Active: &active,
// },
// Limit: 20,
// Page: 1,
// })
//
// if err != nil {
// if apiErr, ok := retailcrm.AsAPIError(err); ok {
// log.Fatalf("http status: %d, %s", status, apiErr.String())
// }
//
// log.Fatalf("http status: %d, error: %s", status, err)
// }
//
// for _, value := range data.Offers {
// log.Printf("%v\n", value)
// }
func (c *Client) StoreOffers(req OffersRequest) (StoreOffersResponse, int, error) {
var result StoreOffersResponse

Expand All @@ -7748,6 +7777,10 @@ func (c *Client) StoreOffers(req OffersRequest) (StoreOffersResponse, int, error
return StoreOffersResponse{}, 0, err
}

for code, value := range req.Properties {
filter.Set(fmt.Sprintf("filter[properties][%s]", code), value)
}

resp, status, err := c.GetRequest(fmt.Sprintf("/store/offers?%s", filter.Encode()))

if err != nil {
Expand Down
74 changes: 73 additions & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9219,11 +9219,46 @@ func TestClient_StoreOffers(t *testing.T) {
Get(prefix+"/store/offers").
MatchParam("filter[active]", "1").
MatchParam("filter[ids][]", "76").
MatchParam("filter[externalIds][]", "offer-external-id").
MatchParam("filter[xmlIds][]", "offer-xml-id").
MatchParam("filter[name]", "Название").
MatchParam("filter[sites][]", "main").
MatchParam("filter[catalogs][]", "2").
MatchParam("filter[groups][]", "10").
MatchParam("filter[priceType]", "base").
MatchParam("filter[properties][color]", "red").
MatchParam("filter[sinceId]", "75").
MatchParam("filter[minPrice]", "100").
MatchParam("filter[maxPrice]", "10000").
MatchParam("filter[minQuantity]", "1").
MatchParam("filter[maxQuantity]", "10").
MatchParam("limit", "20").
MatchParam("page", "1").
Reply(http.StatusOK).
JSON(getStoreOfferResponse())

a := 1
f := OffersRequest{OffersFilter{Ids: []int{76}, Active: &a}}
f := OffersRequest{
OffersFilter: OffersFilter{
Ids: []int{76},
ExternalIDs: []string{"offer-external-id"},
XMLIDs: []string{"offer-xml-id"},
Name: "Название",
Sites: []string{"main"},
Catalogs: []int{2},
Groups: []int{10},
PriceType: "base",
Active: &a,
Properties: map[string]string{"color": "red"},
SinceID: 75,
MinPrice: 100,
MaxPrice: 10000,
MinQuantity: 1,
MaxQuantity: 10,
},
Limit: 20,
Page: 1,
}

resp, status, err := cl.StoreOffers(f)

Expand All @@ -9241,9 +9276,46 @@ func TestClient_StoreOffers(t *testing.T) {

assert.Len(t, resp.Offers, 1)
assert.Equal(t, 76, resp.Offers[0].ID)
assert.Equal(t, "offer-external-id", resp.Offers[0].ExternalID)
assert.Equal(t, "offer-xml-id", resp.Offers[0].XMLID)
assert.Equal(t, "main", resp.Offers[0].Site)
assert.Equal(t, "Название\nПеревод строки", resp.Offers[0].Name)
assert.Equal(t, "Артикул", resp.Offers[0].Article)
assert.Equal(t, "20", resp.Offers[0].VatRate)
assert.Equal(t, 222, resp.Offers[0].Product.ID)
assert.Equal(t, 2, resp.Offers[0].Product.CatalogID)
assert.Equal(t, ProductType("product"), resp.Offers[0].Product.Type)
assert.Equal(t, "product-article", resp.Offers[0].Product.Article)
assert.Equal(t, "Товар", resp.Offers[0].Product.Name)
assert.Equal(t, "https://example.com/product", resp.Offers[0].Product.URL)
assert.Equal(t, "https://example.com/product.jpg", resp.Offers[0].Product.ImageURL)
assert.Equal(t, "Описание товара", resp.Offers[0].Product.Description)
assert.True(t, resp.Offers[0].Product.Popular)
assert.True(t, resp.Offers[0].Product.Stock)
assert.True(t, resp.Offers[0].Product.Novelty)
assert.True(t, resp.Offers[0].Product.Recommended)
assert.Len(t, resp.Offers[0].Product.Options, 1)
assert.Len(t, resp.Offers[0].Product.Groups, 1)
assert.Equal(t, 10, resp.Offers[0].Product.Groups[0].ID)
assert.Equal(t, "group-external-id", resp.Offers[0].Product.Groups[0].ExternalID)
assert.Equal(t, "product-external-id", resp.Offers[0].Product.ExternalID)
assert.Equal(t, "Производитель", resp.Offers[0].Product.Manufacturer)
assert.Equal(t, "2024-01-02 03:04:05", resp.Offers[0].Product.UpdatedAt)
assert.True(t, resp.Offers[0].Product.Active)
assert.Equal(t, float32(5), resp.Offers[0].Product.Quantity)
assert.True(t, resp.Offers[0].Product.Markable)
assert.Equal(t, "chestny_znak", resp.Offers[0].Product.MarkingProvider)
assert.Equal(t, "base", resp.Offers[0].Prices[0].PriceType)
assert.Equal(t, float32(10000), resp.Offers[0].Prices[0].Price)
assert.Equal(t, "RUB", resp.Offers[0].Prices[0].Currency)
assert.Equal(t, float32(10), resp.Offers[0].PurchasePrice)
assert.Equal(t, float32(5), resp.Offers[0].Quantity)
assert.Equal(t, float32(1.5), resp.Offers[0].Weight)
assert.Equal(t, float32(10), resp.Offers[0].Length)
assert.Equal(t, float32(20), resp.Offers[0].Width)
assert.Equal(t, float32(30), resp.Offers[0].Height)
assert.Equal(t, "red", resp.Offers[0].Properties["color"])
assert.True(t, resp.Offers[0].Active)
assert.Equal(t, "1234567890", resp.Offers[0].Barcode)
assert.Equal(t, "pc", resp.Offers[0].Unit.Code)
}
17 changes: 15 additions & 2 deletions filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,21 @@ type LoyaltyAPIFilter struct {
}

type OffersFilter struct {
Ids []int `url:"ids,omitempty,brackets"`
Active *int `url:"active,omitempty"`
Ids []int `url:"ids,omitempty,brackets"` //nolint:revive
ExternalIDs []string `url:"externalIds,omitempty,brackets"`
XMLIDs []string `url:"xmlIds,omitempty,brackets"`
Name string `url:"name,omitempty"`
Sites []string `url:"sites,omitempty,brackets"`
Catalogs []int `url:"catalogs,omitempty,brackets"`
Groups []int `url:"groups,omitempty,brackets"`
PriceType string `url:"priceType,omitempty"`
Active *int `url:"active,omitempty"`
Properties map[string]string `url:"-"`
SinceID int `url:"sinceId,omitempty"`
MinPrice float32 `url:"minPrice,omitempty"`
MaxPrice float32 `url:"maxPrice,omitempty"`
MinQuantity float32 `url:"minQuantity,omitempty"`
MaxQuantity float32 `url:"maxQuantity,omitempty"`
}

type SiteFilter struct {
Expand Down
2 changes: 2 additions & 0 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,6 @@ func (r ConnectRequest) Verify(secret string) bool {

type OffersRequest struct {
OffersFilter `url:"filter,omitempty"`
Limit int `url:"limit,omitempty"`
Page int `url:"page,omitempty"`
}
4 changes: 2 additions & 2 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ type MGChannelTemplatesResponse struct {
}

type StoreOffersResponse struct {
Pagination *Pagination `json:"pagination"`
SuccessfulResponse
Offers []Offer `json:"offers,omitempty"`
Pagination *Pagination `json:"pagination"`
Offers []Offer `json:"offers,omitempty"`
}
36 changes: 35 additions & 1 deletion testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,13 +535,39 @@ func getStoreOfferResponse() string {
"https://s3-s1.retailcrm.tech/ru-central1/retailcrm/dev-vega-d32aea7f9a5bc26eba6ad986077cea03/product/65a92fa0bb737-test.jpeg"
],
"id": 76,
"externalId": "offer-external-id",
"xmlId": "offer-xml-id",
"site": "main",
"name": "Название\nПеревод строки",
"article": "Артикул",
"vatRate": "20",
"product": {
"type": "product",
"catalogId": 2,
"id": 222
"id": 222,
"article": "product-article",
"name": "Товар",
"url": "https://example.com/product",
"imageUrl": "https://example.com/product.jpg",
"description": "Описание товара",
"popular": true,
"stock": true,
"novelty": true,
"recommended": true,
"options": ["option"],
"groups": [
{
"id": 10,
"externalId": "group-external-id"
}
],
"externalId": "product-external-id",
"manufacturer": "Производитель",
"updatedAt": "2024-01-02 03:04:05",
"active": true,
"quantity": 5,
"markable": true,
"markingProvider": "chestny_znak"
},
"prices": [
{
Expand All @@ -553,7 +579,15 @@ func getStoreOfferResponse() string {
],
"purchasePrice": 10,
"quantity": 5,
"weight": 1.5,
"length": 10,
"width": 20,
"height": 30,
"properties": {
"color": "red"
},
"active": true,
"barcode": "1234567890",
"unit": {
"code": "pc",
"name": "Штука",
Expand Down
25 changes: 16 additions & 9 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ type Offer struct {
ExternalID string `json:"externalId,omitempty"`
Name string `json:"name,omitempty"`
XMLID string `json:"xmlId,omitempty"`
Site string `json:"site,omitempty"`
Article string `json:"article,omitempty"`
VatRate string `json:"vatRate,omitempty"`
Price float32 `json:"price,omitempty"`
Expand All @@ -774,6 +775,8 @@ type Offer struct {
Properties StringMap `json:"properties,omitempty"`
Prices []OfferPrice `json:"prices,omitempty"`
Images []string `json:"images,omitempty"`
Active bool `json:"active,omitempty"`
Barcode string `json:"barcode,omitempty"`
Unit *Unit `json:"unit,omitempty"`
Product *Product `json:"product,omitempty"`
}
Expand Down Expand Up @@ -1270,15 +1273,19 @@ const (
// Product type.
type Product struct {
BaseProduct
ID int `json:"id,omitempty"`
Type ProductType `json:"type"`
MaxPrice float32 `json:"maxPrice,omitempty"`
MinPrice float32 `json:"minPrice,omitempty"`
ImageURL string `json:"imageUrl,omitempty"`
Quantity float32 `json:"quantity,omitempty"`
Offers []Offer `json:"offers,omitempty"`
Properties StringMap `json:"properties,omitempty"`
Groups []ProductGroup `json:"groups,omitempty"`
ID int `json:"id,omitempty"`
Type ProductType `json:"type"`
CatalogID int `json:"catalogId,omitempty"`
MaxPrice float32 `json:"maxPrice,omitempty"`
MinPrice float32 `json:"minPrice,omitempty"`
ImageURL string `json:"imageUrl,omitempty"`
Quantity float32 `json:"quantity,omitempty"`
Offers []Offer `json:"offers,omitempty"`
Properties StringMap `json:"properties,omitempty"`
Options []interface{} `json:"options,omitempty"`
Groups []ProductGroup `json:"groups,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
MarkingProvider string `json:"markingProvider,omitempty"`
}

// ProductEditGroupInput type.
Expand Down
Loading