diff --git a/.gitignore b/.gitignore index c3ebba8..2650cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,10 +21,12 @@ _testmain.go *.exe *.test *.prof +coverage.txt # IDE's files .idea *.iml .env +.swp # Project ignores diff --git a/.travis.yml b/.travis.yml index 5d551ea..62acb07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,12 @@ go: - '1.9' - '1.10' - '1.11' + - '1.12' + - '1.13' before_install: - go get -v github.com/google/go-querystring/query - go get -v github.com/h2non/gock - cp .env.dist .env -script: go test -v ./... +script: ./go.test.sh +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 1549bf8..5376d63 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build Status](https://img.shields.io/travis/retailcrm/api-client-go/master.svg?logo=travis&style=flat-square)](https://travis-ci.org/retailcrm/api-client-go) [![GitHub release](https://img.shields.io/github/release/retailcrm/api-client-go.svg?style=flat-square)](https://github.com/retailcrm/api-client-go/releases) -[![GoLang version](https://img.shields.io/badge/go-1.8%2C%201.9%2C%201.10-blue.svg?style=flat-square)](https://golang.org/dl/) +[![GoLang version](https://img.shields.io/badge/go-1.8%20--%201.13-blue?style=flat-square)](https://golang.org/dl/) [![Godoc reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/retailcrm/api-client-go) diff --git a/go.test.sh b/go.test.sh new file mode 100755 index 0000000..d153907 --- /dev/null +++ b/go.test.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e +export DEVELOPER_NODE=1 +export RETAILCRM_URL=https://test.retailcrm.pro +export RETAILCRM_KEY=key +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done \ No newline at end of file diff --git a/v5/client.go b/v5/client.go index 36cdfa9..a80a4ad 100644 --- a/v5/client.go +++ b/v5/client.go @@ -165,10 +165,10 @@ func buildErr(data []byte, err *errs.Failure) { // checkBy select identifier type func checkBy(by string) string { - var context = "id" + var context = ByID - if by != "id" { - context = "externalId" + if by != ByID { + context = ByExternalID } return context @@ -705,7 +705,7 @@ func (c *Client) CustomersUpload(customers []Customer, site ...string) (Customer // // var client = v5.New("https://demo.url", "09jIJ") // -// data, status, err := client.Customer(12, "externalId", "") +// data, status, err := client.Customer(12, v5.ByExternalID, "") // // if err.Error() != "" { // fmt.Printf("%v", err.RuntimeErr) @@ -756,7 +756,7 @@ func (c *Client) Customer(id, by, site string) (CustomerResponse, int, *errs.Fai // ID: 1, // Email: "ivanov@example.com", // }, -// "id", +// v5.ByID, // ) // // if err.Error() != "" { @@ -775,7 +775,7 @@ func (c *Client) CustomerEdit(customer Customer, by string, site ...string) (Cus var uid = strconv.Itoa(customer.ID) var context = checkBy(by) - if context == "externalId" { + if context == ByExternalID { uid = customer.ExternalID } @@ -803,6 +803,1002 @@ func (c *Client) CustomerEdit(customer Customer, by string, site ...string) (Cus return resp, status, nil } +// CorporateCustomers returns list of corporate customers matched the specified filter +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#get--api-v5-customers-corporate +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomers(v5.CorporateCustomersRequest{ +// Filter: CorporateCustomersFilter{ +// City: "Moscow", +// }, +// Page: 3, +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// for _, value := range data.CustomersCorporate { +// fmt.Printf("%v\n", value) +// } +func (c *Client) CorporateCustomers(parameters CorporateCustomersRequest) (CorporateCustomersResponse, int, *errs.Failure) { + var resp CorporateCustomersResponse + + params, _ := query.Values(parameters) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate?%s", params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerCreate creates corporate customer +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-create +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerCreate(v5.CorporateCustomer{ +// Nickname: "Company", +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v", err.Id) +// } +func (c *Client) CorporateCustomerCreate(customer CorporateCustomer, site ...string) (CorporateCustomerChangeResponse, int, *errs.Failure) { + var resp CorporateCustomerChangeResponse + + customerJSON, _ := json.Marshal(&customer) + + p := url.Values{ + "customerCorporate": {string(customerJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest("/customers-corporate/create", p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomersFixExternalIds will fix corporate customers external ID's +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-fix-external-ids +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomersFixExternalIds([]v5.IdentifiersPair{{ +// ID: 1, +// ExternalID: 12, +// }}) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +func (c *Client) CorporateCustomersFixExternalIds(customers []IdentifiersPair) (SuccessfulResponse, int, *errs.Failure) { + var resp SuccessfulResponse + + customersJSON, _ := json.Marshal(&customers) + + p := url.Values{ + "customersCorporate": {string(customersJSON[:])}, + } + + data, status, err := c.PostRequest("/customers-corporate/fix-external-ids", p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomersHistory returns corporate customer's history +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-fix-external-ids +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomersHistory(v5.CorporateCustomersHistoryRequest{ +// Filter: v5.CorporateCustomersHistoryFilter{ +// SinceID: 20, +// }, +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// for _, value := range data.History { +// fmt.Printf("%v\n", value) +// } +func (c *Client) CorporateCustomersHistory(parameters CorporateCustomersHistoryRequest) (CorporateCustomersHistoryResponse, int, *errs.Failure) { + var resp CorporateCustomersHistoryResponse + + params, _ := query.Values(parameters) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate/history?%s", params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomers returns list of corporate customers matched the specified filter +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#get--api-v5-customers-corporate-notes +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomersNotes(v5.CorporateCustomersNotesRequest{ +// Filter: CorporateCustomersNotesFilter{ +// Text: "text", +// }, +// Page: 3, +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// for _, value := range data.Notes { +// fmt.Printf("%v\n", value) +// } +func (c *Client) CorporateCustomersNotes(parameters CorporateCustomersNotesRequest) (CorporateCustomersNotesResponse, int, *errs.Failure) { + var resp CorporateCustomersNotesResponse + + params, _ := query.Values(parameters) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate/notes?%s", params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerNoteCreate creates corporate customer note +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-notes-create +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerNoteCreate(v5.CorporateCustomerNote{ +// Text: "text", +// Customer: &v5.IdentifiersPair{ +// ID: 1, +// } +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v", err.Id) +// } +func (c *Client) CorporateCustomerNoteCreate(note CorporateCustomerNote, site ...string) (CreateResponse, int, *errs.Failure) { + var resp CreateResponse + + noteJSON, _ := json.Marshal(¬e) + + p := url.Values{ + "note": {string(noteJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest("/customers-corporate/notes/create", p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerNoteDelete removes note from corporate customer +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-notes-id-delete +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerNoteDelete(12) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +func (c *Client) CorporateCustomerNoteDelete(id int) (SuccessfulResponse, int, *errs.Failure) { + var resp SuccessfulResponse + + p := url.Values{ + "id": {string(id)}, + } + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/notes/%d/delete", id), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomersUpload corporate customers batch upload +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-upload +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomersUpload([]v5.CorporateCustomer{ +// { +// Nickname: "Company", +// ExternalID: 1, +// }, +// { +// Nickname: "Company 2", +// ExternalID: 2, +// }, +// }} +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.UploadedCustomers) +// } +func (c *Client) CorporateCustomersUpload(customers []CorporateCustomer, site ...string) (CorporateCustomersUploadResponse, int, *errs.Failure) { + var resp CorporateCustomersUploadResponse + + uploadJSON, _ := json.Marshal(&customers) + + p := url.Values{ + "customersCorporate": {string(uploadJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest("/customers-corporate/upload", p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomer returns information about corporate customer +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#get--api-v5-customers-corporate-externalId +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomer(12, v5.ByExternalID, "") +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.CorporateCustomer) +// } +func (c *Client) CorporateCustomer(id, by, site string) (CorporateCustomerResponse, int, *errs.Failure) { + var resp CorporateCustomerResponse + var context = checkBy(by) + + fw := CustomerRequest{context, site} + params, _ := query.Values(fw) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate/%s?%s", id, params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerAddresses returns information about corporate customer addresses +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#get--api-v5-customers-corporate-externalId-addresses +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerAddresses("ext-id", v5.CorporateCustomerAddressesRequest{ +// Filter: v5,CorporateCustomerAddressesFilter{ +// Name: "Main Address", +// }, +// By: v5.ByExternalID, +// Site: "site", +// Limit: 20, +// Page: 1, +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.Addresses) +// } +func (c *Client) CorporateCustomerAddresses(id string, parameters CorporateCustomerAddressesRequest) (CorporateCustomersAddressesResponse, int, *errs.Failure) { + var resp CorporateCustomersAddressesResponse + + parameters.By = checkBy(parameters.By) + params, _ := query.Values(parameters) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate/%s/addresses?%s", id, params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerAddressesCreate creates corporate customer address +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-addresses-create +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := c.CorporateCustomerAddressesCreate("ext-id", v5.ByExternalID, v5.CorporateCustomerAddress{ +// Text: "this is new address", +// Name: "New Address", +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v", data.ID) +// } +func (c *Client) CorporateCustomerAddressesCreate(id string, by string, address CorporateCustomerAddress, site ...string) (CreateResponse, int, *errs.Failure) { + var resp CreateResponse + + addressJSON, _ := json.Marshal(&address) + + p := url.Values{ + "address": {string(addressJSON[:])}, + "by": {string(checkBy(by))}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/addresses/create", id), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomersAddressesEdit edit exact corporate customer address +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-addresses-entityExternalId-edit +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := c.CorporateCustomerAddressesEdit( +// "customer-ext-id", +// v5.ByExternalID, +// v5.ByExternalID, +// CorporateCustomerAddress{ +// ExternalID: "addr-ext-id", +// Name: "Main Address 2", +// }, +// ) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.Customer) +// } +func (c *Client) CorporateCustomerAddressesEdit(customerID, customerBy, entityBy string, address CorporateCustomerAddress, site ...string) (CreateResponse, int, *errs.Failure) { + var ( + resp CreateResponse + uid string + ) + + customerBy = checkBy(customerBy) + entityBy = checkBy(entityBy) + + switch entityBy { + case "id": + uid = strconv.Itoa(address.ID) + case "externalId": + uid = address.ExternalID + } + + addressJSON, _ := json.Marshal(&address) + + p := url.Values{ + "by": {string(customerBy)}, + "entityBy": {string(entityBy)}, + "address": {string(addressJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/addresses/%s/edit", customerID, uid), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerCompanies returns information about corporate customer companies +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#get--api-v5-customers-corporate-externalId-companies +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerCompanies("ext-id", v5.IdentifiersPairRequest{ +// Filter: v5,IdentifiersPairFilter{ +// Ids: []string{"1"}, +// }, +// By: v5.ByExternalID, +// Site: "site", +// Limit: 20, +// Page: 1, +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.Companies) +// } +func (c *Client) CorporateCustomerCompanies(id string, parameters IdentifiersPairRequest) (CorporateCustomerCompaniesResponse, int, *errs.Failure) { + var resp CorporateCustomerCompaniesResponse + + parameters.By = checkBy(parameters.By) + params, _ := query.Values(parameters) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate/%s/companies?%s", id, params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerCompaniesCreate creates corporate customer company +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-companies-create +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := c.CorporateCustomerCompaniesCreate("ext-id", v5.ByExternalID, v5.Company{ +// Name: "Company name", +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v", data.ID) +// } +func (c *Client) CorporateCustomerCompaniesCreate(id string, by string, company Company, site ...string) (CreateResponse, int, *errs.Failure) { + var resp CreateResponse + + companyJSON, _ := json.Marshal(&company) + + p := url.Values{ + "company": {string(companyJSON[:])}, + "by": {string(checkBy(by))}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/companies/create", id), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerCompaniesEdit edit exact corporate customer company +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-companies-entityExternalId-edit +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := c.CorporateCustomerCompaniesEdit( +// "customer-ext-id", +// v5.ByExternalID, +// v5.ByExternalID, +// Company{ +// ExternalID: "company-ext-id", +// Name: "New Company Name", +// }, +// ) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.ID) +// } +func (c *Client) CorporateCustomerCompaniesEdit(customerID, customerBy, entityBy string, company Company, site ...string) (CreateResponse, int, *errs.Failure) { + var ( + resp CreateResponse + uid string + ) + + customerBy = checkBy(customerBy) + entityBy = checkBy(entityBy) + + switch entityBy { + case "id": + uid = strconv.Itoa(company.ID) + case "externalId": + uid = company.ExternalID + } + + addressJSON, _ := json.Marshal(&company) + + p := url.Values{ + "by": {string(customerBy)}, + "entityBy": {string(entityBy)}, + "company": {string(addressJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/companies/%s/edit", customerID, uid), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerContacts returns information about corporate customer contacts +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#get--api-v5-customers-corporate-externalId-contacts +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerContacts("ext-id", v5.IdentifiersPairRequest{ +// Filter: v5.IdentifiersPairFilter{ +// Ids: []string{"1"}, +// }, +// By: v5.ByExternalID, +// Site: "site", +// Limit: 20, +// Page: 1, +// }) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.Contacts) +// } +func (c *Client) CorporateCustomerContacts(id string, parameters IdentifiersPairRequest) (CorporateCustomerContactsResponse, int, *errs.Failure) { + var resp CorporateCustomerContactsResponse + + parameters.By = checkBy(parameters.By) + params, _ := query.Values(parameters) + + data, status, err := c.GetRequest(fmt.Sprintf("/customers-corporate/%s/contacts?%s", id, params.Encode())) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerContactsCreate creates corporate customer contact +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-contacts-create +// +// Example (customer with specified id or externalId should exist in specified site): +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := c.CorporateCustomerContactsCreate("ext-id", v5.ByExternalID, v5.CorporateCustomerContact{ +// IsMain: false, +// Customer: v5.CorporateCustomerContactCustomer{ +// ExternalID: "external_id", +// Site: "site", +// }, +// Companies: []IdentifiersPair{}, +// }, "site") +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v", data.ID) +// } +func (c *Client) CorporateCustomerContactsCreate(id string, by string, contact CorporateCustomerContact, site ...string) (CreateResponse, int, *errs.Failure) { + var resp CreateResponse + + companyJSON, _ := json.Marshal(&contact) + + p := url.Values{ + "contact": {string(companyJSON[:])}, + "by": {string(checkBy(by))}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/contacts/create", id), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerContactsEdit edit exact corporate customer contact +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-contacts-entityExternalId-edit +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := c.CorporateCustomerContactsEdit("ext-id", v5.ByExternalID, v5.ByID, v5.CorporateCustomerContact{ +// IsMain: false, +// Customer: v5.CorporateCustomerContactCustomer{ +// ID: 2350, +// }, +// }, "site") +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.ID) +// } +func (c *Client) CorporateCustomerContactsEdit(customerID, customerBy, entityBy string, contact CorporateCustomerContact, site ...string) (CreateResponse, int, *errs.Failure) { + var ( + resp CreateResponse + uid string + ) + + customerBy = checkBy(customerBy) + entityBy = checkBy(entityBy) + + switch entityBy { + case "id": + uid = strconv.Itoa(contact.Customer.ID) + case "externalId": + uid = contact.Customer.ExternalID + } + + addressJSON, _ := json.Marshal(&contact) + + p := url.Values{ + "by": {string(customerBy)}, + "entityBy": {string(entityBy)}, + "contact": {string(addressJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/contacts/%s/edit", customerID, uid), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + +// CorporateCustomerEdit edit exact corporate customer +// +// For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-externalId-edit +// +// Example: +// +// var client = v5.New("https://demo.url", "09jIJ") +// +// data, status, err := client.CorporateCustomerEdit( +// v5.CorporateCustomer{ +// FirstName: "Ivan", +// LastName: "Ivanov", +// Patronymic: "Ivanovich", +// ID: 1, +// Email: "ivanov@example.com", +// }, +// v5.ByID, +// ) +// +// if err.Error() != "" { +// fmt.Printf("%v", err.RuntimeErr) +// } +// +// if status >= http.StatusBadRequest { +// fmt.Printf("%v", err.ApiErr()) +// } +// +// if data.Success == true { +// fmt.Printf("%v\n", data.Customer) +// } +func (c *Client) CorporateCustomerEdit(customer CorporateCustomer, by string, site ...string) (CustomerChangeResponse, int, *errs.Failure) { + var resp CustomerChangeResponse + var uid = strconv.Itoa(customer.ID) + var context = checkBy(by) + + if context == ByExternalID { + uid = customer.ExternalID + } + + customerJSON, _ := json.Marshal(&customer) + + p := url.Values{ + "by": {string(context)}, + "customerCorporate": {string(customerJSON[:])}, + } + + fillSite(&p, site) + + data, status, err := c.PostRequest(fmt.Sprintf("/customers-corporate/%s/edit", uid), p) + if err.Error() != "" { + return resp, status, err + } + + json.Unmarshal(data, &resp) + + if resp.Success == false { + buildErr(data, err) + return resp, status, err + } + + return resp, status, nil +} + // DeliveryTracking updates tracking data // // For more information see http://www.retailcrm.pro/docs/Developers/ApiVersion5#post--api-v5-delivery-generic-subcode-tracking @@ -1471,7 +2467,7 @@ func (c *Client) OrderPaymentDelete(id int) (SuccessfulResponse, int, *errs.Fail // ID: 12, // Amount: 500, // }, -// "id", +// v5.ByID, // ) // // if err.Error() != "" { @@ -1486,7 +2482,7 @@ func (c *Client) OrderPaymentEdit(payment Payment, by string, site ...string) (S var uid = strconv.Itoa(payment.ID) var context = checkBy(by) - if context == "externalId" { + if context == ByExternalID { uid = payment.ExternalID } @@ -1583,7 +2579,7 @@ func (c *Client) OrdersUpload(orders []Order, site ...string) (OrdersUploadRespo // // var client = v5.New("https://demo.url", "09jIJ") // -// data, status, err := client.Order(12, "externalId", "") +// data, status, err := client.Order(12, v5.ByExternalID, "") // // if err.Error() != "" { // fmt.Printf("%v", err.RuntimeErr) @@ -1631,7 +2627,7 @@ func (c *Client) Order(id, by, site string) (OrderResponse, int, *errs.Failure) // ID: 12, // Items: []v5.OrderItem{{Offer: v5.Offer{ID: 13}, Quantity: 6}}, // }, -// "id", +// v5.ByID, // ) // // if err.Error() != "" { @@ -1646,7 +2642,7 @@ func (c *Client) OrderEdit(order Order, by string, site ...string) (CreateRespon var uid = strconv.Itoa(order.ID) var context = checkBy(by) - if context == "externalId" { + if context == ByExternalID { uid = order.ExternalID } diff --git a/v5/client_test.go b/v5/client_test.go index 8f7d2c2..9de366b 100644 --- a/v5/client_test.go +++ b/v5/client_test.go @@ -309,7 +309,7 @@ func TestClient_CustomerChange(t *testing.T) { str, _ = json.Marshal(f) p = url.Values{ - "by": {string("id")}, + "by": {string(ByID)}, "customer": {string(str)}, } @@ -320,7 +320,7 @@ func TestClient_CustomerChange(t *testing.T) { Reply(200). BodyString(`{"success": true}`) - ed, se, err := c.CustomerEdit(f, "id") + ed, se, err := c.CustomerEdit(f, ByID) if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -335,11 +335,11 @@ func TestClient_CustomerChange(t *testing.T) { gock.New(crmURL). Get(fmt.Sprintf("/api/v5/customers/%v", f.ExternalID)). - MatchParam("by", "externalId"). + MatchParam("by", ByExternalID). Reply(200). BodyString(`{"success": true}`) - data, status, err := c.Customer(f.ExternalID, "externalId", "") + data, status, err := c.Customer(f.ExternalID, ByExternalID, "") if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -394,7 +394,7 @@ func TestClient_CustomerChange_Fail(t *testing.T) { str, _ = json.Marshal(f) p = url.Values{ - "by": {string("id")}, + "by": {string(ByID)}, "customer": {string(str)}, } @@ -405,7 +405,7 @@ func TestClient_CustomerChange_Fail(t *testing.T) { Reply(404). BodyString(`{"success": false, "errorMsg": "Not found"}`) - ed, se, err := c.CustomerEdit(f, "id") + ed, se, err := c.CustomerEdit(f, ByID) if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -420,11 +420,11 @@ func TestClient_CustomerChange_Fail(t *testing.T) { gock.New(crmURL). Get(fmt.Sprintf("/api/v5/customers/%v", codeFail)). - MatchParam("by", "externalId"). + MatchParam("by", ByExternalID). Reply(404). BodyString(`{"success": false, "errorMsg": "Not found"}`) - data, status, err := c.Customer(codeFail, "externalId", "") + data, status, err := c.Customer(codeFail, ByExternalID, "") if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -721,6 +721,981 @@ func TestClient_CustomersHistory_Fail(t *testing.T) { } } +func TestClient_CorporateCustomersList(t *testing.T) { + defer gock.Off() + + gock.New(crmURL). + Get("/api/v5/customers-corporate"). + MatchParam("filter[city]", "Москва"). + MatchParam("page", "3"). + Reply(200). + BodyString(`{"success":true,"pagination":{"limit":20,"totalCount":1,"currentPage":3,"totalPageCount":1}}`) + + c := client() + + data, status, err := c.CorporateCustomers(CorporateCustomersRequest{ + Filter: CorporateCustomersFilter{ + City: "Москва", + }, + Page: 3, + }) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Logf("%v", err.ApiError()) + } + + if data.Success != true { + t.Logf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomersCreate(t *testing.T) { + defer gock.Off() + + gock.New(crmURL). + Post("/api/v5/customers-corporate/create"). + Reply(201). + BodyString(`{"success":true,"id":2344}`) + + c := client() + customer := CorporateCustomer{ + ExternalID: "ext-id", + Nickname: "Test Customer", + Vip: true, + Bad: false, + CustomFields: nil, + PersonalDiscount: 10, + DiscountCardNumber: "1234567890", + Source: &Source{ + Source: "source", + Medium: "medium", + Campaign: "campaign", + Keyword: "keyword", + Content: "content", + }, + Companies: []Company{ + { + IsMain: true, + ExternalID: "company-ext-id", + Active: true, + Name: "name", + Brand: "brand", + Site: "https://retailcrm.pro", + Contragent: &Contragent{ + ContragentType: "legal-entity", + LegalName: "Legal Name", + LegalAddress: "Legal Address", + INN: "000000000", + OKPO: "000000000", + KPP: "000000000", + OGRN: "000000000", + BIK: "000000000", + Bank: "bank", + BankAddress: "bankAddress", + CorrAccount: "corrAccount", + BankAccount: "bankAccount", + }, + Address: &IdentifiersPair{ + ID: 0, + ExternalID: "ext-addr-id", + }, + CustomFields: nil, + }, + }, + Addresses: []CorporateCustomerAddress{ + { + Index: "123456", + CountryISO: "RU", + Region: "Russia", + RegionID: 0, + City: "Moscow", + CityID: 0, + CityType: "city", + Street: "Pushkinskaya", + StreetID: 0, + StreetType: "street", + Building: "", + Flat: "", + IntercomCode: "", + Floor: 0, + Block: 0, + House: "", + Housing: "", + Metro: "", + Notes: "", + Text: "", + ExternalID: "ext-addr-id", + Name: "Main Address", + }, + }, + } + + data, status, err := c.CorporateCustomerCreate(customer, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status != http.StatusCreated { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomersFixExternalIds(t *testing.T) { + c := client() + + customers := []IdentifiersPair{{ + ID: 123, + ExternalID: RandomString(8), + }} + + defer gock.Off() + + jr, _ := json.Marshal(&customers) + + p := url.Values{ + "customersCorporate": {string(jr[:])}, + } + + gock.New(crmURL). + Post("/customers-corporate/fix-external-ids"). + MatchType("url"). + BodyString(p.Encode()). + Reply(200). + BodyString(`{"success": true}`) + + fx, fe, err := c.CorporateCustomersFixExternalIds(customers) + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if fe != http.StatusOK { + t.Errorf("%v", err.ApiError()) + } + + if fx.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomersFixExternalIds_Fail(t *testing.T) { + c := client() + + customers := []IdentifiersPair{{ExternalID: RandomString(8)}} + + defer gock.Off() + + jr, _ := json.Marshal(&customers) + + p := url.Values{ + "customersCorporate": {string(jr[:])}, + } + + gock.New(crmURL). + Post("/customers-corporate/fix-external-ids"). + MatchType("url"). + BodyString(p.Encode()). + Reply(400). + BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"id": "ID must be an integer"}}`) + + data, status, err := c.CorporateCustomersFixExternalIds(customers) + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status < http.StatusBadRequest { + t.Error(statusFail) + } + + if data.Success != false { + t.Error(successFail) + } +} + +func TestClient_CorporateCustomersHistory(t *testing.T) { + c := client() + f := CorporateCustomersHistoryRequest{ + Filter: CorporateCustomersHistoryFilter{ + SinceID: 20, + }, + } + defer gock.Off() + + gock.New(crmURL). + Get("/customers-corporate/history"). + MatchParam("filter[sinceId]", "20"). + Reply(200). + BodyString(`{"success": true, "history": [{"id": 1}]}`) + + data, status, err := c.CorporateCustomersHistory(f) + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if len(data.History) == 0 { + t.Errorf("%v", "Empty history") + } +} + +func TestClient_CorporateCustomersHistory_Fail(t *testing.T) { + c := client() + f := CorporateCustomersHistoryRequest{ + Filter: CorporateCustomersHistoryFilter{ + StartDate: "2020-13-12", + }, + } + + defer gock.Off() + + gock.New(crmURL). + Get("/customers-corporate/history"). + MatchParam("filter[startDate]", "2020-13-12"). + Reply(400). + BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"children[startDate]": "Значение недопустимо."}}`) + + data, status, err := c.CorporateCustomersHistory(f) + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status < http.StatusBadRequest { + t.Error(statusFail) + } + + if data.Success != false { + t.Error(successFail) + } +} + +func TestClient_CorporateCustomersNotes(t *testing.T) { + defer gock.Off() + + gock.New(crmURL). + Get("/api/v5/customers-corporate/notes"). + MatchParam("filter[text]", "sample"). + Reply(200). + BodyString(` + { + "success": true, + "pagination": { + "limit": 20, + "totalCount": 1, + "currentPage": 1, + "totalPageCount": 1 + }, + "notes": [ + { + "customer": { + "site": "site", + "id": 2346, + "externalId": "ext-id", + "type": "customer_corporate" + }, + "managerId": 24, + "id": 106, + "text": "
sample text
", + "createdAt": "2019-10-15 17:08:59" + } + ] + } + `) + + c := client() + + data, status, err := c.CorporateCustomersNotes(CorporateCustomersNotesRequest{ + Filter: CorporateCustomersNotesFilter{ + Text: "sample", + }, + }) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if data.Notes[0].Text != "sample text
" { + t.Errorf("invalid note text") + } +} + +func TestClient_CorporateCustomerNoteCreate(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/notes/create"). + Reply(201). + BodyString(`{"success":true,"id":1}`) + + data, status, err := c.CorporateCustomerNoteCreate(CorporateCustomerNote{ + Text: "another note", + Customer: &IdentifiersPair{ + ID: 1, + }, + }, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if data.ID != 1 { + t.Error("invalid note id") + } +} + +func TestClient_CorporateCustomerNoteDelete(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/notes/1/delete"). + Reply(200). + BodyString(`{"success":true}`) + + data, status, err := c.CorporateCustomerNoteDelete(1) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomersUpload(t *testing.T) { + c := client() + customers := make([]CorporateCustomer, 3) + + for i := range customers { + customers[i] = CorporateCustomer{ + Nickname: fmt.Sprintf("Name_%s", RandomString(8)), + ExternalID: RandomString(8), + } + } + + defer gock.Off() + + str, _ := json.Marshal(customers) + + p := url.Values{ + "customersCorporate": {string(str)}, + } + + gock.New(crmURL). + Post("/api/v5/customers-corporate/upload"). + MatchType("url"). + BodyString(p.Encode()). + Reply(200). + BodyString(`{"success": true}`) + + data, status, err := c.CorporateCustomersUpload(customers) + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomersUpload_Fail(t *testing.T) { + c := client() + + customers := []CorporateCustomer{{ExternalID: strconv.Itoa(iCodeFail)}} + + defer gock.Off() + + str, _ := json.Marshal(customers) + p := url.Values{ + "customersCorporate": {string(str)}, + } + + gock.New(crmURL). + Post("/api/v5/customers-corporate/upload"). + MatchType("url"). + BodyString(p.Encode()). + Reply(460). + BodyString(`{"success": false, "errorMsg": "Customers are loaded with errors"}`) + + data, status, err := c.CorporateCustomersUpload(customers) + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status < http.StatusBadRequest { + t.Error(statusFail) + } + + if data.Success != false { + t.Error(successFail) + } +} + +func TestClient_CorporateCustomer(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Get("/customers-corporate/ext-id"). + MatchParam("by", ByExternalID). + MatchParam("site", "site"). + Reply(200). + BodyString(` + { + "success": true, + "customerCorporate": { + "type": "customer_corporate", + "id": 2346, + "externalId": "ext-id", + "nickName": "Test Customer 2", + "mainAddress": { + "id": 2034, + "externalId": "ext-addr-id223", + "name": "Main Address" + }, + "createdAt": "2019-10-15 16:16:56", + "vip": false, + "bad": false, + "site": "site", + "marginSumm": 0, + "totalSumm": 0, + "averageSumm": 0, + "ordersCount": 0, + "costSumm": 0, + "customFields": [], + "personalDiscount": 10, + "mainCompany": { + "id": 26, + "externalId": "company-ext-id", + "name": "name" + } + } + } + `) + + data, status, err := c.CorporateCustomer("ext-id", ByExternalID, "site") + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomerAddresses(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Get("/customers-corporate/ext-id/addresses"). + MatchParams(map[string]string{ + "by": ByExternalID, + "filter[name]": "Main Address", + "limit": "20", + "page": "1", + "site": "site", + }). + Reply(200). + BodyString(` + { + "success": true, + "addresses": [ + { + "id": 2034, + "index": "123456", + "countryIso": "RU", + "region": "Russia", + "city": "Moscow", + "cityType": "city", + "street": "Pushkinskaya", + "streetType": "street", + "text": "street Pushkinskaya", + "externalId": "ext-addr-id223", + "name": "Main Address" + } + ] + } + `) + + data, status, err := c.CorporateCustomerAddresses("ext-id", CorporateCustomerAddressesRequest{ + Filter: CorporateCustomerAddressesFilter{ + Name: "Main Address", + }, + By: ByExternalID, + Site: "site", + Limit: 20, + Page: 1, + }) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if len(data.Addresses) == 0 { + t.Error("data.Addresses must not be empty") + } +} + +func TestClient_CorporateCustomerAddressesCreate(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/ext-id/addresses/create"). + Reply(201). + BodyString(`{"success":true,"id":1}`) + + data, status, err := c.CorporateCustomerAddressesCreate("ext-id", ByExternalID, CorporateCustomerAddress{ + Text: "this is new address", + Name: "New Address", + }, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomerAddressesEdit(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/customer-ext-id/addresses/addr-ext-id/edit"). + Reply(200). + BodyString(`{"success":true,"id":1}`) + + data, status, err := c.CorporateCustomerAddressesEdit( + "customer-ext-id", + ByExternalID, + ByExternalID, + CorporateCustomerAddress{ + ExternalID: "addr-ext-id", + Name: "Main Address 2", + }, + "site", + ) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomerCompanies(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Get("/customers-corporate/ext-id/companies"). + MatchParams(map[string]string{ + "by": ByExternalID, + "filter[ids][]": "1", + "limit": "20", + "page": "1", + "site": "site", + }). + Reply(200). + BodyString(` + { + "success": true, + "companies": [ + { + "isMain": true, + "id": 1, + "externalId": "company-ext-id", + "customer": { + "site": "site", + "id": 2346, + "externalId": "ext-id", + "type": "customer_corporate" + }, + "active": true, + "name": "name", + "brand": "brand", + "site": "https://retailcrm.pro", + "createdAt": "2019-10-15 16:16:56", + "contragent": { + "contragentType": "legal-entity", + "legalName": "Legal Name", + "legalAddress": "Legal Address", + "INN": "000000000", + "OKPO": "000000000", + "KPP": "000000000", + "OGRN": "000000000", + "BIK": "000000000", + "bank": "bank", + "bankAddress": "bankAddress", + "corrAccount": "corrAccount", + "bankAccount": "bankAccount" + }, + "address": { + "id": 2034, + "index": "123456", + "countryIso": "RU", + "region": "Russia", + "city": "Moscow", + "cityType": "city", + "street": "Pushkinskaya", + "streetType": "street", + "text": "street Pushkinskaya", + "externalId": "ext-addr-id", + "name": "Main Address 2" + }, + "marginSumm": 0, + "totalSumm": 0, + "averageSumm": 0, + "ordersCount": 0, + "costSumm": 0, + "customFields": [] + } + ] + } + `) + + data, status, err := c.CorporateCustomerCompanies("ext-id", IdentifiersPairRequest{ + Filter: IdentifiersPairFilter{ + Ids: []string{"1"}, + }, + By: ByExternalID, + Site: "site", + Limit: 20, + Page: 1, + }) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if len(data.Companies) == 0 { + t.Error("data.Companies must not be empty") + } +} + +func TestClient_CorporateCustomerCompaniesCreate(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/ext-id/companies/create"). + Reply(201). + BodyString(`{"success":true,"id":1}`) + + data, status, err := c.CorporateCustomerCompaniesCreate("ext-id", ByExternalID, Company{ + Name: "New Company", + }, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomerCompaniesEdit(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/customer-ext-id/companies/company-ext-id/edit"). + Reply(200). + BodyString(`{"success":true,"id":1}`) + + data, status, err := c.CorporateCustomerCompaniesEdit( + "customer-ext-id", + ByExternalID, + ByExternalID, + Company{ + ExternalID: "company-ext-id", + Name: "New Company Name 2", + }, + "site", + ) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + +func TestClient_CorporateCustomerContacts(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Get("/customers-corporate/ext-id/contacts"). + MatchParams(map[string]string{ + "by": ByExternalID, + "limit": "20", + "page": "1", + "site": "site", + }). + Reply(200). + BodyString(` + { + "success": true, + "contacts": [ + { + "isMain": false, + "customer": { + "id": 2347, + "site": "site" + }, + "companies": [] + } + ] + } + `) + + data, status, err := c.CorporateCustomerContacts("ext-id", IdentifiersPairRequest{ + Filter: IdentifiersPairFilter{}, + By: ByExternalID, + Site: "site", + Limit: 20, + Page: 1, + }) + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if len(data.Contacts) == 0 { + t.Error("data.Contacts must not be empty") + } +} + +func TestClient_CorporateCustomerContactsCreate(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers/create"). + Reply(201). + BodyString(`{"success":true,"id":2}`) + + gock.New(crmURL). + Post("/customers-corporate/ext-id/contacts/create"). + Reply(201). + BodyString(`{"success":true,"id":3}`) + + createResponse, createStatus, createErr := c.CustomerCreate(Customer{ + ExternalID: "test-customer-as-contact-person", + FirstName: "Contact", + LastName: "Person", + }, "site") + + if createErr.Error() != "" { + t.Errorf("%v", createErr.Error()) + } + + if createStatus >= http.StatusBadRequest { + t.Errorf("%v", createErr.ApiError()) + } + + if createResponse.Success != true { + t.Errorf("%v", createErr.ApiError()) + } + + if createResponse.ID != 2 { + t.Errorf("invalid createResponse.ID: should be `2`, got `%d`", createResponse.ID) + } + + data, status, err := c.CorporateCustomerContactsCreate("ext-id", ByExternalID, CorporateCustomerContact{ + IsMain: false, + Customer: CorporateCustomerContactCustomer{ + ExternalID: "test-customer-as-contact-person", + Site: "site", + }, + Companies: []IdentifiersPair{}, + }, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("%v", err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if data.ID != 3 { + t.Errorf("invalid data.ID: should be `3`, got `%d`", data.ID) + } +} + +func TestClient_CorporateCustomerContactsEdit(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/ext-id/contacts/2350/edit"). + Reply(200). + BodyString(`{"success":true,"id":19}`) + + data, status, err := c.CorporateCustomerContactsEdit("ext-id", ByExternalID, ByID, CorporateCustomerContact{ + IsMain: false, + Customer: CorporateCustomerContactCustomer{ + ID: 2350, + }, + }, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("(%d) %v", status, err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } + + if data.ID == 0 { + t.Errorf("invalid data.ID: should be `19`, got `%d`", data.ID) + } +} + +func TestClient_CorporateCustomerEdit(t *testing.T) { + c := client() + + defer gock.Off() + + gock.New(crmURL). + Post("/customers-corporate/ext-id/edit"). + Reply(200). + BodyString(`{"success":true,"id":2346}`) + + data, status, err := c.CorporateCustomerEdit(CorporateCustomer{ + ExternalID: "ext-id", + Nickname: "Another Nickname 2", + Vip: true, + }, ByExternalID, "site") + + if err.Error() != "" { + t.Errorf("%v", err.Error()) + } + + if status >= http.StatusBadRequest { + t.Errorf("(%d) %v", status, err.ApiError()) + } + + if data.Success != true { + t.Errorf("%v", err.ApiError()) + } +} + func TestClient_NotesNotes(t *testing.T) { c := client() @@ -1002,7 +1977,7 @@ func TestClient_OrderChange(t *testing.T) { jr, _ = json.Marshal(&f) p = url.Values{ - "by": {string("id")}, + "by": {string(ByID)}, "order": {string(jr[:])}, } @@ -1013,7 +1988,7 @@ func TestClient_OrderChange(t *testing.T) { Reply(200). BodyString(`{"success": true}`) - ed, se, err := c.OrderEdit(f, "id") + ed, se, err := c.OrderEdit(f, ByID) if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -1028,11 +2003,11 @@ func TestClient_OrderChange(t *testing.T) { gock.New(crmURL). Get(fmt.Sprintf("/orders/%s", f.ExternalID)). - MatchParam("by", "externalId"). + MatchParam("by", ByExternalID). Reply(200). BodyString(`{"success": true}`) - data, status, err := c.Order(f.ExternalID, "externalId", "") + data, status, err := c.Order(f.ExternalID, ByExternalID, "") if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -1065,7 +2040,7 @@ func TestClient_OrderChange_Fail(t *testing.T) { jr, _ := json.Marshal(&f) p := url.Values{ - "by": {string("id")}, + "by": {string(ByID)}, "order": {string(jr[:])}, } @@ -1076,7 +2051,7 @@ func TestClient_OrderChange_Fail(t *testing.T) { Reply(404). BodyString(`{"success": false, "errorMsg": "Not found map"}`) - data, status, err := c.OrderEdit(f, "id") + data, status, err := c.OrderEdit(f, ByID) if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -1421,7 +2396,7 @@ func TestClient_PaymentCreateEditDelete(t *testing.T) { Reply(200). BodyString(`{"success": true}`) - paymentEditResponse, status, err := c.OrderPaymentEdit(k, "id") + paymentEditResponse, status, err := c.OrderPaymentEdit(k, ByID) if err.Error() != "" { t.Errorf("%v", err.Error()) } @@ -1512,7 +2487,7 @@ func TestClient_PaymentCreateEditDelete_Fail(t *testing.T) { Reply(404). BodyString(`{"success": false, "errorMsg": "Payment not found"}`) - paymentEditResponse, status, err := c.OrderPaymentEdit(k, "id") + paymentEditResponse, status, err := c.OrderPaymentEdit(k, ByID) if err.Error() != "" { t.Errorf("%v", err.Error()) } diff --git a/v5/filters.go b/v5/filters.go index 3e2fdf7..6a653bd 100644 --- a/v5/filters.go +++ b/v5/filters.go @@ -57,6 +57,73 @@ type CustomersFilter struct { CustomFields map[string]string `url:"customFields,omitempty,brackets"` } +// CorporateCustomersFilter type +type CorporateCustomersFilter struct { + ContragentName string `url:"contragentName,omitempty"` + ContragentInn string `url:"contragentInn,omitempty"` + ContragentKpp string `url:"contragentKpp,omitempty"` + ContragentBik string `url:"contragentBik,omitempty"` + ContragentCorrAccount string `url:"contragentCorrAccount,omitempty"` + ContragentBankAccount string `url:"contragentBankAccount,omitempty"` + ContragentTypes []string `url:"contragentTypes,omitempty,brackets"` + ExternalIds []string `url:"externalIds,omitempty,brackets"` + Name string `url:"name,omitempty"` + City string `url:"city,omitempty"` + Region string `url:"region,omitempty"` + Email string `url:"email,omitempty"` + Notes string `url:"notes,omitempty"` + MinOrdersCount int `url:"minOrdersCount,omitempty"` + MaxOrdersCount int `url:"maxOrdersCount,omitempty"` + MinAverageSumm float32 `url:"minAverageSumm,omitempty"` + MaxAverageSumm float32 `url:"maxAverageSumm,omitempty"` + MinTotalSumm float32 `url:"minTotalSumm,omitempty"` + MaxTotalSumm float32 `url:"maxTotalSumm,omitempty"` + ClassSegment string `url:"classSegment,omitempty"` + DiscountCardNumber string `url:"discountCardNumber,omitempty"` + Attachments int `url:"attachments,omitempty"` + MinCostSumm float32 `url:"minCostSumm,omitempty"` + MaxCostSumm float32 `url:"maxCostSumm,omitempty"` + Vip int `url:"vip,omitempty"` + Bad int `url:"bad,omitempty"` + TasksCount int `url:"tasksCounts,omitempty"` + Ids []string `url:"ids,omitempty,brackets"` + Sites []string `url:"sites,omitempty,brackets"` + Managers []string `url:"managers,omitempty,brackets"` + ManagerGroups []string `url:"managerGroups,omitempty,brackets"` + DateFrom string `url:"dateFrom,omitempty"` + DateTo string `url:"dateTo,omitempty"` + FirstOrderFrom string `url:"firstOrderFrom,omitempty"` + FirstOrderTo string `url:"firstOrderTo,omitempty"` + LastOrderFrom string `url:"lastOrderFrom,omitempty"` + LastOrderTo string `url:"lastOrderTo,omitempty"` + CustomFields map[string]string `url:"customFields,omitempty,brackets"` +} + +// CorporateCustomersNotesFilter type +type CorporateCustomersNotesFilter struct { + Ids []string `url:"ids,omitempty,brackets"` + CustomerIds []string `url:"ids,omitempty,brackets"` + CustomerExternalIds []string `url:"customerExternalIds,omitempty,brackets"` + ManagerIds []string `url:"managerIds,omitempty,brackets"` + Text string `url:"text,omitempty"` + CreatedAtFrom string `url:"createdAtFrom,omitempty"` + CreatedAtTo string `url:"createdAtTo,omitempty"` +} + +// CorporateCustomerAddressesFilter type +type CorporateCustomerAddressesFilter struct { + Ids []string `url:"ids,omitempty,brackets"` + Name string `url:"name,omitempty"` + City string `url:"city,omitempty"` + Region string `url:"region,omitempty"` +} + +// IdentifiersPairFilter type +type IdentifiersPairFilter struct { + Ids []string `url:"ids,omitempty,brackets"` + ExternalIds []string `url:"externalIds,omitempty,brackets"` +} + // CustomersHistoryFilter type type CustomersHistoryFilter struct { CustomerID int `url:"customerId,omitempty"` @@ -66,6 +133,16 @@ type CustomersHistoryFilter struct { EndDate string `url:"endDate,omitempty"` } +// CorporateCustomersHistoryFilter type +type CorporateCustomersHistoryFilter struct { + CustomerID int `url:"customerId,omitempty"` + SinceID int `url:"sinceId,omitempty"` + CustomerExternalID string `url:"customerExternalId,omitempty"` + ContactIds []string `url:"contactIds,omitempty,brackets"` + StartDate string `url:"startDate,omitempty"` + EndDate string `url:"endDate,omitempty"` +} + // OrdersFilter type type OrdersFilter struct { Ids []int `url:"ids,omitempty,brackets"` diff --git a/v5/request.go b/v5/request.go index 9c78e2d..2ae19b6 100644 --- a/v5/request.go +++ b/v5/request.go @@ -13,6 +13,38 @@ type CustomersRequest struct { Page int `url:"page,omitempty"` } +// CorporateCustomersRequest type +type CorporateCustomersRequest struct { + Filter CorporateCustomersFilter `url:"filter,omitempty"` + Limit int `url:"limit,omitempty"` + Page int `url:"page,omitempty"` +} + +// CorporateCustomersNotesRequest type +type CorporateCustomersNotesRequest struct { + Filter CorporateCustomersNotesFilter `url:"filter,omitempty"` + Limit int `url:"limit,omitempty"` + Page int `url:"page,omitempty"` +} + +// CorporateCustomerAddressesRequest type +type CorporateCustomerAddressesRequest struct { + Filter CorporateCustomerAddressesFilter `url:"filter,omitempty"` + By string `url:"by,omitempty"` + Site string `url:"site,omitempty"` + Limit int `url:"limit,omitempty"` + Page int `url:"page,omitempty"` +} + +// IdentifiersPairRequest type +type IdentifiersPairRequest struct { + Filter IdentifiersPairFilter `url:"filter,omitempty"` + By string `url:"by,omitempty"` + Site string `url:"site,omitempty"` + Limit int `url:"limit,omitempty"` + Page int `url:"page,omitempty"` +} + // CustomersUploadRequest type type CustomersUploadRequest struct { Customers []Customer `url:"customers,omitempty,brackets"` @@ -26,6 +58,13 @@ type CustomersHistoryRequest struct { Page int `url:"page,omitempty"` } +// CorporateCustomersHistoryRequest type +type CorporateCustomersHistoryRequest struct { + Filter CorporateCustomersHistoryFilter `url:"filter,omitempty"` + Limit int `url:"limit,omitempty"` + Page int `url:"page,omitempty"` +} + // OrderRequest type type OrderRequest struct { By string `url:"by,omitempty"` diff --git a/v5/response.go b/v5/response.go index 557c6ca..8fb8c72 100644 --- a/v5/response.go +++ b/v5/response.go @@ -37,6 +37,12 @@ type CustomerResponse struct { Customer *Customer `json:"customer,omitempty,brackets"` } +// CorporateCustomerResponse type +type CorporateCustomerResponse struct { + Success bool `json:"success"` + CorporateCustomer *CorporateCustomer `json:"customerCorporate,omitempty,brackets"` +} + // CustomersResponse type type CustomersResponse struct { Success bool `json:"success"` @@ -44,6 +50,38 @@ type CustomersResponse struct { Customers []Customer `json:"customers,omitempty,brackets"` } +// CorporateCustomersResponse type +type CorporateCustomersResponse struct { + Success bool `json:"success"` + Pagination *Pagination `json:"pagination,omitempty"` + CustomersCorporate []CorporateCustomer `json:"customersCorporate,omitempty,brackets"` +} + +// CorporateCustomersNotesResponse type +type CorporateCustomersNotesResponse struct { + Success bool `json:"success"` + Pagination *Pagination `json:"pagination,omitempty"` + Notes []Note `json:"notes,omitempty,brackets"` +} + +// CorporateCustomersAddressesResponse type +type CorporateCustomersAddressesResponse struct { + Success bool `json:"success"` + Addresses []CorporateCustomerAddress `json:"addresses"` +} + +// CorporateCustomerCompaniesResponse type +type CorporateCustomerCompaniesResponse struct { + Success bool `json:"success"` + Companies []Company `json:"companies"` +} + +// CorporateCustomerContactsResponse type +type CorporateCustomerContactsResponse struct { + Success bool `json:"success"` + Contacts []CorporateCustomerContact `json:"contacts"` +} + // CustomerChangeResponse type type CustomerChangeResponse struct { Success bool `json:"success"` @@ -51,12 +89,18 @@ type CustomerChangeResponse struct { State string `json:"state,omitempty"` } +// CorporateCustomerChangeResponse type +type CorporateCustomerChangeResponse CustomerChangeResponse + // CustomersUploadResponse type type CustomersUploadResponse struct { Success bool `json:"success"` UploadedCustomers []IdentifiersPair `json:"uploadedCustomers,omitempty,brackets"` } +// CorporateCustomersUploadResponse type +type CorporateCustomersUploadResponse CustomersUploadResponse + // CustomersHistoryResponse type type CustomersHistoryResponse struct { Success bool `json:"success,omitempty"` @@ -65,6 +109,14 @@ type CustomersHistoryResponse struct { Pagination *Pagination `json:"pagination,omitempty"` } +// CorporateCustomersHistoryResponse type +type CorporateCustomersHistoryResponse struct { + Success bool `json:"success,omitempty"` + GeneratedAt string `json:"generatedAt,omitempty"` + History []CorporateCustomerHistoryRecord `json:"history,omitempty,brackets"` + Pagination *Pagination `json:"pagination,omitempty"` +} + // OrderResponse type type OrderResponse struct { Success bool `json:"success"` diff --git a/v5/types.go b/v5/types.go index bb98a56..1d97f2f 100644 --- a/v5/types.go +++ b/v5/types.go @@ -2,6 +2,12 @@ package v5 import "net/http" +// ByID is "id" constant to use as `by` property in methods +const ByID = "id" + +// ByExternalId is "externalId" constant to use as `by` property in methods +const ByExternalID = "externalId" + // Client type type Client struct { URL string @@ -145,6 +151,85 @@ type Customer struct { CustomFields map[string]string `json:"customFields,omitempty,brackets"` } +// CorporateCustomer type +type CorporateCustomer struct { + ID int `json:"id,omitempty"` + ExternalID string `json:"externalId,omitempty"` + Nickname string `json:"nickName,omitempty"` + CreatedAt string `json:"createdAt,omitempty"` + Vip bool `json:"vip,omitempty"` + Bad bool `json:"bad,omitempty"` + CustomFields map[string]string `json:"customFields,omitempty,brackets"` + PersonalDiscount float32 `json:"personalDiscount,omitempty"` + DiscountCardNumber string `json:"discountCardNumber,omitempty"` + ManagerID int `json:"managerId,omitempty"` + Source *Source `json:"source,omitempty"` + CustomerContacts []CorporateCustomerContact `json:"customerContacts,omitempty"` + Companies []Company `json:"companies,omitempty"` + Addresses []CorporateCustomerAddress `json:"addresses,omitempty"` +} + +type CorporateCustomerContact struct { + IsMain bool `json:"isMain,omitempty"` + Customer CorporateCustomerContactCustomer `json:"customer,omitempty"` + Companies []IdentifiersPair `json:"companies,omitempty"` +} + +// CorporateCustomerAddress type. Address didn't inherited in order to simplify declaration. +type CorporateCustomerAddress struct { + ID int `json:"id,omitempty"` + Index string `json:"index,omitempty"` + CountryISO string `json:"countryIso,omitempty"` + Region string `json:"region,omitempty"` + RegionID int `json:"regionId,omitempty"` + City string `json:"city,omitempty"` + CityID int `json:"cityId,omitempty"` + CityType string `json:"cityType,omitempty"` + Street string `json:"street,omitempty"` + StreetID int `json:"streetId,omitempty"` + StreetType string `json:"streetType,omitempty"` + Building string `json:"building,omitempty"` + Flat string `json:"flat,omitempty"` + IntercomCode string `json:"intercomCode,omitempty"` + Floor int `json:"floor,omitempty"` + Block int `json:"block,omitempty"` + House string `json:"house,omitempty"` + Housing string `json:"housing,omitempty"` + Metro string `json:"metro,omitempty"` + Notes string `json:"notes,omitempty"` + Text string `json:"text,omitempty"` + ExternalID string `json:"externalId,omitempty"` + Name string `json:"name,omitempty"` +} + +type CorporateCustomerContactCustomer struct { + ID int `json:"id,omitempty"` + ExternalID string `json:"externalId,omitempty"` + BrowserID string `json:"browserId,omitempty"` + Site string `json:"site,omitempty"` +} + +type Company struct { + ID int `json:"id,omitempty"` + IsMain bool `json:"isMain,omitempty"` + ExternalID string `json:"externalId,omitempty"` + Active bool `json:"active,omitempty"` + Name string `json:"name,omitempty"` + Brand string `json:"brand,omitempty"` + Site string `json:"site,omitempty"` + CreatedAt string `json:"createdAt,omitempty"` + Contragent *Contragent `json:"contragent,omitempty"` + Address *IdentifiersPair `json:"address,omitempty"` + CustomFields map[string]string `json:"customFields,omitempty,brackets"` +} + +// CorporateCustomerNote type +type CorporateCustomerNote struct { + ManagerID int `json:"managerId,omitempty"` + Text string `json:"text,omitempty"` + Customer *IdentifiersPair `json:"customer,omitempty"` +} + // Phone type type Phone struct { Number string `json:"number,omitempty"` @@ -163,6 +248,19 @@ type CustomerHistoryRecord struct { Customer *Customer `json:"customer,omitempty,brackets"` } +// CorporateCustomerHistoryRecord type +type CorporateCustomerHistoryRecord struct { + ID int `json:"id,omitempty"` + CreatedAt string `json:"createdAt,omitempty"` + Created bool `json:"created,omitempty"` + Deleted bool `json:"deleted,omitempty"` + Source string `json:"source,omitempty"` + Field string `json:"field,omitempty"` + User *User `json:"user,omitempty,brackets"` + APIKey *APIKey `json:"apiKey,omitempty,brackets"` + CorporateCustomer *CorporateCustomer `json:"corporateCustomer,omitempty,brackets"` +} + /** Order related types */