Skip to content

Instantly share code, notes, and snippets.

@RobinHerzog
Created December 7, 2017 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RobinHerzog/1e9ab75e253774ba83c71e1a03f494f2 to your computer and use it in GitHub Desktop.
Save RobinHerzog/1e9ab75e253774ba83c71e1a03f494f2 to your computer and use it in GitHub Desktop.
Javascript to migrate Salesforce's data to Zoho.
const jsforce = require("jsforce");
const Zoho = require("zoho");
const lodash = require("lodash");
const bluebird = require("bluebird");
const rp = require('request-promise');
const sf = new jsforce.Connection({
loginUrl: "https://login.salesforce.com",
});
const zoho = new Zoho.CRM({
authtoken: "",
});
const zohoGetLeadRecord = function (params) {
return new Promise((resolve, reject) => {
zoho.getRecords('leads', params, (err, data) => {
if (err) {
console.log(err);
return resolve([]);
}
//console.log(data);
if (data && data.data && data.data.Leads && data.data.Leads.row) {
resolve(
data.data.Leads.row.reduce((acc, row) => {
acc.push(
row.FL.reduce((acc, field) => {
acc[field.val] = field.content;
return acc;
}, {}),
);
return acc;
}, []),
);
} else {
resolve([]);
}
});
});
};
const zohoGetDealsRecord = function (params) {
return new Promise((resolve, reject) => {
zoho.getRecords('Potentials', params, (err, data) => {
if (err) {
console.log(err);
return resolve([]);
}
//console.log(data);
if (data && data.data && data.data.Potentials && data.data.Potentials.row) {
resolve(
data.data.Potentials.row.reduce((acc, row) => {
acc.push(
row.FL.reduce((acc, field) => {
acc[field.val] = field.content;
return acc;
}, {}),
);
return acc;
}, []),
);
} else {
resolve([]);
}
});
});
};
const zohoGetAllProductRecord = function () {
const iteration = lodash.range(1, 200, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetProductRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 2},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
})
]).then(function (result) {
return result[0];
});
};
const zohoGetAllContactRecord = function () {
const iteration = lodash.range(1, 2800, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetContactRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 2},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
})
]).then(function (result) {
return result[0];
});
};
const zohoGetAllPotentialRecord = function () {
const iteration = lodash.range(1, 2000, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetDealsRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 2},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
})
]).then(function (result) {
return result[0];
});
};
const zohoGetContactRecord = function (params) {
return new Promise((resolve, reject) => {
zoho.getRecords('contacts', params, (err, data) => {
if (err) {
console.log(err);
return resolve([]);
}
if (data && data.data && data.data.Contacts && data.data.Contacts.row) {
resolve(
data.data.Contacts.row.reduce((acc, row) => {
acc.push(
row.FL.reduce((acc, field) => {
acc[field.val] = field.content;
return acc;
}, {}),
);
return acc;
}, []),
);
} else {
resolve([]);
}
});
});
};
const zohoGetProductRecord = function (params) {
return new Promise((resolve, reject) => {
zoho.getRecords('products', params, (err, data) => {
if (err) {
//console.log(err);
return resolve([]);
}
if (data && data.data && data.data.Products && data.data.Products.row) {
resolve(
data.data.Products.row.reduce((acc, row) => {
acc.push(
row.FL.reduce((acc, field) => {
acc[field.val] = field.content;
return acc;
}, {}),
);
return acc;
}, []),
);
} else {
resolve([]);
}
});
});
};
function sfGetLeadRecord() {
return getSfAll("SELECT Id, FirstName, LastName, Email FROM Lead");
}
// const sfGetLeadRecord = function () {
// return new Promise((resolve, reject) => {
// sf.query("SELECT Id, FirstName, LastName, Email FROM Lead", function (err,
// result,) {
// if (err) {
// return reject(err);
// }
// resolve(result.records);
// });
// });
// };
function sfGetDealsRecord() {
return getSfAll("SELECT Id, Name FROM Opportunity");
}
// const sfGetDealsRecord = function () {
// return new Promise((resolve, reject) => {
// sf.query("SELECT Id, Name FROM Opportunity", function (err,
// result,) {
// if (err) {
// return reject(err);
// }
// resolve(result.records);
// });
// });
// };
// function sfGetContactRecord() {
// return getSfAll("SELECT Id, FirstName, LastName, Email FROM Contact");
// }
const sfGetContactRecord = function () {
return new Promise((resolve, reject) => {
sf.query("SELECT Id, FirstName, LastName, Email FROM Contact", function (err,
result,) {
if (err) {
return reject(err);
}
resolve(result.records);
});
});
};
function sfGetProductsRecord() {
return getSfAll("SELECT Id, Name FROM Product2");
}
// const sfGetProductsRecord = function () {
// return new Promise((resolve, reject) => {
// sf.query("SELECT Id, Name FROM Product2", function (err,
// result,) {
// if (err) {
// return reject(err);
// }
// resolve(result.records);
// });
// });
// };
function sfOpportunityLineItemRecord() {
return getSfAll("SELECT Id, Name, Product2Id, OpportunityId, TotalPrice FROM OpportunityLineItem");
}
function sfOpportunityContactRole() {
return getSfAll("SELECT Id,OpportunityId, ContactId, Role FROM OpportunityContactRole");
}
// const sfOpportunityLineItemRecord = function () {
// return new Promise((resolve, reject) => {
// sf.query("SELECT Id, Name, Product2Id, OpportunityId, TotalPrice FROM OpportunityLineItem", function (err,
// result,) {
// if (err) {
// return reject(err);
// }
// resolve(result.records);
// });
// });
// };
function getLeadsIDMap() {
const iteration = lodash.range(1, 2000, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetLeadRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 2},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
}),
sfGetLeadRecord(),
]).then(function (result) {
const zohoLeads = result[0];
const sfLeads = result[1];
const sfLeadsByEmail = lodash.keyBy(sfLeads, "Email");
const mapSf = new Map();
for (const zohoLead of zohoLeads) {
if (sfLeadsByEmail.hasOwnProperty(zohoLead.Email)) {
const sfLead = sfLeadsByEmail[zohoLead.Email];
//console.log(sfLead.Id, zohoLead.LEADID);
mapSf.set(sfLead.Id, zohoLead.LEADID);
}
}
return mapSf;
});
}
function getContactsIDMap() {
const iteration = lodash.range(1, 2800, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetContactRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 2},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
}),
sfGetContactRecord(),
]).then(function (result) {
const zohoContacts = result[0];
const sfContacts = result[1];
sfContacts.map((val) => {
val['Field_SF'] = val.FirstName + " " + val.LastName + " " + val.Email;
return val;
});
zohoContacts.map((val) => {
val['Field_Zoho'] = val["First Name"] + " " + val["Last Name"] + " " + val.Email;
return val;
});
const sfContactsByEmail = lodash.keyBy(sfContacts, "Email");
const mapSf = new Map();
for (const zohoContact of zohoContacts) {
// if (zohoContact["Full Name"] === "undefined")
// console.log('=>'+ zohoContact["First Name"], zohoContact["Last Name"]);
// else
// console.log(zohoContact["Full Name"]);
if (sfContactsByEmail.hasOwnProperty(zohoContact["Email"])) {
const sfContact = sfContactsByEmail[zohoContact["Email"]];
mapSf.set(sfContact.Id, zohoContact.CONTACTID);
}
}
return mapSf;
});
}
function getProductIDMap() {
const iteration = lodash.range(1, 300, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetProductRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 1},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
}),
sfGetProductsRecord(),
]).then(function (result) {
const zohoProducts = result[0];
const sfProducts = result[1];
const sfProductByName = lodash.keyBy(sfProducts.records, "Name");
const mapSf = new Map();
for (const zohoProduct of zohoProducts) {
if (sfProductByName.hasOwnProperty(zohoProduct["Product Name"])) {
const sfProduct = sfProductByName[zohoProduct["Product Name"]];
mapSf.set(sfProduct.Id, zohoProduct.PRODUCTID);
}
}
return mapSf;
});
}
function getDealsIDMap() {
const iteration = lodash.range(1, 2000, 100);
return Promise.all([
bluebird
.map(
iteration,
(range, index) => {
return zohoGetDealsRecord({
fromIndex: range,
toIndex: 100 * (index + 1),
});
},
{concurrency: 1},
)
.then(function (results) {
return results.reduce((acc, result) => {
acc.push(...result);
return acc;
}, []);
}),
sfGetDealsRecord(),
]).then(function (result) {
const zohoDeals = result[0];
const sfDeals = result[1];
const sfDealsByName = lodash.keyBy(sfDeals.records, "Name");
const mapSf = new Map();
for (const zohoDeal of zohoDeals) {
if (sfDealsByName.hasOwnProperty(zohoDeal["Potential Name"])) {
const sfDeal = sfDealsByName[zohoDeal["Potential Name"]];
mapSf.set(sfDeal.Id, zohoDeal.POTENTIALID);
}
}
return mapSf;
});
}
function getSfAll(query) {
return new Promise((resolve, reject) => {
const records = [];
function more(l) {
sf.queryMore(l, function (err, result) {
records.push(...result.records);
if (result.done) {
return resolve(records);
}
console.log(result.nextRecordsUrl);
return more(result.nextRecordsUrl);
});
}
sf.queryAll(query, (err, resAll) => {
if (typeof resAll.nextRecordsUrl !== "undefined") {
more(resAll.nextRecordsUrl);
} else {
resolve(resAll);
}
});
});
}
function getSfTasks() {
return getSfAll("SELECT AccountId, CallDisposition, CallDurationInSeconds, CallObject, CallType, IsArchived, IsClosed, IsHighPriority, IsRecurrence, IsReminderSet, OwnerId, Priority, Description, WhatId, WhoId, Id, Status, Subject, Type, ActivityDate, RecurrenceActivityId, RecurrenceDayOfMonth, RecurrenceDayOfWeekMask, RecurrenceEndDateOnly, RecurrenceInstance, RecurrenceInterval, RecurrenceMonthOfYear, RecurrenceRegeneratedType, RecurrenceStartDateOnly,TaskSubtype, RecurrenceTimeZoneSidKey, RecurrenceType, ReminderDateTime, WhatCount FROM Task");
}
async function awaitFor() {
return new Promise(resolve => setTimeout(resolve, 200));
}
sf.login("", "", async function (err,) {
if (err) {
return console.error(err);
}
// Promise.all([
// zohoGetAllPotentialRecord(),
// sfOpportunityContactRole(),
// getContactsIDMap(),
// getDealsIDMap()
// ]).then(async function (maps) {
//
// const ContactMaps = maps[2];
// const DealsMaps = maps[3];
//
// const zohoByContactId = lodash.keyBy(maps[0], "CONTACTID");
//
// let i = 0;
// for (contactRole of maps[1].records) {
// if (typeof zohoByContactId[ContactMaps.get(contactRole.ContactId)] === "undefined" && typeof DealsMaps.get(contactRole.OpportunityId) !== "undefined") {
//
// //console.log(contactRole.ContactId, ContactMaps.get(contactRole.ContactId), contactRole.OpportunityId, DealsMaps.get(contactRole.OpportunityId));
//
// const xml = '<Potentials><row no="1"><FL val="CONTACTID">'+ContactMaps.get(contactRole.ContactId)+'</FL></row></Potentials>';
//
// const options = {
// uri: 'https://crm.zoho.com/crm/private/json/Potentials/updateRecords',
// method: 'POST',
// timeout: 100000, // 10 min.
// qs: {
// authtoken: "",
// scope: "crmapi",
// id: DealsMaps.get(contactRole.OpportunityId)
// },
// formData: {
// xmlData: xml
// },
// json: true,
// simple: false
// };
//
// rp(options)
// .then((results) => {
// console.log(results);
// }).catch(function (err) {
// throw new Error(err);
// });
//
// await awaitFor();
//
// i++;
// }
// }
// console.log(i);
//
//
// });
//
// getSfTasks().then(function (contacts) {
// console.log(contacts);
// });
//console.log(await zohoGetUsersRecord());
// sfOpportunityLineItemRecord().then(function (contacts) {
// // console.log(contacts);
// });
// Promise.all([
// getContactsIDMap(),
// getSfTasks()
// ]).then(async function (maps) {
//
//
// const ContactMap = maps[0];
//
// let i = 0;
// const Tasks = lodash.chunk(maps[1], 100);
// for (task of Tasks) {
//
// let xml = "<Tasks>";
// let row = 1;
// for (task_item of task) {
//
// if(typeof MAP_USERS_EMAIL[task_item.OwnerId] === "undefined") {
// task_item.OwnerId = '2830205000002009684';
// }
//
// if (task_item.Subject.indexOf("Email") === -1 && typeof ContactMap.get(task_item.WhoId) !== "undefined" && task_item.Status === "Completed") {
// // let desc;
// // if(task_item.Description !== null)
// // desc = task_item.Description.replace(/(\r\n|\n|\r|,)/gm, "");
// // else
// // desc = "";
// //
// // if(desc.length > 500) {
// // desc = "";
// // }
//
// let desc = (task_item.Description !== null) ? task_item.Description.replace(/(\r\n|\n|\r|,)/gm, "") : '';
//
// // console.log("=======>"+task_item.TaskSubtype+"<=======", desc.length);
// // console.log('------------');
// // console.log('------------');
// // console.log('------------');
// // console.log('------------');
//
// xml += '<row no="' + row + '"><FL val="Subject">['+task_item.TaskSubtype +'] ' + task_item.Subject + ' - [SF Import]</FL><FL val="Description"><![CDATA[ ' + desc + ']]></FL><FL val="Status">' + task_item.Status + '</FL><FL val="Due Date">' + task_item.ActivityDate + '</FL><FL val="CONTACTID">' + ContactMap.get(task_item.WhoId) + '</FL><FL val="Task Owner">' + MAP_USERS_EMAIL[task_item.OwnerId] + '</FL></row>';
// row++;
// }
//
// }
// xml += "</Tasks>";
//
//
// console.log(xml);
//
// const options = {
// uri: 'https://crm.zoho.com/crm/private/json/Tasks/insertRecords',
// method: 'POST',
// timeout: 100000, // 10 min.
// qs: {
// authtoken: "",
// scope: "crmapi",
// },
// formData: {
// xmlData: xml
// },
// json:true,
// simple: false
// };
//
// rp(options)
// .then((results) => {
// console.log(results);
// }).catch(function (err) {
// throw new Error(err);
// });
//
// await awaitFor();
//
// }
// });
Promise.all([
sfOpportunityLineItemRecord(),
getProductIDMap(),
getDealsIDMap()
]).then(async function (maps) {
//const opportunityLineItem = maps[0];
const productMap = maps[1];
const dealsMap = maps[2];
let i = 0;
for (dealsItem of maps[0].records) {
//console.log(productMap.get(dealsItem.Product2Id));
//console.log(dealsItem.Product2Id+' => '+productMap)
// console.log(dealsItem);
const options = {
uri: 'https://crm.zoho.com/crm/private/json/Potentials/updateRelatedRecords',
timeout: 100000, // 10 min.
qs: {
authtoken: "",
scope: "crmapi",
id: dealsMap.get(dealsItem.OpportunityId),
//id: MapDeals[deals.OpportunityId],
relatedModule: "Products",
xmlData: '<Products><row no=\"1\"><FL val=\"PRODUCTID\">' + productMap.get(dealsItem.Product2Id) + '</FL><FL val=\"list_price\">' + dealsItem.TotalPrice + '</FL></row></Products>'
},
simple: false,
json: true
};
console.log(options);
// rp(options)
// .then((results) => {
// console.log(results);
// }).catch(function (err) {
// throw new Error(err + " - " + dealsMap.get(dealsItem.OpportunityId));
// });
await awaitFor();
console.log(options);
console.log('===> '+i);
// console.log(dealsItem.Product2Id);
}
});
// getSfTasks().then(function(contacts) {
// console.log(contacts);
// });
// getProductIDMap().then(function(map) {
// console.log(map.size);
// });
});
@RobinHerzog
Copy link
Author

If you can, when importing your file into Zoho, create a custom fields with Salesforce ID. You will be able to map on it instead of Email.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment