From b9254d12e305d6a2d9e41ef446590c435d5a5b97 Mon Sep 17 00:00:00 2001 From: Alex Lushpai Date: Mon, 4 May 2026 13:15:47 +0300 Subject: [PATCH] Delivery calculate method --- client.go | 58 ++++++++++++++++++++++++++ client_test.go | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ request.go | 6 +++ response.go | 6 +++ types.go | 34 ++++++++++++++++ 5 files changed, 212 insertions(+) diff --git a/client.go b/client.go index bd74793..8778bf1 100644 --- a/client.go +++ b/client.go @@ -2547,6 +2547,64 @@ func (c *Client) DeliveryShipments(parameters DeliveryShipmentsRequest) (Deliver return resp, status, nil } +// DeliveryCalculate calculates delivery cost +// +// For more information see https://docs.retailcrm.ru/Developers/API/APIMethods#post--api-v5-delivery-calculate +// +// Example: +// +// var client = retailcrm.New("https://demo.url", "09jIJ") +// +// data, status, err := client.DeliveryCalculate(retailcrm.DeliveryCalculateRequest{ +// DeliveryTypeCodes: []string{"courier"}, +// Order: retailcrm.DeliveryCalculateOrder{ +// Weight: 1200, +// Delivery: &retailcrm.DeliveryCalculateSerializedDelivery{ +// Address: &retailcrm.Address{ +// City: "Москва", +// Building: "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.Calculations { +// log.Printf("%v\n", value) +// } +func (c *Client) DeliveryCalculate(parameters DeliveryCalculateRequest) (DeliveryCalculateResponse, int, error) { + var resp DeliveryCalculateResponse + + orderJSON, err := marshalToString(¶meters.Order) + if err != nil { + return resp, 0, err + } + + p := url.Values{ + "deliveryTypeCodes[]": parameters.DeliveryTypeCodes, + "order": {orderJSON}, + } + + data, status, err := c.PostRequest("/delivery/calculate", p) + if err != nil { + return resp, status, err + } + + err = json.Unmarshal(data, &resp) + if err != nil { + return resp, status, err + } + + return resp, status, nil +} + // DeliveryShipmentCreate creates shipment // // For more information see http://www.simla.com/docs/Developers/API/APIVersions/APIv5#post--api-v5-delivery-shipments-create diff --git a/client_test.go b/client_test.go index f72cef2..3d47d33 100644 --- a/client_test.go +++ b/client_test.go @@ -6797,6 +6797,114 @@ func TestClient_DeliveryShipments_Fail(t *testing.T) { } } +func TestClient_DeliveryCalculate(t *testing.T) { + defer gock.Off() + + req := DeliveryCalculateRequest{ + DeliveryTypeCodes: []string{"courier", "pickup"}, + Order: DeliveryCalculateOrder{ + Weight: 1.2, + Length: 10, + Width: 20, + Height: 30, + Items: []DeliveryCalculateOrderProduct{ + { + InitialPrice: 1000, + DiscountManualAmount: 100, + DiscountManualPercent: 5, + Quantity: 2, + }, + }, + Delivery: &DeliveryCalculateSerializedDelivery{ + Date: "2024-01-02", + Time: &DeliveryTime{ + From: "10:00", + To: "18:00", + }, + Address: &Address{ + City: "Москва", + Street: "Ленина", + Building: "1", + }, + }, + }, + } + + orderJSON, err := json.Marshal(req.Order) + assert.NoError(t, err) + + p := url.Values{ + "deliveryTypeCodes[]": req.DeliveryTypeCodes, + "order": {string(orderJSON)}, + } + + gock.New(crmURL). + Post(prefix + "/delivery/calculate"). + BodyString(p.Encode()). + Reply(http.StatusOK). + BodyString(`{"success": true, "calculations": [{"code": "courier", "available": true, "vatRate": "20", "cost": 350.5}]}`) + + res, status, err := client().DeliveryCalculate(req) + + if err != nil { + t.Errorf("%v", err) + } + + if !statuses[status] { + t.Errorf("%v", err) + } + + assert.True(t, res.Success) + assert.Len(t, res.Calculations, 1) + assert.Equal(t, "courier", res.Calculations[0].Code) + assert.True(t, res.Calculations[0].Available) + assert.Equal(t, "20", res.Calculations[0].VatRate) + assert.Equal(t, float32(350.5), res.Calculations[0].Cost) +} + +func TestClient_DeliveryCalculate_Fail(t *testing.T) { + defer gock.Off() + + req := DeliveryCalculateRequest{ + DeliveryTypeCodes: []string{codeFail}, + Order: DeliveryCalculateOrder{ + Delivery: &DeliveryCalculateSerializedDelivery{ + Address: &Address{ + City: "Москва", + }, + }, + }, + } + + orderJSON, err := json.Marshal(req.Order) + assert.NoError(t, err) + + p := url.Values{ + "deliveryTypeCodes[]": req.DeliveryTypeCodes, + "order": {string(orderJSON)}, + } + + gock.New(crmURL). + Post(prefix + "/delivery/calculate"). + BodyString(p.Encode()). + Reply(http.StatusBadRequest). + BodyString(`{"success": false, "errorMsg": "Errors in the input parameters"}`) + + res, status, err := client().DeliveryCalculate(req) + + if err == nil { + t.Error("Error must be return") + } + + if status < http.StatusBadRequest { + t.Error(statusFail) + } + + if res.Success != false { + t.Error(successFail) + } +} + func TestClient_Cost(t *testing.T) { c := client() diff --git a/request.go b/request.go index ee04741..ed55dc8 100644 --- a/request.go +++ b/request.go @@ -194,6 +194,12 @@ type DeliveryShipmentsRequest struct { Page int `url:"page,omitempty"` } +// DeliveryCalculateRequest type. +type DeliveryCalculateRequest struct { + DeliveryTypeCodes []string + Order DeliveryCalculateOrder +} + // ClearCartRequest type. type ClearCartRequest struct { ClearedAt string `json:"clearedAt,omitempty"` diff --git a/response.go b/response.go index 23731d8..1080ad4 100644 --- a/response.go +++ b/response.go @@ -426,6 +426,12 @@ type DeliveryShipmentsResponse struct { DeliveryShipments []DeliveryShipment `json:"deliveryShipments,omitempty"` } +// DeliveryCalculateResponse type. +type DeliveryCalculateResponse struct { + SuccessfulResponse + Calculations []DeliveryCalculation `json:"calculations,omitempty"` +} + // DeliveryShipmentResponse type. type DeliveryShipmentResponse struct { Success bool `json:"success"` diff --git a/types.go b/types.go index 1f3c69f..f78eb8e 100644 --- a/types.go +++ b/types.go @@ -504,6 +504,40 @@ type OrderDeliveryData struct { AdditionalFields map[string]interface{} } +// DeliveryCalculateOrder type. +type DeliveryCalculateOrder struct { + Weight float32 `json:"weight,omitempty"` + Length int `json:"length,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + Items []DeliveryCalculateOrderProduct `json:"items,omitempty"` + Delivery *DeliveryCalculateSerializedDelivery `json:"delivery,omitempty"` +} + +// DeliveryCalculateOrderProduct type. +type DeliveryCalculateOrderProduct struct { + InitialPrice float32 `json:"initialPrice,omitempty"` + DiscountManualAmount float32 `json:"discountManualAmount,omitempty"` + DiscountManualPercent float32 `json:"discountManualPercent,omitempty"` + Quantity float32 `json:"quantity,omitempty"` +} + +// DeliveryCalculateSerializedDelivery type. +type DeliveryCalculateSerializedDelivery struct { + Date string `json:"date,omitempty"` + Time *DeliveryTime `json:"time,omitempty"` + Address *Address `json:"address,omitempty"` +} + +// DeliveryCalculation type. +type DeliveryCalculation struct { + Code string `json:"code,omitempty"` + Available bool `json:"available,omitempty"` + VatRate string `json:"vatRate,omitempty"` + Cost float32 `json:"cost,omitempty"` + Description string `json:"description,omitempty"` +} + // SetCartItem type. type SetCartItem struct { Quantity float64 `json:"quantity,omitempty"`