2021-05-09 05:08:54 -07:00
|
|
|
import {
|
2021-05-07 16:54:40 -07:00
|
|
|
OptionsWithUri,
|
|
|
|
} from 'request';
|
|
|
|
|
2020-02-12 07:04:43 -08:00
|
|
|
import {
|
|
|
|
IExecuteFunctions,
|
2021-04-29 02:56:19 -07:00
|
|
|
IHookFunctions,
|
2020-02-12 07:04:43 -08:00
|
|
|
} from 'n8n-core';
|
2021-04-29 02:56:19 -07:00
|
|
|
|
2020-02-12 07:04:43 -08:00
|
|
|
import {
|
2021-04-29 02:56:19 -07:00
|
|
|
IDataObject,
|
2021-05-09 07:30:16 -07:00
|
|
|
ILoadOptionsFunctions,
|
2021-04-29 02:56:19 -07:00
|
|
|
NodeApiError,
|
2020-02-12 07:04:43 -08:00
|
|
|
} from 'n8n-workflow';
|
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
import {
|
|
|
|
flow,
|
|
|
|
} from 'lodash';
|
|
|
|
|
|
|
|
export async function zohoApiRequest(
|
2021-05-09 07:30:16 -07:00
|
|
|
this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions,
|
2021-04-29 02:56:19 -07:00
|
|
|
method: string,
|
|
|
|
endpoint: string,
|
|
|
|
body: IDataObject = {},
|
|
|
|
qs: IDataObject = {},
|
|
|
|
uri?: string,
|
|
|
|
) {
|
2021-05-09 07:30:16 -07:00
|
|
|
const { oauthTokenData: { api_domain: apiDomain } } = this.getCredentials('zohoOAuth2Api') as ZohoOAuth2ApiCredentials;
|
2021-04-29 02:56:19 -07:00
|
|
|
|
2020-02-12 07:04:43 -08:00
|
|
|
const options: OptionsWithUri = {
|
|
|
|
body: {
|
|
|
|
data: [
|
|
|
|
body,
|
|
|
|
],
|
|
|
|
},
|
2021-04-29 02:56:19 -07:00
|
|
|
method,
|
2020-02-12 07:04:43 -08:00
|
|
|
qs,
|
2021-05-07 01:02:53 -07:00
|
|
|
uri: uri ?? `${apiDomain}/crm/v2${endpoint}`,
|
2020-10-22 06:46:03 -07:00
|
|
|
json: true,
|
2020-02-12 07:04:43 -08:00
|
|
|
};
|
2021-04-29 02:56:19 -07:00
|
|
|
|
|
|
|
if (!Object.keys(body).length) {
|
|
|
|
delete options.body;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Object.keys(qs).length) {
|
|
|
|
delete options.qs;
|
|
|
|
}
|
|
|
|
|
2020-02-12 07:04:43 -08:00
|
|
|
try {
|
2021-05-07 01:02:53 -07:00
|
|
|
console.log(JSON.stringify(options, null, 2));
|
2021-05-09 07:30:16 -07:00
|
|
|
return await this.helpers.requestOAuth2?.call(this, 'zohoOAuth2Api', options);
|
2020-02-12 07:04:43 -08:00
|
|
|
} catch (error) {
|
2021-04-16 09:33:36 -07:00
|
|
|
throw new NodeApiError(this.getNode(), error);
|
2020-02-12 07:04:43 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
/**
|
|
|
|
* Make an authenticated API request to Zoho CRM API and return all items.
|
|
|
|
*/
|
|
|
|
export async function zohoApiRequestAllItems(
|
2021-05-09 07:30:16 -07:00
|
|
|
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
2021-04-29 02:56:19 -07:00
|
|
|
method: string,
|
|
|
|
endpoint: string,
|
2021-05-09 07:30:16 -07:00
|
|
|
body: IDataObject = {},
|
|
|
|
qs: IDataObject = {},
|
2021-04-29 02:56:19 -07:00
|
|
|
) {
|
2020-02-12 07:04:43 -08:00
|
|
|
const returnData: IDataObject[] = [];
|
|
|
|
|
|
|
|
let responseData;
|
|
|
|
let uri: string | undefined;
|
2021-04-29 02:56:19 -07:00
|
|
|
qs.per_page = 200;
|
|
|
|
qs.page = 0;
|
2020-02-12 07:04:43 -08:00
|
|
|
|
|
|
|
do {
|
2021-04-29 02:56:19 -07:00
|
|
|
responseData = await zohoApiRequest.call(this, method, endpoint, body, qs, uri);
|
2020-02-15 16:23:22 -08:00
|
|
|
uri = responseData.info.more_records;
|
2021-04-29 02:56:19 -07:00
|
|
|
returnData.push.apply(returnData, responseData['data']);
|
|
|
|
qs.page++;
|
2020-02-12 07:04:43 -08:00
|
|
|
} while (
|
2020-02-15 16:23:22 -08:00
|
|
|
responseData.info.more_records !== undefined &&
|
|
|
|
responseData.info.more_records === true
|
2020-02-12 07:04:43 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
return returnData;
|
|
|
|
}
|
2021-04-29 02:56:19 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a Zoho CRM API listing by returning all items or up to a limit.
|
|
|
|
*/
|
|
|
|
export async function handleListing(
|
|
|
|
this: IExecuteFunctions,
|
|
|
|
method: string,
|
|
|
|
endpoint: string,
|
|
|
|
body: IDataObject = {},
|
|
|
|
qs: IDataObject = {},
|
|
|
|
) {
|
|
|
|
let responseData;
|
|
|
|
|
2021-05-09 05:43:57 -07:00
|
|
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
2021-04-29 02:56:19 -07:00
|
|
|
|
|
|
|
if (returnAll) {
|
|
|
|
return await zohoApiRequestAllItems.call(this, method, endpoint, body, qs);
|
|
|
|
}
|
|
|
|
|
|
|
|
const limit = this.getNodeParameter('limit', 0) as number;
|
|
|
|
responseData = await zohoApiRequestAllItems.call(this, method, endpoint, body, qs);
|
|
|
|
return responseData.slice(0, limit);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------
|
|
|
|
// field adjusters
|
|
|
|
// ----------------------------------------
|
|
|
|
|
2021-05-09 07:30:16 -07:00
|
|
|
/**
|
|
|
|
* Place a product ID at a nested position in a product details field.
|
|
|
|
*/
|
|
|
|
export const adjustProductDetails = (productDetails: ProductDetails) => {
|
|
|
|
return productDetails.map(p => {
|
|
|
|
return {
|
|
|
|
...omit('product', p),
|
|
|
|
product: { id: p.id },
|
|
|
|
};
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
/**
|
|
|
|
* Place a location field's contents at the top level of the payload.
|
|
|
|
*/
|
2021-05-09 05:43:57 -07:00
|
|
|
const adjustLocationFields = (locationType: LocationType) => (allFields: AllFields) => {
|
2021-04-29 02:56:19 -07:00
|
|
|
const locationField = allFields[locationType];
|
|
|
|
|
2021-05-09 05:43:57 -07:00
|
|
|
if (!locationField) return allFields;
|
|
|
|
|
|
|
|
return {
|
|
|
|
...omit(locationType, allFields),
|
|
|
|
...locationField.address_fields,
|
|
|
|
};
|
2021-04-29 02:56:19 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const adjustAddressFields = adjustLocationFields('Address');
|
|
|
|
const adjustBillingAddressFields = adjustLocationFields('Billing_Address');
|
|
|
|
const adjustMailingAddressFields = adjustLocationFields('Mailing_Address');
|
|
|
|
const adjustShippingAddressFields = adjustLocationFields('Shipping_Address');
|
|
|
|
const adjustOtherAddressFields = adjustLocationFields('Other_Address');
|
|
|
|
|
2021-05-07 04:19:01 -07:00
|
|
|
/**
|
2021-05-09 06:19:25 -07:00
|
|
|
* Remove from a date field the timestamp set by the datepicker.
|
2021-05-07 04:19:01 -07:00
|
|
|
*/
|
2021-05-09 05:43:57 -07:00
|
|
|
const adjustDateField = (dateType: DateType) => (allFields: AllFields) => {
|
2021-05-07 04:19:01 -07:00
|
|
|
const dateField = allFields[dateType];
|
|
|
|
|
|
|
|
if (!dateField) return allFields;
|
|
|
|
|
|
|
|
allFields[dateType] = dateField.split('T')[0];
|
2021-05-07 01:58:12 -07:00
|
|
|
|
|
|
|
return allFields;
|
|
|
|
};
|
|
|
|
|
2021-05-07 04:19:01 -07:00
|
|
|
const adjustDateOfBirthField = adjustDateField('Date_of_Birth');
|
|
|
|
const adjustClosingDateField = adjustDateField('Closing_Date');
|
2021-05-07 08:28:42 -07:00
|
|
|
const adjustInvoiceDateField = adjustDateField('Invoice_Date');
|
|
|
|
const adjustDueDateField = adjustDateField('Due_Date');
|
|
|
|
|
2021-05-09 05:43:57 -07:00
|
|
|
/**
|
|
|
|
* Place an account name field's contents at the top level of the payload.
|
|
|
|
*/
|
|
|
|
const adjustAccountField = (allFields: AllFields) => {
|
2021-05-07 08:28:42 -07:00
|
|
|
if (!allFields.Account_Name) return allFields;
|
|
|
|
|
2021-05-09 05:43:57 -07:00
|
|
|
return {
|
|
|
|
...omit('Account_Name', allFields),
|
|
|
|
...allFields.Account_Name.account_name_fields,
|
|
|
|
};
|
2021-05-07 08:28:42 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
export const adjustAccountFields = flow(
|
|
|
|
adjustBillingAddressFields,
|
|
|
|
adjustShippingAddressFields,
|
|
|
|
);
|
|
|
|
|
|
|
|
export const adjustContactFields = flow(
|
|
|
|
adjustMailingAddressFields,
|
|
|
|
adjustOtherAddressFields,
|
|
|
|
adjustDateOfBirthField,
|
|
|
|
);
|
2021-05-07 04:19:01 -07:00
|
|
|
|
|
|
|
export const adjustDealFields = adjustClosingDateField;
|
2021-05-07 08:28:42 -07:00
|
|
|
|
|
|
|
export const adjustInvoiceFields = flow(
|
|
|
|
adjustBillingAddressFields,
|
|
|
|
adjustShippingAddressFields,
|
|
|
|
adjustInvoiceDateField,
|
|
|
|
adjustDueDateField,
|
|
|
|
adjustAccountField,
|
|
|
|
);
|
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
export const adjustLeadFields = adjustAddressFields;
|
2021-05-07 08:28:42 -07:00
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
export const adjustPurchaseOrderFields = adjustInvoiceFields;
|
2021-05-07 08:28:42 -07:00
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
export const adjustQuoteFields = adjustInvoiceFields;
|
2021-05-07 08:28:42 -07:00
|
|
|
|
2021-04-29 02:56:19 -07:00
|
|
|
export const adjustSalesOrderFields = adjustInvoiceFields;
|
|
|
|
|
|
|
|
// ----------------------------------------
|
|
|
|
// helpers
|
|
|
|
// ----------------------------------------
|
|
|
|
|
2021-05-09 05:43:57 -07:00
|
|
|
const omit = (keyToOmit: string, { [keyToOmit]: _, ...omittedPropObj }) => omittedPropObj;
|
2021-04-29 02:56:19 -07:00
|
|
|
|
|
|
|
// ----------------------------------------
|
|
|
|
// types
|
|
|
|
// ----------------------------------------
|
|
|
|
|
|
|
|
type LocationType = 'Address' | 'Billing_Address' | 'Mailing_Address' | 'Shipping_Address' | 'Other_Address';
|
|
|
|
|
2021-05-07 08:28:42 -07:00
|
|
|
type DateType = 'Date_of_Birth' | 'Closing_Date' | 'Due_Date' | 'Invoice_Date';
|
2021-05-07 04:19:01 -07:00
|
|
|
|
2021-05-09 05:43:57 -07:00
|
|
|
export type AllFields =
|
|
|
|
{ [Date in DateType]?: string } &
|
|
|
|
{ [Location in LocationType]?: { address_fields: { [key: string]: string } } } &
|
|
|
|
{ Account_Name?: { account_name_fields: { [key: string]: string } } } &
|
|
|
|
IDataObject;
|
2021-05-09 07:30:16 -07:00
|
|
|
|
|
|
|
export type ProductDetails = Array<{ id: string, quantity: number }>;
|
|
|
|
|
|
|
|
type ZohoOAuth2ApiCredentials = {
|
|
|
|
oauthTokenData: {
|
|
|
|
api_domain: string;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export type LoadedProducts = Array<{
|
|
|
|
Product_Name: string;
|
|
|
|
id: string;
|
|
|
|
}>;
|