| @@ -69,3 +69,7 @@ func (c EspoCRMAPIConfig) apiAuthURL() string { | |||
| func (c EspoCRMAPIConfig) apiUploadAttachmentURL() string { | |||
| return c.apiURL("Attachment/action/upload") | |||
| } | |||
| func (c EspoCRMAPIConfig) apiConvertLeadURL() string { | |||
| return c.apiURL("Lead/action/convert") | |||
| } | |||
| @@ -0,0 +1,103 @@ | |||
| package main | |||
| import ( | |||
| "encoding/json" | |||
| "log" | |||
| ) | |||
| type crmdContact struct { | |||
| crmdEntityBase | |||
| SolutationName string `json:"solutationName,omitempty"` | |||
| FirstName string `json:"firstName,omitempty"` | |||
| LastName string `json:"lastName,omitempty"` | |||
| AccountID string `json:"accountID,omitempty"` | |||
| Title string `json:"title,omitempty"` | |||
| EmailAddress string `json:"emailAddress,omitempty"` | |||
| PhoneNumber string `json:"phoneNumber,omitempty"` | |||
| DoNotCall bool `json:"doNotCall,omitempty"` | |||
| AddressStreet string `json:"addressStreet,omitempty"` | |||
| AddressCity string `json:"addressCity,omitempty"` | |||
| AddressState string `json:"addressState,omitempty"` | |||
| AddressCountry string `json:"addressCountry,omitempty"` | |||
| AddresPostalCode string `json:"addressPostalcode,omitempty"` | |||
| AccountType string `json:"accountType,omitempty"` | |||
| EmailAddressData []struct { | |||
| EmailAddress string `json:"emailAddress,omitempty"` | |||
| Lower string `json:"lower,omitempty"` | |||
| Primary bool `json:"primary,omitempty"` | |||
| OptOut bool `json:"optOut,omitempty"` | |||
| Invalid bool `json:"invalid,omitempty"` | |||
| } `json:"emailAddressData,omitempty"` | |||
| PhoneNumberData []struct { | |||
| PhoneNumber string `json:"phoneNumber,omitempty"` | |||
| Primary bool `json:"primary,omitempty"` | |||
| Type string `json:"type,omitempty"` | |||
| } `json:"phoneNumberData,omitempty"` | |||
| AccountName string `json:"accountName,omitempty"` | |||
| AccountIDs []string `json:"accountsIds,omitempty"` | |||
| AccountNames map[string]string `json:"accountsNames,omitempty"` | |||
| AccountsColumns map[string]struct { | |||
| Role string `json:"role,omitempty"` | |||
| } `json:"accountsColumns,omitempty"` | |||
| CompainID string `json:"campainId,omitempty"` | |||
| CampainName string `json:"campainName,omitempty"` | |||
| PortalUserID string `json:"portalUserId,omitempty"` | |||
| PortalUserName string `json:"portalUserName,omitempty"` | |||
| OriginalLeadID string `json:"originalLeadId,omitempty"` | |||
| OriginalLeadName string `json:"originalLeadName,omitempty"` | |||
| IsFollowed bool `json:"isFollowed,omitempty"` | |||
| FollwersIDs []string `json:"followersIds,omitempty"` | |||
| FollowersNames map[string]string `json:"followersNames,omitempty"` | |||
| } | |||
| type crmdConvert struct { | |||
| ID string `json:"id,omitempty"` | |||
| Records struct { | |||
| Contact crmdContact `json:"Contact,omitempty"` | |||
| } `json:"records,omitempty"` | |||
| } | |||
| func (m *crmdContact) convertFromLead(leadID string) (err error) { | |||
| url := CRMConfig.apiConvertLeadURL() | |||
| lead, err := crmGetLead(leadID) | |||
| if err != nil { | |||
| log.Println(err) | |||
| return | |||
| } | |||
| convert := crmdConvert{} | |||
| convert.ID = leadID | |||
| convert.Records.Contact.copyFromLead(lead) | |||
| jsonB, err := json.Marshal(convert) | |||
| if err != nil { | |||
| return | |||
| } | |||
| jsonStr, err := postRAW(jsonB, url, crmBuildCommonAPIHeader()) | |||
| //log.Println(jsonStr) | |||
| if err != nil { | |||
| return | |||
| } | |||
| entity, err := crmJSON2Entity("Lead", jsonStr) | |||
| if err != nil { | |||
| return | |||
| } | |||
| convertedLead := entity.(crmdLead) | |||
| entity, err = crmGetEntity("Contact", convertedLead.CreatedContactID) | |||
| if err != nil { | |||
| return | |||
| } | |||
| *m = entity.(crmdContact) | |||
| return | |||
| } | |||
| func (m *crmdContact) copyFromLead(lead crmdLead) { | |||
| m.SolutationName = lead.SalutationName | |||
| m.FirstName = lead.FirstName | |||
| m.LastName = lead.LastName | |||
| m.EmailAddressData = lead.EmailAddressData | |||
| m.PhoneNumberData = lead.PhoneNumberData | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| package main | |||
| import ( | |||
| "encoding/json" | |||
| "testing" | |||
| "time" | |||
| ) | |||
| func TestConvertLead2Contact(t *testing.T) { | |||
| //create a lead | |||
| l := crmdLead{} | |||
| l.FirstName = "testConvert" + time.Now().Format(getCrmTimeLayout()) | |||
| l.LastName = "temp lead (deletable)" | |||
| l.Status = "Deletable" | |||
| l.Password = "password" | |||
| l.EmailAddress = "non-exit-fake@badmail.com" | |||
| l.PhoneNumber = "12345678-1" | |||
| l.ForceDuplicate = true | |||
| jsonB, err := json.Marshal(l) | |||
| AssertEqual(t, err, nil, "") | |||
| entity, err := crmCreateEntity("Lead", jsonB) | |||
| AssertEqual(t, err, nil, "") | |||
| lead := entity.(crmdLead) | |||
| AssertEqual(t, lead.ID != "", true, "") | |||
| //conver to contact | |||
| cc := crmdContact{} | |||
| err = cc.convertFromLead(lead.ID) | |||
| AssertEqual(t, err, nil, "") | |||
| AssertEqual(t, cc.OriginalLeadID, lead.ID, "") | |||
| AssertEqual(t, cc.FirstName, lead.FirstName, "") | |||
| AssertEqual(t, cc.LastName, lead.LastName, "") | |||
| AssertEqual(t, cc.EmailAddress, lead.EmailAddress, "") | |||
| AssertEqual(t, cc.PhoneNumber, lead.PhoneNumber, "") | |||
| //delete lead | |||
| crmDeleteEntity("Lead", lead.ID) | |||
| //delete contact | |||
| crmDeleteEntity("Contact", cc.ID) | |||
| } | |||
| @@ -80,6 +80,10 @@ func crmJSON2Entity(entityType string, data string) (r interface{}, err error) { | |||
| r = e | |||
| case "Account": | |||
| //r = crmdAccount{} | |||
| case "Contact": | |||
| e := crmdContact{} | |||
| err = json.Unmarshal([]byte(data), &e) | |||
| r = e | |||
| default: | |||
| log.Fatalf("crmJSON2Entity: Unknown EntityType %s", entityType) | |||
| } | |||
| @@ -179,3 +179,8 @@ func importExistingWechatUserAsLead() { | |||
| } | |||
| } | |||
| } | |||
| func (m crmdLead) crmLeadConvert2Contact() (newContact crmdContact) { | |||
| newContact.convertFromLead(m.ID) | |||
| return | |||
| } | |||
| @@ -28,7 +28,7 @@ func crmpixel(w http.ResponseWriter, r *http.Request) { | |||
| if err == nil { | |||
| w.Write(pixel) | |||
| } else { | |||
| fmt.Fprint(w, "decodig wrong") | |||
| fmt.Fprint(w, "decodig pixel image wrong") | |||
| } | |||
| } | |||
| @@ -0,0 +1,83 @@ | |||
| { | |||
| "id": "5961bfac40e1b4e69", | |||
| "records": { | |||
| "Contact": { | |||
| "firstName": "Anonymous", | |||
| "lastName": "User 127.0.0.1:49261", | |||
| "salutationName": "", | |||
| "title": "", | |||
| "addressStreet": "", | |||
| "addressCity": "", | |||
| "addressState": "", | |||
| "addressCountry": "", | |||
| "addressPostalCode": "", | |||
| "emailAddress": null, | |||
| "emailAddressData": [], | |||
| "phoneNumber": null, | |||
| "phoneNumberData": [], | |||
| "doNotCall": false, | |||
| "description": "Anonymous user from 127.0.0.1:49261", | |||
| "assignedUserId": null, | |||
| "assignedUserName": null, | |||
| "teamsIds": [], | |||
| "teamsNames": {}, | |||
| "campaignId": null, | |||
| "campaignName": null | |||
| } | |||
| } | |||
| } | |||
| //converted by lead | |||
| { | |||
| "id": "5961bfac40e1b4e69", | |||
| "name": "Anonymous User 127.0.0.1:49261", | |||
| "deleted": false, | |||
| "salutationName": null, | |||
| "firstName": "Anonymous", | |||
| "lastName": "User 127.0.0.1:49261", | |||
| "title": null, | |||
| "status": "Converted", | |||
| "source": "", | |||
| "industry": "", | |||
| "opportunityAmount": null, | |||
| "website": null, | |||
| "addressStreet": null, | |||
| "addressCity": null, | |||
| "addressState": null, | |||
| "addressCountry": null, | |||
| "addressPostalCode": null, | |||
| "emailAddress": null, | |||
| "phoneNumber": null, | |||
| "doNotCall": false, | |||
| "description": "Anonymous user from 127.0.0.1:49261", | |||
| "createdAt": "2017-07-09 05:31:24", | |||
| "modifiedAt": "2017-07-09 11:11:45", | |||
| "accountName": null, | |||
| "password": "Password", | |||
| "wechatOpenID": null, | |||
| "opportunityAmountCurrency": null, | |||
| "opportunityAmountConverted": null, | |||
| "emailAddressData": [], | |||
| "phoneNumberData": [], | |||
| "createdById": "58ef420cac3cf6c95", | |||
| "createdByName": "wechat robot", | |||
| "modifiedById": "1", | |||
| "modifiedByName": "Admin", | |||
| "assignedUserId": null, | |||
| "assignedUserName": null, | |||
| "teamsIds": [], | |||
| "teamsNames": {}, | |||
| "campaignId": null, | |||
| "campaignName": null, | |||
| "createdAccountId": null, | |||
| "createdAccountName": null, | |||
| "createdContactId": "59620f71ae30391e8", | |||
| "createdContactName": "Anonymous User 127.0.0.1:49261", | |||
| "createdOpportunityId": null, | |||
| "createdOpportunityName": null, | |||
| "isFollowed": false, | |||
| "followersIds": [], | |||
| "followersNames": {}, | |||
| "avatarId": null, | |||
| "avatarName": null | |||
| } | |||
| @@ -0,0 +1,110 @@ | |||
| { | |||
| "id": "59620f71ae30391e8", | |||
| "name": "Anonymous User 127.0.0.1:49261", | |||
| "deleted": false, | |||
| "salutationName": "", | |||
| "firstName": "Anonymous", | |||
| "lastName": "User 127.0.0.1:49261", | |||
| "accountId": null, | |||
| "title": null, | |||
| "description": "Anonymous user from 127.0.0.1:49261", | |||
| "emailAddress": null, | |||
| "phoneNumber": null, | |||
| "doNotCall": false, | |||
| "addressStreet": "", | |||
| "addressCity": "", | |||
| "addressState": "", | |||
| "addressCountry": "", | |||
| "addressPostalCode": "", | |||
| "accountType": null, | |||
| "createdAt": "2017-07-09 11:11:45", | |||
| "modifiedAt": "2017-07-09 11:11:45", | |||
| "emailAddressData": [], | |||
| "phoneNumberData": [], | |||
| "accountName": null, | |||
| "accountsIds": [], | |||
| "accountsNames": {}, | |||
| "accountsColumns": {}, | |||
| "campaignId": null, | |||
| "campaignName": null, | |||
| "createdById": "1", | |||
| "createdByName": "Admin", | |||
| "modifiedById": null, | |||
| "modifiedByName": null, | |||
| "assignedUserId": null, | |||
| "assignedUserName": null, | |||
| "teamsIds": [], | |||
| "teamsNames": {}, | |||
| "portalUserId": null, | |||
| "portalUserName": null, | |||
| "originalLeadId": "5961bfac40e1b4e69", | |||
| "originalLeadName": "Anonymous User 127.0.0.1:49261", | |||
| "isFollowed": false, | |||
| "followersIds": [], | |||
| "followersNames": {} | |||
| } | |||
| { | |||
| "id": "57efb926aa6725fdd", | |||
| "name": "Vincent Zhang", | |||
| "deleted": false, | |||
| "salutationName": "Mr.", | |||
| "firstName": "Vincent", | |||
| "lastName": "Zhang", | |||
| "accountId": "57e3de733402c8d5f", | |||
| "title": "IT Admin", | |||
| "description": "\u5e7f\u4e1c\u6885\u53bf\u4eba\uff0c1982\u5e74\u51fa\u751f\uff0c \u5728\u6089\u5c3c\u5927\u5b66\u5b66\u4e60\u8fc7 \u4e2d\u5b66\u6559\u80b2\u3002\n\nMM \u7684\u7f51\u9875\u4ecb\u7ecd\nVincent has taught Mandarin in China, Indonesia and Australia.\n\nHis qualifications include a Bachelor of Arts in Teaching Chinese as a Second Language from Jinan University, Guangzhou, a Master of Education from the University of Sydney, and a Diploma in Education from the University of New South Wales.\n\nI always cater to the needs of individual students. I like to develop my students\u2019 speaking and listening skills using a range of methods, such as conversation, discussion and debate. \u2013 Vincent", | |||
| "emailAddress": null, | |||
| "phoneNumber": null, | |||
| "doNotCall": false, | |||
| "addressStreet": "", | |||
| "addressCity": "", | |||
| "addressState": "", | |||
| "addressCountry": "", | |||
| "addressPostalCode": "", | |||
| "accountType": "Customer", | |||
| "createdAt": "2016-10-01 13:24:54", | |||
| "modifiedAt": "2016-10-10 06:18:54", | |||
| "emailAddressData": [], | |||
| "phoneNumberData": [], | |||
| "accountName": "Macquarie Mandarin", | |||
| "accountsIds": [ | |||
| "57e3de733402c8d5f" | |||
| ], | |||
| "accountsNames": { | |||
| "57e3de733402c8d5f": "Macquarie Mandarin" | |||
| }, | |||
| "accountsColumns": { | |||
| "57e3de733402c8d5f": { | |||
| "role": "IT Admin" | |||
| } | |||
| }, | |||
| "campaignId": null, | |||
| "campaignName": null, | |||
| "createdById": "1", | |||
| "createdByName": "\u5b59\u9e4f sunpeng", | |||
| "modifiedById": "1", | |||
| "modifiedByName": "\u5b59\u9e4f sunpeng", | |||
| "assignedUserId": "1", | |||
| "assignedUserName": "\u5b59\u9e4f sunpeng", | |||
| "teamsIds": [], | |||
| "teamsNames": {}, | |||
| "portalUserId": null, | |||
| "portalUserName": null, | |||
| "originalLeadId": null, | |||
| "originalLeadName": null, | |||
| "isFollowed": true, | |||
| "followersIds": [ | |||
| "1" | |||
| ], | |||
| "followersNames": { | |||
| "1": "\u5b59\u9e4f sunpeng" | |||
| }, | |||
| "avatarId": "57fb32cd3de8755df", | |||
| "avatarName": "06_vincen.jpg", | |||
| "namecardId": null, | |||
| "namecardName": null, | |||
| "namecardbackId": null, | |||
| "namecardbackName": null | |||
| } | |||