diff --git a/configCRM.go b/configCRM.go index d9784b6..ea68752 100644 --- a/configCRM.go +++ b/configCRM.go @@ -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") +} diff --git a/crmContact.go b/crmContact.go new file mode 100644 index 0000000..346b460 --- /dev/null +++ b/crmContact.go @@ -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 +} diff --git a/crmContact_test.go b/crmContact_test.go new file mode 100644 index 0000000..b1a4c28 --- /dev/null +++ b/crmContact_test.go @@ -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) +} diff --git a/crmEntity.go b/crmEntity.go index 93b518a..89d76c3 100644 --- a/crmEntity.go +++ b/crmEntity.go @@ -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) } diff --git a/crmLead.go b/crmLead.go index dc85870..c31b098 100644 --- a/crmLead.go +++ b/crmLead.go @@ -179,3 +179,8 @@ func importExistingWechatUserAsLead() { } } } + +func (m crmdLead) crmLeadConvert2Contact() (newContact crmdContact) { + newContact.convertFromLead(m.ID) + return +} diff --git a/crmpixel.go b/crmpixel.go index b025612..3907037 100644 --- a/crmpixel.go +++ b/crmpixel.go @@ -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") } } diff --git a/sample_data/conver2contact.json b/sample_data/conver2contact.json new file mode 100644 index 0000000..68d057a --- /dev/null +++ b/sample_data/conver2contact.json @@ -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 +} \ No newline at end of file diff --git a/sample_data/crm_contact.json b/sample_data/crm_contact.json new file mode 100644 index 0000000..53f61cc --- /dev/null +++ b/sample_data/crm_contact.json @@ -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 +} \ No newline at end of file