mirror of
https://github.com/retailcrm/api-client-go.git
synced 2024-11-21 20:36:03 +03:00
add /api/v5/loyalty/loyalties
, /api/v5/loyalty/loyalties/{id}
add field in `deliveryPaymentTypes` for ` /api/v5/reference/delivery-types`
This commit is contained in:
parent
90f790e148
commit
ddc2b3f785
177
client.go
177
client.go
@ -6084,3 +6084,180 @@ func (c *Client) LoyaltyAccounts(req LoyaltyAccountsRequest) (LoyaltyAccountsRes
|
|||||||
|
|
||||||
return result, status, nil
|
return result, status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoyaltyCalculate calculations of the maximum discount
|
||||||
|
//
|
||||||
|
// For more information see https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5#post--api-v5-loyalty-calculate
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var client = retailcrm.New("https://demo.url", "09jIJ")
|
||||||
|
//
|
||||||
|
// req := LoyaltyCalculateRequest{
|
||||||
|
// Site: "main",
|
||||||
|
// Order: Order{
|
||||||
|
// PrivilegeType: "loyalty_level",
|
||||||
|
// Customer: &Customer{
|
||||||
|
// ID: 123,
|
||||||
|
// },
|
||||||
|
// Items: []OrderItem{
|
||||||
|
// {
|
||||||
|
// InitialPrice: 10000,
|
||||||
|
// Quantity: 1,
|
||||||
|
// Offer: Offer{ID: 214},
|
||||||
|
// PriceType: &PriceType{Code: "base"},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Bonuses: 10,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// data, status, err := client.LoyaltyCalculate(req)
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if data.Success == true {
|
||||||
|
// log.Printf("%v", data.Order.PrivilegeType)
|
||||||
|
// log.Printf("%v", data.Order.BonusesCreditTotal)
|
||||||
|
// }
|
||||||
|
func (c *Client) LoyaltyCalculate(req LoyaltyCalculateRequest) (LoyaltyCalculateResponse, int, error) {
|
||||||
|
var result LoyaltyCalculateResponse
|
||||||
|
|
||||||
|
orderJSON, err := json.Marshal(req.Order)
|
||||||
|
|
||||||
|
p := url.Values{
|
||||||
|
"site": {req.Site},
|
||||||
|
"order": {string(orderJSON)},
|
||||||
|
"bonuses": {fmt.Sprintf("%f", req.Bonuses)},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, status, err := c.PostRequest("/loyalty/calculate", p)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp, &result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoyalties calculations of the maximum discount
|
||||||
|
//
|
||||||
|
// For more information see https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5#get--api-v5-loyalty-loyalties
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var client = retailcrm.New("https://demo.url", "09jIJ")
|
||||||
|
//
|
||||||
|
// req := LoyaltiesRequest{
|
||||||
|
// Filter: LoyaltyApiFilter{
|
||||||
|
// Active: active,
|
||||||
|
// Ids: []int{2},
|
||||||
|
// Sites: []string{"main"},
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// data, status, err := client.GetLoyalties(req)
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if data.Success == true {
|
||||||
|
// for _, l := range data.Loyalties {
|
||||||
|
// log.Printf("%v", l.ID)
|
||||||
|
// log.Printf("%v", l.Active)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
func (c *Client) GetLoyalties(req LoyaltiesRequest) (LoyaltiesResponse, int, error) {
|
||||||
|
var result LoyaltiesResponse
|
||||||
|
|
||||||
|
p, _ := query.Values(req)
|
||||||
|
|
||||||
|
resp, status, err := c.GetRequest(fmt.Sprintf("/loyalty/loyalties?%s", p.Encode()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp, &result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoyaltyById calculations of the maximum discount
|
||||||
|
//
|
||||||
|
// For more information see https://docs.retailcrm.ru/Developers/API/APIVersions/APIv5#get--api-v5-loyalty-loyalties
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var client = retailcrm.New("https://demo.url", "09jIJ")
|
||||||
|
//
|
||||||
|
// data, status, err := client.GetLoyaltyById(2)
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if data.Success == true {
|
||||||
|
// log.Printf("%v", res.Loyalty.ID)
|
||||||
|
// log.Printf("%v", res.Loyalty.Active)
|
||||||
|
// }
|
||||||
|
func (c *Client) GetLoyaltyById(ID int) (LoyaltyResponse, int, error) {
|
||||||
|
var result LoyaltyResponse
|
||||||
|
|
||||||
|
resp, status, err := c.GetRequest(fmt.Sprintf("/loyalty/loyalties/%d", ID))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp, &result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) OrderIntegrationDeliveryCancel(by string, force bool, ID string) (SuccessfulResponse, int, error) {
|
||||||
|
var result SuccessfulResponse
|
||||||
|
|
||||||
|
resp, status, err := c.PostRequest(fmt.Sprintf("/orders/%s/delivery/cancel?by=%s&force=%t", ID, checkBy(by), force), strings.NewReader(""))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp, &result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, status, nil
|
||||||
|
}
|
||||||
|
202
client_test.go
202
client_test.go
@ -3125,7 +3125,26 @@ func TestClient_DeliveryTypes(t *testing.T) {
|
|||||||
gock.New(crmURL).
|
gock.New(crmURL).
|
||||||
Get("/reference/delivery-types").
|
Get("/reference/delivery-types").
|
||||||
Reply(200).
|
Reply(200).
|
||||||
BodyString(`{"success": true}`)
|
BodyString(`{
|
||||||
|
"success": true,
|
||||||
|
"deliveryTypes": {
|
||||||
|
"courier": {
|
||||||
|
"name": "Доставка курьером",
|
||||||
|
"code": "courier",
|
||||||
|
"active": true,
|
||||||
|
"deliveryPaymentTypes": [
|
||||||
|
{
|
||||||
|
"code": "cash",
|
||||||
|
"cod": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "bank-card",
|
||||||
|
"cod": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
data, st, err := c.DeliveryTypes()
|
data, st, err := c.DeliveryTypes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3139,6 +3158,14 @@ func TestClient_DeliveryTypes(t *testing.T) {
|
|||||||
if data.Success != true {
|
if data.Success != true {
|
||||||
t.Errorf("%v", err)
|
t.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "Доставка курьером", data.DeliveryTypes["courier"].Name)
|
||||||
|
assert.Equal(t, "courier", data.DeliveryTypes["courier"].Code)
|
||||||
|
assert.True(t, data.DeliveryTypes["courier"].Active)
|
||||||
|
assert.Equal(t, "cash", data.DeliveryTypes["courier"].DeliveryPaymentTypes[0].Code)
|
||||||
|
assert.Equal(t, "bank-card", data.DeliveryTypes["courier"].DeliveryPaymentTypes[1].Code)
|
||||||
|
assert.False(t, data.DeliveryTypes["courier"].DeliveryPaymentTypes[0].Cod)
|
||||||
|
assert.False(t, data.DeliveryTypes["courier"].DeliveryPaymentTypes[1].Cod)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_LegalEntities(t *testing.T) {
|
func TestClient_LegalEntities(t *testing.T) {
|
||||||
@ -7272,6 +7299,8 @@ func TestClient_LoyaltyBonusStatusDetails(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestClient_LoyaltyAccounts(t *testing.T) {
|
func TestClient_LoyaltyAccounts(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
req := LoyaltyAccountsRequest{
|
req := LoyaltyAccountsRequest{
|
||||||
Filter: LoyaltyAccountApiFilter{
|
Filter: LoyaltyAccountApiFilter{
|
||||||
Status: "activated",
|
Status: "activated",
|
||||||
@ -7314,3 +7343,174 @@ func TestClient_LoyaltyAccounts(t *testing.T) {
|
|||||||
assert.Equal(t, req.Filter.PhoneNumber, res.LoyaltyAccounts[0].PhoneNumber)
|
assert.Equal(t, req.Filter.PhoneNumber, res.LoyaltyAccounts[0].PhoneNumber)
|
||||||
assert.Equal(t, req.Filter.Status, res.LoyaltyAccounts[0].Status)
|
assert.Equal(t, req.Filter.Status, res.LoyaltyAccounts[0].Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClient_LoyaltyCalculate(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
req := getLoyaltyCalculateReq()
|
||||||
|
orderJSON, err := json.Marshal(req.Order)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
p := url.Values{
|
||||||
|
"site": {req.Site},
|
||||||
|
"bonuses": {fmt.Sprintf("%f", req.Bonuses)},
|
||||||
|
"order": {string(orderJSON)},
|
||||||
|
}
|
||||||
|
|
||||||
|
gock.New(crmURL).
|
||||||
|
Post(prefix + "/loyalty/calculate").
|
||||||
|
BodyString(p.Encode()).
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(getLoyaltyCalculateResponse())
|
||||||
|
|
||||||
|
res, status, err := client().LoyaltyCalculate(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !statuses[status] {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Success != true {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, float32(999), res.Order.BonusesCreditTotal)
|
||||||
|
assert.Equal(t, float32(10), res.Order.BonusesChargeTotal)
|
||||||
|
assert.Equal(t, req.Order.PrivilegeType, res.Order.PrivilegeType)
|
||||||
|
assert.Equal(t, float64(9990), res.Order.TotalSumm)
|
||||||
|
assert.Equal(t, 13, res.Order.LoyaltyAccount.ID)
|
||||||
|
assert.Equal(t, float64(240), res.Order.LoyaltyAccount.Amount)
|
||||||
|
assert.Equal(t, req.Order.Customer.ID, res.Order.Customer.ID)
|
||||||
|
|
||||||
|
assert.Equal(t, float32(999), res.Order.Items[0].BonusesCreditTotal)
|
||||||
|
assert.Equal(t, float32(10), res.Order.Items[0].BonusesChargeTotal)
|
||||||
|
assert.Equal(t, req.Order.Items[0].PriceType.Code, res.Order.Items[0].PriceType.Code)
|
||||||
|
assert.Equal(t, req.Order.Items[0].InitialPrice, res.Order.Items[0].InitialPrice)
|
||||||
|
assert.Equal(t, "bonus_charge", res.Order.Items[0].Discounts[0].Type)
|
||||||
|
assert.Equal(t, float32(10), res.Order.Items[0].Discounts[0].Amount)
|
||||||
|
assert.Equal(t, float64(9990), res.Order.Items[0].Prices[0].Price)
|
||||||
|
assert.Equal(t, float32(1), res.Order.Items[0].Prices[0].Quantity)
|
||||||
|
assert.Equal(t, float32(1), res.Order.Items[0].Quantity)
|
||||||
|
assert.Equal(t, "696999ed-bc8d-4d0f-9627-527acf7b1d57", res.Order.Items[0].Offer.XMLID)
|
||||||
|
|
||||||
|
assert.Equal(t, req.Order.PrivilegeType, res.Calculations[0].PrivilegeType)
|
||||||
|
assert.Equal(t, req.Bonuses, res.Calculations[0].Discount)
|
||||||
|
assert.Equal(t, float32(999), res.Calculations[0].CreditBonuses)
|
||||||
|
assert.Equal(t, float32(240), res.Calculations[0].MaxChargeBonuses)
|
||||||
|
|
||||||
|
assert.Equal(t, float32(240), res.Calculations[0].MaxChargeBonuses)
|
||||||
|
assert.Equal(t, "Бонусная программа", res.Loyalty.Name)
|
||||||
|
assert.Equal(t, float32(1), res.Loyalty.ChargeRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_GetLoyalties(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
active := new(int)
|
||||||
|
*active = 1
|
||||||
|
|
||||||
|
req := LoyaltiesRequest{
|
||||||
|
Filter: LoyaltyApiFilter{
|
||||||
|
Active: active,
|
||||||
|
Ids: []int{2},
|
||||||
|
Sites: []string{"main"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
gock.New(crmURL).
|
||||||
|
Get(prefix + "/loyalty/loyalties").
|
||||||
|
MatchParams(map[string]string{
|
||||||
|
"filter[active]": "1",
|
||||||
|
"filter[sites][]": "main",
|
||||||
|
"filter[ids][]": "2",
|
||||||
|
}).
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(getLoyaltiesResponse())
|
||||||
|
|
||||||
|
res, status, err := client().GetLoyalties(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !statuses[status] {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Success != true {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotEmpty(t, res.Pagination)
|
||||||
|
assert.True(t, res.Loyalties[0].Active)
|
||||||
|
assert.False(t, res.Loyalties[0].Blocked)
|
||||||
|
assert.Equal(t, 2, res.Loyalties[0].ID)
|
||||||
|
assert.Equal(t, "Бонусная программа", res.Loyalties[0].Name)
|
||||||
|
assert.False(t, res.Loyalties[0].ConfirmSmsCharge)
|
||||||
|
assert.False(t, res.Loyalties[0].ConfirmSmsCharge)
|
||||||
|
assert.NotEmpty(t, res.Loyalties[0].CreatedAt)
|
||||||
|
assert.NotEmpty(t, res.Loyalties[0].ActivatedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_GetLoyaltyById(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
gock.New(crmURL).
|
||||||
|
Get(prefix + "/loyalty/loyalties/2").
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(getLoyaltyResponse())
|
||||||
|
|
||||||
|
res, status, err := client().GetLoyaltyById(2)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !statuses[status] {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Success != true {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.True(t, res.Loyalty.Active)
|
||||||
|
assert.False(t, res.Loyalty.Blocked)
|
||||||
|
assert.Equal(t, 2, res.Loyalty.ID)
|
||||||
|
assert.Equal(t, 4, len(res.Loyalty.LoyaltyLevels))
|
||||||
|
assert.Equal(t, "Бонусная программа", res.Loyalty.Name)
|
||||||
|
assert.False(t, res.Loyalty.ConfirmSmsCharge)
|
||||||
|
assert.False(t, res.Loyalty.ConfirmSmsCharge)
|
||||||
|
assert.NotEmpty(t, res.Loyalty.CreatedAt)
|
||||||
|
assert.NotEmpty(t, res.Loyalty.ActivatedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_OrderIntegrationDeliveryCancel(t *testing.T) {
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
gock.New(crmURL).
|
||||||
|
Post(prefix + "/orders/123/delivery/cancel").
|
||||||
|
MatchParams(map[string]string{
|
||||||
|
"by": "id",
|
||||||
|
"force": "true",
|
||||||
|
}).
|
||||||
|
Reply(http.StatusOK).
|
||||||
|
JSON(`{"success":true}`)
|
||||||
|
|
||||||
|
res, status, err := client().OrderIntegrationDeliveryCancel("id", true, "123")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !statuses[status] {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Success != true {
|
||||||
|
t.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
34
filters.go
34
filters.go
@ -439,3 +439,37 @@ type AccountBonusOperationsFilter struct {
|
|||||||
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
|
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
|
||||||
CreatedAtTo string `url:"createdAtTo,omitempty"`
|
CreatedAtTo string `url:"createdAtTo,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LoyaltyBonusApiFilterType struct {
|
||||||
|
Date string `url:"date,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoyaltyAccountApiFilter struct {
|
||||||
|
ID string `url:"id,omitempty"`
|
||||||
|
Status string `url:"status,,omitempty"`
|
||||||
|
Customer string `url:"customer,omitempty"`
|
||||||
|
MinOrderSum string `url:"minOrderSum,omitempty"`
|
||||||
|
MaxOrderSum string `url:"maxOrderSum,omitempty"`
|
||||||
|
MinAmount string `url:"minAmount,omitempty"`
|
||||||
|
MaxAmount string `url:"maxAmount,omitempty"`
|
||||||
|
PhoneNumber string `url:"phoneNumber,omitempty"`
|
||||||
|
CardNumber string `url:"cardNumber,omitempty"`
|
||||||
|
Ids []int `url:"ids,omitempty,brackets"`
|
||||||
|
Loyalties []int `url:"loyalties,omitempty,brackets"`
|
||||||
|
Sites []string `url:"sites,omitempty,brackets"`
|
||||||
|
Level int `url:"level,omitempty"`
|
||||||
|
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
|
||||||
|
CreatedAtTo string `url:"createdAtTo,omitempty"`
|
||||||
|
BurnDateFrom string `url:"burnDateFrom,omitempty"`
|
||||||
|
BurnDateTo string `url:"burnDateTo,omitempty"`
|
||||||
|
CustomFields []string `url:"customFields,omitempty,brackets"`
|
||||||
|
CustomerId string `url:"customerId,omitempty"`
|
||||||
|
CustomerExternalId string `url:"customerExternalId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoyaltyApiFilter struct {
|
||||||
|
Active *int `url:"active,omitempty"`
|
||||||
|
Blocked *int `url:"blocked,omitempty"`
|
||||||
|
Ids []int `url:"ids,omitempty,brackets"`
|
||||||
|
Sites []string `url:"sites,omitempty,brackets"`
|
||||||
|
}
|
||||||
|
12
request.go
12
request.go
@ -266,9 +266,15 @@ type LoyaltyAccountsRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoyaltyCalculateRequest struct {
|
type LoyaltyCalculateRequest struct {
|
||||||
Site string `url:"site"`
|
Site string
|
||||||
Order Order `url:"order"`
|
Order Order
|
||||||
Bonuses float64 `url:"bonuses"`
|
Bonuses float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoyaltiesRequest struct {
|
||||||
|
Limit int `url:"limit,omitempty"`
|
||||||
|
Page int `url:"page,omitempty"`
|
||||||
|
Filter LoyaltyApiFilter `url:"filter,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemURL returns system URL from the connection request without trailing slash.
|
// SystemURL returns system URL from the connection request without trailing slash.
|
||||||
|
15
response.go
15
response.go
@ -623,7 +623,7 @@ type LoyaltyBonusStatisticResponse struct {
|
|||||||
|
|
||||||
type LoyaltyAccountsResponse struct {
|
type LoyaltyAccountsResponse struct {
|
||||||
SuccessfulResponse
|
SuccessfulResponse
|
||||||
Pagination *Pagination `json:"pagination,omitempty"`
|
Pagination *Pagination `json:"pagination"`
|
||||||
LoyaltyAccounts []LoyaltyAccount `json:"loyaltyAccounts,omitempty"`
|
LoyaltyAccounts []LoyaltyAccount `json:"loyaltyAccounts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,5 +631,16 @@ type LoyaltyCalculateResponse struct {
|
|||||||
SuccessfulResponse
|
SuccessfulResponse
|
||||||
Order SerializedLoyaltyOrder `json:"order,omitempty"`
|
Order SerializedLoyaltyOrder `json:"order,omitempty"`
|
||||||
Calculations []LoyaltyCalculation `json:"calculations,omitempty"`
|
Calculations []LoyaltyCalculation `json:"calculations,omitempty"`
|
||||||
Loyalty Loyalty `json:"loyalty,omitempty"`
|
Loyalty SerializedLoyalty `json:"loyalty,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoyaltiesResponse struct {
|
||||||
|
SuccessfulResponse
|
||||||
|
Pagination *Pagination `json:"pagination"`
|
||||||
|
Loyalties []Loyalty `json:"loyalties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoyaltyResponse struct {
|
||||||
|
SuccessfulResponse
|
||||||
|
Loyalty Loyalty `json:"loyalty"`
|
||||||
}
|
}
|
||||||
|
204
testutils.go
204
testutils.go
@ -220,3 +220,207 @@ func getLoyaltyAccountsResponse() string {
|
|||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLoyaltyCalculateReq() LoyaltyCalculateRequest {
|
||||||
|
return LoyaltyCalculateRequest{
|
||||||
|
Site: "main",
|
||||||
|
Order: Order{
|
||||||
|
PrivilegeType: "loyalty_level",
|
||||||
|
Customer: &Customer{
|
||||||
|
ID: 123,
|
||||||
|
},
|
||||||
|
Items: []OrderItem{
|
||||||
|
{
|
||||||
|
InitialPrice: 10000,
|
||||||
|
Quantity: 1,
|
||||||
|
Offer: Offer{ID: 214},
|
||||||
|
PriceType: &PriceType{Code: "base"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Bonuses: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoyaltyCalculateResponse() string {
|
||||||
|
return `{
|
||||||
|
"success": true,
|
||||||
|
"order": {
|
||||||
|
"bonusesCreditTotal": 999,
|
||||||
|
"bonusesChargeTotal": 10,
|
||||||
|
"privilegeType": "loyalty_level",
|
||||||
|
"totalSumm": 9990,
|
||||||
|
"loyaltyAccount": {
|
||||||
|
"id": 13,
|
||||||
|
"amount": 240
|
||||||
|
},
|
||||||
|
"loyaltyLevel": {
|
||||||
|
"id": 6,
|
||||||
|
"name": "Любитель"
|
||||||
|
},
|
||||||
|
"customer": {
|
||||||
|
"id": 123,
|
||||||
|
"personalDiscount": 0
|
||||||
|
},
|
||||||
|
"delivery": {
|
||||||
|
"cost": 0
|
||||||
|
},
|
||||||
|
"site": "main",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"bonusesChargeTotal": 10,
|
||||||
|
"bonusesCreditTotal": 999,
|
||||||
|
"priceType": {
|
||||||
|
"code": "base"
|
||||||
|
},
|
||||||
|
"initialPrice": 10000,
|
||||||
|
"discounts": [
|
||||||
|
{
|
||||||
|
"type": "bonus_charge",
|
||||||
|
"amount": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"discountTotal": 10,
|
||||||
|
"prices": [
|
||||||
|
{
|
||||||
|
"price": 9990,
|
||||||
|
"quantity": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quantity": 1,
|
||||||
|
"offer": {
|
||||||
|
"xmlId": "696999ed-bc8d-4d0f-9627-527acf7b1d57"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"calculations": [
|
||||||
|
{
|
||||||
|
"privilegeType": "loyalty_level",
|
||||||
|
"discount": 10,
|
||||||
|
"creditBonuses": 999,
|
||||||
|
"maxChargeBonuses": 240,
|
||||||
|
"maximum": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"privilegeType": "none",
|
||||||
|
"discount": 10,
|
||||||
|
"creditBonuses": 0,
|
||||||
|
"maxChargeBonuses": 240,
|
||||||
|
"maximum": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"loyalty": {
|
||||||
|
"name": "Бонусная программа",
|
||||||
|
"chargeRate": 1
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoyaltiesResponse() string {
|
||||||
|
return `{
|
||||||
|
"success": true,
|
||||||
|
"pagination": {
|
||||||
|
"limit": 20,
|
||||||
|
"totalCount": 1,
|
||||||
|
"currentPage": 1,
|
||||||
|
"totalPageCount": 1
|
||||||
|
},
|
||||||
|
"loyalties": [
|
||||||
|
{
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 5,
|
||||||
|
"name": "Новичок",
|
||||||
|
"sum": 0,
|
||||||
|
"privilegeSize": 5,
|
||||||
|
"privilegeSizePromo": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 6,
|
||||||
|
"name": "Любитель",
|
||||||
|
"sum": 10000,
|
||||||
|
"privilegeSize": 10,
|
||||||
|
"privilegeSizePromo": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 7,
|
||||||
|
"name": "Продвинутый покупатель",
|
||||||
|
"sum": 25000,
|
||||||
|
"privilegeSize": 15,
|
||||||
|
"privilegeSizePromo": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 8,
|
||||||
|
"name": "Мастер шоппинга",
|
||||||
|
"sum": 50000,
|
||||||
|
"privilegeSize": 20,
|
||||||
|
"privilegeSizePromo": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active": true,
|
||||||
|
"blocked": false,
|
||||||
|
"id": 2,
|
||||||
|
"name": "Бонусная программа",
|
||||||
|
"confirmSmsCharge": false,
|
||||||
|
"confirmSmsRegistration": false,
|
||||||
|
"createdAt": "2022-01-18 15:40:22",
|
||||||
|
"activatedAt": "2022-12-08 12:05:45"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoyaltyResponse() string {
|
||||||
|
return `{
|
||||||
|
"success": true,
|
||||||
|
"loyalty": {
|
||||||
|
"levels": [
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 5,
|
||||||
|
"name": "Новичок",
|
||||||
|
"sum": 0,
|
||||||
|
"privilegeSize": 5,
|
||||||
|
"privilegeSizePromo": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 6,
|
||||||
|
"name": "Любитель",
|
||||||
|
"sum": 10000,
|
||||||
|
"privilegeSize": 10,
|
||||||
|
"privilegeSizePromo": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 7,
|
||||||
|
"name": "Продвинутый покупатель",
|
||||||
|
"sum": 25000,
|
||||||
|
"privilegeSize": 15,
|
||||||
|
"privilegeSizePromo": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bonus_percent",
|
||||||
|
"id": 8,
|
||||||
|
"name": "Мастер шоппинга",
|
||||||
|
"sum": 50000,
|
||||||
|
"privilegeSize": 20,
|
||||||
|
"privilegeSizePromo": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active": true,
|
||||||
|
"blocked": false,
|
||||||
|
"id": 2,
|
||||||
|
"name": "Бонусная программа",
|
||||||
|
"confirmSmsCharge": false,
|
||||||
|
"confirmSmsRegistration": false,
|
||||||
|
"createdAt": "2022-01-18 15:40:22",
|
||||||
|
"activatedAt": "2022-12-08 12:05:45"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
158
types.go
158
types.go
@ -329,6 +329,7 @@ type Order struct {
|
|||||||
CustomFields StringMap `json:"customFields,omitempty"`
|
CustomFields StringMap `json:"customFields,omitempty"`
|
||||||
Payments OrderPayments `json:"payments,omitempty"`
|
Payments OrderPayments `json:"payments,omitempty"`
|
||||||
ApplyRound *bool `json:"applyRound,omitempty"`
|
ApplyRound *bool `json:"applyRound,omitempty"`
|
||||||
|
PrivilegeType string `json:"privilegeType,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrdersStatus type.
|
// OrdersStatus type.
|
||||||
@ -774,17 +775,23 @@ type DeliveryService struct {
|
|||||||
|
|
||||||
// DeliveryType type.
|
// DeliveryType type.
|
||||||
type DeliveryType struct {
|
type DeliveryType struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Code string `json:"code,omitempty"`
|
Code string `json:"code,omitempty"`
|
||||||
Active bool `json:"active,omitempty"`
|
Active bool `json:"active,omitempty"`
|
||||||
DefaultCost float32 `json:"defaultCost,omitempty"`
|
DefaultCost float32 `json:"defaultCost,omitempty"`
|
||||||
DefaultNetCost float32 `json:"defaultNetCost,omitempty"`
|
DefaultNetCost float32 `json:"defaultNetCost,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
IntegrationCode string `json:"integrationCode,omitempty"`
|
IntegrationCode string `json:"integrationCode,omitempty"`
|
||||||
VatRate string `json:"vatRate,omitempty"`
|
VatRate string `json:"vatRate,omitempty"`
|
||||||
DefaultForCrm bool `json:"defaultForCrm,omitempty"`
|
DefaultForCrm bool `json:"defaultForCrm,omitempty"`
|
||||||
DeliveryServices []string `json:"deliveryServices,omitempty"`
|
DeliveryServices []string `json:"deliveryServices,omitempty"`
|
||||||
PaymentTypes []string `json:"paymentTypes,omitempty"`
|
PaymentTypes []string `json:"paymentTypes,omitempty"` //Deprecated, use DeliveryPaymentTypes
|
||||||
|
DeliveryPaymentTypes []DeliveryPaymentType `json:"deliveryPaymentTypes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeliveryPaymentType struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Cod bool `json:"cod,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LegalEntity type.
|
// LegalEntity type.
|
||||||
@ -904,21 +911,24 @@ type StatusGroup struct {
|
|||||||
|
|
||||||
// Site type.
|
// Site type.
|
||||||
type Site struct {
|
type Site struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Code string `json:"code,omitempty"`
|
Code string `json:"code,omitempty"`
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Phones string `json:"phones,omitempty"`
|
Phones string `json:"phones,omitempty"`
|
||||||
Zip string `json:"zip,omitempty"`
|
Zip string `json:"zip,omitempty"`
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
CountryIso string `json:"countryIso,omitempty"`
|
CountryIso string `json:"countryIso,omitempty"`
|
||||||
YmlURL string `json:"ymlUrl,omitempty"`
|
YmlURL string `json:"ymlUrl,omitempty"`
|
||||||
LoadFromYml bool `json:"loadFromYml,omitempty"`
|
LoadFromYml bool `json:"loadFromYml,omitempty"`
|
||||||
CatalogUpdatedAt string `json:"catalogUpdatedAt,omitempty"`
|
CatalogUpdatedAt string `json:"catalogUpdatedAt,omitempty"`
|
||||||
CatalogLoadingAt string `json:"catalogLoadingAt,omitempty"`
|
CatalogLoadingAt string `json:"catalogLoadingAt,omitempty"`
|
||||||
Contragent *LegalEntity `json:"contragent,omitempty"`
|
Contragent *LegalEntity `json:"contragent,omitempty"`
|
||||||
DefaultForCRM bool `json:"defaultForCrm,omitempty"`
|
DefaultForCRM bool `json:"defaultForCrm,omitempty"`
|
||||||
Ordering int `json:"ordering,omitempty"`
|
Ordering int `json:"ordering,omitempty"`
|
||||||
|
IsDemo bool `json:"isDemo,omitempty"`
|
||||||
|
CatalogId string `json:"catalogId,omitempty"`
|
||||||
|
IsCatalogMainSite bool `json:"isCatalogMainSite,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store type.
|
// Store type.
|
||||||
@ -1310,7 +1320,17 @@ type LoyaltyAccount struct {
|
|||||||
|
|
||||||
// Loyalty type.
|
// Loyalty type.
|
||||||
type Loyalty struct {
|
type Loyalty struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
|
LoyaltyLevels []LoyaltyLevel `json:"levels,omitempty"`
|
||||||
|
Active bool `json:"active,omitempty"`
|
||||||
|
Blocked bool `json:"blocked,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ConfirmSmsCharge bool `json:"confirmSmsCharge,omitempty"`
|
||||||
|
ConfirmSmsRegistration bool `json:"confirmSmsRegistration,omitempty"`
|
||||||
|
CreatedAt string `json:"createdAt,omitempty"`
|
||||||
|
ActivatedAt string `json:"activatedAt,omitempty"`
|
||||||
|
DeactivatedAt string `json:"deactivatedAt,omitempty"`
|
||||||
|
BlockedAt string `json:"blockedAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoyaltyLevel type.
|
// LoyaltyLevel type.
|
||||||
@ -1332,55 +1352,28 @@ type SmsVerification struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoyaltyBonus struct {
|
type LoyaltyBonus struct {
|
||||||
Amount float64 `json:"amount"`
|
Amount float32 `json:"amount"`
|
||||||
ActivationDate string `json:"activationDate"`
|
ActivationDate string `json:"activationDate"`
|
||||||
ExpiredDate string `json:"expiredDate,omitempty"`
|
ExpiredDate string `json:"expiredDate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BonusDetail struct {
|
type BonusDetail struct {
|
||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Amount float64 `json:"amount"`
|
Amount float32 `json:"amount"`
|
||||||
}
|
|
||||||
|
|
||||||
type LoyaltyBonusApiFilterType struct {
|
|
||||||
Date string `url:"date,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoyaltyAccountApiFilter struct {
|
|
||||||
ID string `url:"id,omitempty"`
|
|
||||||
Status string `url:"status,,omitempty"`
|
|
||||||
Customer string `url:"customer,omitempty"`
|
|
||||||
MinOrderSum string `url:"minOrderSum,omitempty"`
|
|
||||||
MaxOrderSum string `url:"maxOrderSum,omitempty"`
|
|
||||||
MinAmount string `url:"minAmount,omitempty"`
|
|
||||||
MaxAmount string `url:"maxAmount,omitempty"`
|
|
||||||
PhoneNumber string `url:"phoneNumber,omitempty"`
|
|
||||||
CardNumber string `url:"cardNumber,omitempty"`
|
|
||||||
Ids []int `url:"ids,omitempty,brackets"`
|
|
||||||
Loyalties []int `url:"loyalties,omitempty,brackets"`
|
|
||||||
Sites []string `url:"sites,omitempty,brackets"`
|
|
||||||
Level int `url:"level,omitempty"`
|
|
||||||
CreatedAtFrom string `url:"createdAtFrom,omitempty"`
|
|
||||||
CreatedAtTo string `url:"createdAtTo,omitempty"`
|
|
||||||
BurnDateFrom string `url:"burnDateFrom,omitempty"`
|
|
||||||
BurnDateTo string `url:"burnDateTo,omitempty"`
|
|
||||||
CustomFields []string `url:"customFields,omitempty,brackets"`
|
|
||||||
CustomerId string `url:"customerId,omitempty"`
|
|
||||||
CustomerExternalId string `url:"customerExternalId,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SerializedLoyaltyOrder struct {
|
type SerializedLoyaltyOrder struct {
|
||||||
BonusesCreditTotal float64 `json:"bonusesCreditTotal,omitempty"`
|
BonusesCreditTotal float32 `json:"bonusesCreditTotal,omitempty"`
|
||||||
BonusesChargeTotal float64 `json:"bonusesChargeTotal,omitempty"`
|
BonusesChargeTotal float32 `json:"bonusesChargeTotal,omitempty"`
|
||||||
PrivilegeType string `json:"privilegeType,omitempty"`
|
PrivilegeType string `json:"privilegeType,omitempty"`
|
||||||
TotalSumm float64 `json:"totalSumm,omitempty"`
|
TotalSumm float64 `json:"totalSumm,omitempty"`
|
||||||
PersonalDiscountPercent float64 `json:"personalDiscountPercent,omitempty"`
|
PersonalDiscountPercent float32 `json:"personalDiscountPercent,omitempty"`
|
||||||
LoyaltyAccount LoyaltyAccount `json:"loyaltyAccount"`
|
LoyaltyAccount LoyaltyAccount `json:"loyaltyAccount"`
|
||||||
LoyaltyEventDiscount LoyaltyEventDiscount `json:"loyaltyEventDiscount,omitempty"`
|
LoyaltyEventDiscount LoyaltyEventDiscount `json:"loyaltyEventDiscount,omitempty"`
|
||||||
Customer Customer `json:"customer"`
|
Customer Customer `json:"customer"`
|
||||||
Delivery Delivery `json:"delivery,omitempty"`
|
Delivery Delivery `json:"delivery,omitempty"`
|
||||||
Site string `json:"site,omitempty"`
|
Site string `json:"site,omitempty"`
|
||||||
Items []OrderItem `json:"items,omitempty"`
|
Items []LoyaltyItems `json:"items,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoyaltyEventDiscount struct {
|
type LoyaltyEventDiscount struct {
|
||||||
@ -1388,7 +1381,46 @@ type LoyaltyEventDiscount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoyaltyItems struct {
|
type LoyaltyItems struct {
|
||||||
BonusesChargeTotal float64 `json:"bonusesChargeTotal,omitempty"`
|
BonusesChargeTotal float32 `json:"bonusesChargeTotal,omitempty"`
|
||||||
BonusesCreditTotal float64 `json:"bonusesCreditTotal,omitempty"`
|
BonusesCreditTotal float32 `json:"bonusesCreditTotal,omitempty"`
|
||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
|
ExternalIds []CodeValueModel `json:"externalIds,omitempty"`
|
||||||
|
PriceType PriceType `json:"priceType,omitempty"`
|
||||||
|
InitialPrice float32 `json:"initialPrice,omitempty"`
|
||||||
|
Discounts []AbstractDiscount `json:"discounts,omitempty"`
|
||||||
|
Prices []OrderProductPriceItem `json:"prices,omitempty"`
|
||||||
|
VatRate string `json:"vatRate,omitempty"`
|
||||||
|
CreatedAt string `json:"createdAt"`
|
||||||
|
Quantity float32 `json:"quantity"`
|
||||||
|
Offer Offer `json:"offer,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeValueModel struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AbstractDiscount struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Amount float32 `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderProductPriceItem struct {
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
Quantity float32 `json:"quantity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoyaltyCalculation struct {
|
||||||
|
PrivilegeType string `json:"privilegeType"`
|
||||||
|
Discount float32 `json:"discount"`
|
||||||
|
CreditBonuses float32 `json:"creditBonuses"`
|
||||||
|
LoyaltyEventDiscount LoyaltyEventDiscount `json:"loyaltyEventDiscount,omitempty"`
|
||||||
|
MaxChargeBonuses float32 `json:"maxChargeBonuses,omitempty"`
|
||||||
|
Maximum *bool `json:"maximum,omitempty"`
|
||||||
|
Loyalty SerializedLoyalty `json:"loyalty,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SerializedLoyalty struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ChargeRate float32 `json:"chargeRate"`
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user