diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index 2a762e0476..a7ac4db33b 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -2,6 +2,16 @@ This list shows all the versions which include breaking changes and how to upgrade. +## 0.127.0 + +### What changed? + +For the Zoho node, the `lead:create` operation now requires a "Company" parameter, the parameter "Address" is now inside "Additional Options", and the parameters "Title" and "Is Duplicate Record" were removed. Also, the `lead:delete` operation now returns only the `id` of the deleted lead. + +### When is action necessary? + +If you are using `lead:create` with "Company" or "Address", reset the parameters; for the other two parameters, no action needed. If you are using the response from `lead:delete`, reselect the `id` key. + ## 0.118.0 ### What changed? diff --git a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts index d5e07a3877..fa862de03b 100644 --- a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts @@ -1,59 +1,434 @@ -import { +import { OptionsWithUri, } from 'request'; import { IExecuteFunctions, - IExecuteSingleFunctions, - ILoadOptionsFunctions, + IHookFunctions, } from 'n8n-core'; + import { - IDataObject, NodeApiError + IDataObject, + ILoadOptionsFunctions, + NodeApiError, + NodeOperationError, } from 'n8n-workflow'; -export async function zohoApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any - const { oauthTokenData: { api_domain } } = this.getCredentials('zohoOAuth2Api') as { [key: string]: IDataObject }; +import { + flow, + sortBy, +} from 'lodash'; + +import { + AllFields, + CamelCaseResource, + DateType, + GetAllFilterOptions, + IdType, + LoadedFields, + LoadedLayouts, + LocationType, + NameType, + ProductDetails, + ResourceItems, + SnakeCaseResource, + ZohoOAuth2ApiCredentials, +} from './types'; + +export async function zohoApiRequest( + this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + body: IDataObject = {}, + qs: IDataObject = {}, + uri?: string, +) { + const { oauthTokenData } = this.getCredentials('zohoOAuth2Api') as ZohoOAuth2ApiCredentials; const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/json', - }, - method, body: { data: [ body, ], }, + method, qs, - uri: uri || `${api_domain}/crm/v2${resource}`, + uri: uri ?? `${oauthTokenData.api_domain}/crm/v2${endpoint}`, json: true, }; + + if (!Object.keys(body).length) { + delete options.body; + } + + if (!Object.keys(qs).length) { + delete options.qs; + } + try { - //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'zohoOAuth2Api', options); + const responseData = await this.helpers.requestOAuth2?.call(this, 'zohoOAuth2Api', options); + + if (responseData === undefined) return []; + + throwOnErrorStatus.call(this, responseData); + + return responseData; } catch (error) { throw new NodeApiError(this.getNode(), error); } } -export async function zohoApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any - +/** + * Make an authenticated API request to Zoho CRM API and return all items. + */ +export async function zohoApiRequestAllItems( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + body: IDataObject = {}, + qs: IDataObject = {}, +) { const returnData: IDataObject[] = []; let responseData; let uri: string | undefined; - query.per_page = 200; - query.page = 0; + qs.per_page = 200; + qs.page = 0; do { - responseData = await zohoApiRequest.call(this, method, endpoint, body, query, uri); + responseData = await zohoApiRequest.call(this, method, endpoint, body, qs, uri); + if (Array.isArray(responseData) && !responseData.length) return returnData; + returnData.push(...responseData.data); uri = responseData.info.more_records; - returnData.push.apply(returnData, responseData[propertyName]); - query.page++; + qs.page++; } while ( responseData.info.more_records !== undefined && responseData.info.more_records === true ); return returnData; -} \ No newline at end of file +} + +/** + * 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 = {}, +) { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + + if (returnAll) { + return await zohoApiRequestAllItems.call(this, method, endpoint, body, qs); + } + + const responseData = await zohoApiRequestAllItems.call(this, method, endpoint, body, qs); + const limit = this.getNodeParameter('limit', 0) as number; + + return responseData.slice(0, limit); +} + +export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: CamelCaseResource) { + throw new NodeOperationError( + this.getNode(), + `Please enter at least one field to update for the ${resource}.`, + ); +} + +export function throwOnMissingProducts( + this: IExecuteFunctions, + resource: CamelCaseResource, + productDetails: ProductDetails, + ) { + if (!productDetails.length) { + throw new NodeOperationError( + this.getNode(), + `Please enter at least one product for the ${resource}.`, + ); + } +} + +export function throwOnErrorStatus( + this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, + responseData: { data?: Array<{ status: string, message: string }> }, +) { + if (responseData?.data?.[0].status === 'error') { + throw new NodeOperationError(this.getNode(), responseData as Error); + } +} + +// ---------------------------------------- +// required field adjusters +// ---------------------------------------- + +/** + * 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 }, + quantity: p.quantity || 1, + }; + }); +}; + +// ---------------------------------------- +// additional field adjusters +// ---------------------------------------- + +/** + * Place a product ID at a nested position in a product details field. + * + * Only for updating products from Invoice, Purchase Order, Quote, and Sales Order. + */ +export const adjustProductDetailsOnUpdate = (allFields: AllFields) => { + if (!allFields.Product_Details) return allFields; + + return allFields.Product_Details.map(p => { + return { + ...omit('product', p), + product: { id: p.id }, + quantity: p.quantity || 1, + }; + }); +}; + +/** + * Place a location field's contents at the top level of the payload. + */ +const adjustLocationFields = (locationType: LocationType) => (allFields: AllFields) => { + const locationField = allFields[locationType]; + + if (!locationField) return allFields; + + return { + ...omit(locationType, allFields), + ...locationField.address_fields, + }; +}; + +const adjustAddressFields = adjustLocationFields('Address'); +const adjustBillingAddressFields = adjustLocationFields('Billing_Address'); +const adjustMailingAddressFields = adjustLocationFields('Mailing_Address'); +const adjustShippingAddressFields = adjustLocationFields('Shipping_Address'); +const adjustOtherAddressFields = adjustLocationFields('Other_Address'); + +/** + * Remove from a date field the timestamp set by the datepicker. + */ + const adjustDateField = (dateType: DateType) => (allFields: AllFields) => { + const dateField = allFields[dateType]; + + if (!dateField) return allFields; + + allFields[dateType] = dateField.split('T')[0]; + + return allFields; +}; + +const adjustDateOfBirthField = adjustDateField('Date_of_Birth'); +const adjustClosingDateField = adjustDateField('Closing_Date'); +const adjustInvoiceDateField = adjustDateField('Invoice_Date'); +const adjustDueDateField = adjustDateField('Due_Date'); +const adjustPurchaseOrderDateField = adjustDateField('PO_Date'); +const adjustValidTillField = adjustDateField('Valid_Till'); + +/** + * Place an ID field's value nested inside the payload. + */ +const adjustIdField = (idType: IdType, nameProperty: NameType) => (allFields: AllFields) => { + const idValue = allFields[idType]; + + if (!idValue) return allFields; + + return { + ...omit(idType, allFields), + [nameProperty]: { id: idValue }, + }; +}; + +const adjustAccountIdField = adjustIdField('accountId', 'Account_Name'); +const adjustContactIdField = adjustIdField('contactId', 'Full_Name'); +const adjustDealIdField = adjustIdField('dealId', 'Deal_Name'); + +const adjustCustomFields = (allFields: AllFields) => { + const { customFields, ...rest } = allFields; + + if (!customFields?.customFields.length) return allFields; + + return customFields.customFields.reduce((acc, cur) => { + acc[cur.fieldId] = cur.value; + return acc; + }, rest); +}; + +// ---------------------------------------- +// payload adjusters +// ---------------------------------------- + +export const adjustAccountPayload = flow( + adjustBillingAddressFields, + adjustShippingAddressFields, + adjustCustomFields, +); + +export const adjustContactPayload = flow( + adjustMailingAddressFields, + adjustOtherAddressFields, + adjustDateOfBirthField, + adjustCustomFields, +); + +export const adjustDealPayload = flow( + adjustClosingDateField, + adjustCustomFields, +); + +export const adjustInvoicePayload = flow( + adjustBillingAddressFields, + adjustShippingAddressFields, + adjustInvoiceDateField, + adjustDueDateField, + adjustAccountIdField, + adjustCustomFields, +); + +export const adjustInvoicePayloadOnUpdate = flow( + adjustInvoicePayload, + adjustProductDetailsOnUpdate, +); + +export const adjustLeadPayload = flow( + adjustAddressFields, + adjustCustomFields, +); + +export const adjustPurchaseOrderPayload = flow( + adjustBillingAddressFields, + adjustShippingAddressFields, + adjustDueDateField, + adjustPurchaseOrderDateField, + adjustCustomFields, +); + +export const adjustQuotePayload = flow( + adjustBillingAddressFields, + adjustShippingAddressFields, + adjustValidTillField, + adjustCustomFields, +); + +export const adjustSalesOrderPayload = flow( + adjustBillingAddressFields, + adjustShippingAddressFields, + adjustDueDateField, + adjustAccountIdField, + adjustContactIdField, + adjustDealIdField, + adjustCustomFields, +); + +export const adjustVendorPayload = flow( + adjustAddressFields, + adjustCustomFields, +); + +export const adjustProductPayload = adjustCustomFields; + +// ---------------------------------------- +// helpers +// ---------------------------------------- + +/** + * Create a copy of an object without a specific property. + */ +const omit = (propertyToOmit: string, { [propertyToOmit]: _, ...remainingObject }) => remainingObject; + +/** + * Convert items in a Zoho CRM API response into n8n load options. + */ +export const toLoadOptions = (items: ResourceItems, nameProperty: NameType) => + items.map((item) => ({ name: item[nameProperty], value: item.id })); + +/** + * Retrieve all fields for a resource, sorted alphabetically. + */ +export async function getFields( + this: ILoadOptionsFunctions, + resource: SnakeCaseResource, + { onlyCustom } = { onlyCustom: false }, +) { + const qs = { module: getModuleName(resource) }; + + let { fields } = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, qs) as LoadedFields; + + if (onlyCustom) { + fields = fields.filter(({ custom_field }) => custom_field); + } + + const options = fields.map(({ field_label, api_name }) => ({ name: field_label, value: api_name })); + + return sortBy(options, o => o.name); +} + +export function getModuleName(resource: string) { + const map: { [key: string]: string } = { + account: 'Accounts', + contact: 'Contacts', + deal: 'Deals', + invoice: 'Invoices', + lead: 'Leads', + product: 'Products', + purchaseOrder: 'Purchase_Orders', + salesOrder: 'Sales_Orders', + vendor: 'Vendors', + quote: 'Quotes', + }; + + return map[resource]; +} + +export async function getPicklistOptions( + this: ILoadOptionsFunctions, + resource: string, + targetField: string, +) { + const qs = { module: getModuleName(resource) }; + const responseData = await zohoApiRequest.call(this, 'GET', '/settings/layouts', {}, qs) as LoadedLayouts; + + const pickListOptions = responseData.layouts[0] + .sections.find(section => section.api_name === getSectionApiName(resource)) + ?.fields.find(f => f.api_name === targetField) + ?.pick_list_values; + + if (!pickListOptions) return []; + + return pickListOptions.map( + (option) => ({ name: option.display_value, value: option.actual_value }), + ); +} + + +function getSectionApiName(resource: string) { + if (resource === 'purchaseOrder') return 'Purchase Order Information'; + if (resource === 'salesOrder') return 'Sales Order Information'; + + return `${capitalizeInitial(resource)} Information`; +} + +/** + * Add filter options to a query string object. + */ +export const addGetAllFilterOptions = (qs: IDataObject, options: GetAllFilterOptions) => { + if (Object.keys(options).length) { + const { fields, ...rest } = options; + Object.assign(qs, fields && { fields: fields.join(',') }, rest); + } +}; + +export const capitalizeInitial = (str: string) => str[0].toUpperCase() + str.slice(1); diff --git a/packages/nodes-base/nodes/Zoho/LeadDescription.ts b/packages/nodes-base/nodes/Zoho/LeadDescription.ts deleted file mode 100644 index d232098ca7..0000000000 --- a/packages/nodes-base/nodes/Zoho/LeadDescription.ts +++ /dev/null @@ -1,706 +0,0 @@ -import { INodeProperties } from 'n8n-workflow'; - -export const leadOperations = [ - { - displayName: 'Operation', - name: 'operation', - type: 'options', - displayOptions: { - show: { - resource: [ - 'lead', - ], - }, - }, - options: [ - { - name: 'Create', - value: 'create', - description: 'Create a new lead', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete a lead', - }, - { - name: 'Get', - value: 'get', - description: 'Get data of a lead', - }, - { - name: 'Get All', - value: 'getAll', - description: 'Get data of all leads', - }, - { - name: 'Get Fields', - value: 'getFields', - description: `Get the fields' metadata`, - }, - { - name: 'Update', - value: 'update', - description: 'Update a lead', - }, - ], - default: 'create', - description: 'The operation to perform.', - }, -] as INodeProperties[]; - -export const leadFields = [ - -/* -------------------------------------------------------------------------- */ -/* lead:create */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Last Name', - name: 'lastName', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'create', - ], - }, - }, - description: `User's last name`, - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - operation: [ - 'create', - ], - resource: [ - 'lead', - ], - }, - }, - options: [ - { - displayName: 'Annual Revenue', - name: 'annualRevenue', - type: 'number', - typeOptions: { - numberPrecision: 2, - }, - default: 0, - }, - { - displayName: 'Company', - name: 'company', - type: 'string', - default: '', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - }, - { - displayName: 'Email', - name: 'email', - type: 'string', - default: '', - }, - { - displayName: 'Email Opt Out', - name: 'emailOptOut', - type: 'boolean', - default: false, - }, - { - displayName: 'Fax', - name: 'fax', - type: 'string', - default: '', - }, - { - displayName: 'First Name', - name: 'firstName', - type: 'string', - default: '', - }, - { - displayName: 'Industry', - name: 'industry', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getIndustries', - }, - default: '', - }, - { - displayName: 'Is Record Duplicate', - name: 'isRecordDuplicate', - type: 'boolean', - default: false, - }, - { - displayName: 'Lead Source', - name: 'leadSource', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getLeadSources', - }, - default: '', - }, - { - displayName: 'Lead Status', - name: 'leadStatus', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getLeadStatuses', - }, - default: '', - }, - { - displayName: 'Mobile', - name: 'mobile', - type: 'string', - default: '', - }, - { - displayName: 'No. of Employees', - name: 'numberOfEmployees', - type: 'number', - default: 1, - }, - { - displayName: 'Owner', - name: 'owner', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getUsers', - }, - }, - { - displayName: 'Phone', - name: 'phone', - type: 'string', - default: '', - }, - { - displayName: 'Salutation', - name: 'salutation', - type: 'string', - default: '', - }, - { - displayName: 'Secondary Email', - name: 'secondaryEmail', - type: 'string', - default: '', - }, - { - displayName: 'Skype ID', - name: 'SkypeId', - type: 'string', - default: '', - }, - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - }, - { - displayName: 'Twitter', - name: 'twitter', - type: 'string', - default: '', - }, - { - displayName: 'Website', - name: 'website', - type: 'string', - default: '', - }, - ], - }, - { - displayName: 'Address', - name: 'addressUi', - type: 'fixedCollection', - default: {}, - placeholder: 'Add Address', - typeOptions: { - multipleValues: false, - }, - required: false, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'create', - ], - }, - }, - options: [ - { - name: 'addressValues', - displayName: 'Address', - values: [ - { - displayName: 'Street', - name: 'street', - type: 'string', - default: '', - }, - { - displayName: 'City', - name: 'city', - type: 'string', - default: '', - }, - { - displayName: 'State', - name: 'state', - type: 'string', - default: '', - }, - { - displayName: 'Country', - name: 'country', - type: 'string', - default: '', - }, - { - displayName: 'Zip Code', - name: 'zipCode', - type: 'string', - default: '', - }, - ], - }, - ], - }, -/* -------------------------------------------------------------------------- */ -/* lead:update */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Lead ID', - name: 'leadId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'update', - ], - }, - }, - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - operation: [ - 'update', - ], - resource: [ - 'lead', - ], - }, - }, - options: [ - { - displayName: 'Annual Revenue', - name: 'annualRevenue', - type: 'number', - typeOptions: { - numberPrecision: 2, - }, - default: 0, - }, - { - displayName: 'Company', - name: 'company', - type: 'string', - default: '', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - }, - { - displayName: 'Email', - name: 'email', - type: 'string', - default: '', - }, - { - displayName: 'Email Opt Out', - name: 'emailOptOut', - type: 'boolean', - default: false, - }, - { - displayName: 'Fax', - name: 'fax', - type: 'string', - default: '', - }, - { - displayName: 'First Name', - name: 'firstName', - type: 'string', - default: '', - }, - { - displayName: 'Industry', - name: 'industry', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getIndustries', - }, - default: '', - }, - { - displayName: 'Is Record Duplicate', - name: 'isRecordDuplicate', - type: 'boolean', - default: false, - }, - { - displayName: 'Last Name', - name: 'lastName', - type: 'string', - default: '', - description: `User's last name`, - }, - { - displayName: 'Lead Source', - name: 'leadSource', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getLeadSources', - }, - default: '', - }, - { - displayName: 'Lead Status', - name: 'leadStatus', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getLeadStatuses', - }, - default: '', - }, - { - displayName: 'Mobile', - name: 'mobile', - type: 'string', - default: '', - }, - { - displayName: 'No. of Employees', - name: 'numberOfEmployees', - type: 'number', - default: 1, - }, - { - displayName: 'Owner', - name: 'owner', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getUsers', - }, - }, - { - displayName: 'Phone', - name: 'phone', - type: 'string', - default: '', - }, - { - displayName: 'Salutation', - name: 'salutation', - type: 'string', - default: '', - }, - { - displayName: 'Secondary Email', - name: 'secondaryEmail', - type: 'string', - default: '', - }, - { - displayName: 'Skype ID', - name: 'SkypeId', - type: 'string', - default: '', - }, - { - displayName: 'Title', - name: 'title', - type: 'string', - default: '', - }, - { - displayName: 'Twitter', - name: 'twitter', - type: 'string', - default: '', - }, - { - displayName: 'Website', - name: 'website', - type: 'string', - default: '', - }, - ], - }, - { - displayName: 'Address', - name: 'addressUi', - type: 'fixedCollection', - default: {}, - placeholder: 'Add Address', - typeOptions: { - multipleValues: false, - }, - required: false, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'update', - ], - }, - }, - options: [ - { - name: 'addressValues', - displayName: 'Address', - values: [ - { - displayName: 'Street', - name: 'street', - type: 'string', - default: '', - }, - { - displayName: 'City', - name: 'city', - type: 'string', - default: '', - }, - { - displayName: 'State', - name: 'state', - type: 'string', - default: '', - }, - { - displayName: 'Country', - name: 'country', - type: 'string', - default: '', - }, - { - displayName: 'Zip Code', - name: 'zipCode', - type: 'string', - default: '', - }, - ], - }, - ], - }, -/* -------------------------------------------------------------------------- */ -/* lead:get */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Lead ID', - name: 'leadId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'get', - ], - }, - }, - }, -/* -------------------------------------------------------------------------- */ -/* lead:getAll */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'getAll', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'getAll', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 200, - }, - default: 100, - description: 'How many results to return.', - }, - { - displayName: 'Options', - name: 'options', - type: 'collection', - placeholder: 'Add Option', - default: {}, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'getAll', - ], - }, - }, - options: [ - { - displayName: 'Approved', - name: 'approved', - type: 'boolean', - default: true, - description: 'To get the list of approved records. Default value is true.', - }, - { - displayName: 'Converted', - name: 'converted', - type: 'boolean', - default: false, - description: 'To get the list of converted records. Default value is false', - }, - { - displayName: 'Fields', - name: 'fields', - type: 'multiOptions', - typeOptions: { - loadOptionsMethod: 'getLeadFields', - }, - default: [], - }, - { - displayName: 'Include Child', - name: 'includeChild', - type: 'boolean', - default: false, - description: 'To include records from the child territories. True includes child territory records', - }, - { - displayName: 'Sort By', - name: 'sortBy', - type: 'multiOptions', - typeOptions: { - loadOptionsMethod: 'getLeadFields', - }, - default: [], - }, - { - displayName: 'Sort Order', - name: 'sortOrder', - type: 'options', - options: [ - { - name: 'ASC', - value: 'asc', - }, - { - name: 'DESC', - value: 'desc', - }, - ], - default: 'desc', - description: 'Order sort attribute ascending or descending.', - }, - { - displayName: 'Territory ID', - name: 'territoryId', - type: 'string', - default: '', - description: 'To get the list of records based on the territory ', - }, - ], - }, -/* -------------------------------------------------------------------------- */ -/* lead:delete */ -/* -------------------------------------------------------------------------- */ - { - displayName: 'Lead ID', - name: 'leadId', - type: 'string', - default: '', - required: true, - displayOptions: { - show: { - resource: [ - 'lead', - ], - operation: [ - 'delete', - ], - }, - }, - }, -] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/LeadInterface.ts b/packages/nodes-base/nodes/Zoho/LeadInterface.ts deleted file mode 100644 index 957f816cb6..0000000000 --- a/packages/nodes-base/nodes/Zoho/LeadInterface.ts +++ /dev/null @@ -1,37 +0,0 @@ -export interface ILead { - Annual_Revenue?: number; - City?: string; - Company?: string; - Country?: string; - Description?: string; - Designation?: string; - Email?: string; - Email_Opt_Out?: boolean; - Fax?: string; - First_Name?: string; - Industry?: string; - Is_Record_Duplicate?: boolean; - Last_Name?: string; - Lead_Owner?: string; - Lead_Source?: string; - Lead_Status?: string; - Mobile?: string; - No_of_Employees?: number; - Phone?: string; - Salutation?: string; - Secondary_Email?: string; - Skype_ID?: string; - State?: string; - Street?: string; - Twitter?: string; - Website?: string; - Zip_Code?: string; -} - -export interface IAddress { - street?: string; - city?: string; - state?: string; - country?: string; - zipCode?: string; -} diff --git a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts index 8380f56049..da7c713551 100644 --- a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts +++ b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts @@ -6,37 +6,80 @@ import { IDataObject, ILoadOptionsFunctions, INodeExecutionData, - INodePropertyOptions, INodeType, INodeTypeDescription, } from 'n8n-workflow'; import { + addGetAllFilterOptions, + adjustAccountPayload, + adjustContactPayload, + adjustDealPayload, + adjustInvoicePayload, + adjustInvoicePayloadOnUpdate, + adjustLeadPayload, + adjustProductDetails, + adjustProductPayload, + adjustPurchaseOrderPayload, + adjustQuotePayload, + adjustSalesOrderPayload, + adjustVendorPayload, + getFields, + getModuleName, + getPicklistOptions, + handleListing, + throwOnEmptyUpdate, + throwOnMissingProducts, + toLoadOptions, zohoApiRequest, zohoApiRequestAllItems, } from './GenericFunctions'; import { - leadFields, - leadOperations, -} from './LeadDescription'; + CamelCaseResource, + GetAllFilterOptions, + LoadedAccounts, + LoadedContacts, + LoadedDeals, + LoadedProducts, + LoadedVendors, + ProductDetails, +} from './types'; import { - IAddress, - ILead, -} from './LeadInterface'; + accountFields, + accountOperations, + contactFields, + contactOperations, + dealFields, + dealOperations, + invoiceFields, + invoiceOperations, + leadFields, + leadOperations, + productFields, + productOperations, + purchaseOrderFields, + purchaseOrderOperations, + quoteFields, + quoteOperations, + salesOrderFields, + salesOrderOperations, + vendorFields, + vendorOperations, +} from './descriptions'; export class ZohoCrm implements INodeType { description: INodeTypeDescription = { displayName: 'Zoho CRM', name: 'zohoCrm', icon: 'file:zoho.svg', + group: ['transform'], subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', - group: ['input'], version: 1, - description: 'Consume Zoho CRM API.', + description: 'Consume the Zoho API', defaults: { - name: 'Zoho CRM', + name: 'Zoho', color: '#CE2232', }, inputs: ['main'], @@ -53,134 +96,222 @@ export class ZohoCrm implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Account', + value: 'account', + }, + { + name: 'Contact', + value: 'contact', + }, + { + name: 'Deal', + value: 'deal', + }, + { + name: 'Invoice', + value: 'invoice', + }, { name: 'Lead', value: 'lead', }, + { + name: 'Product', + value: 'product', + }, + { + name: 'Purchase Order', + value: 'purchaseOrder', + }, + { + name: 'Quote', + value: 'quote', + }, + { + name: 'Sales Order', + value: 'salesOrder', + }, + { + name: 'Vendor', + value: 'vendor', + }, ], - default: 'lead', - description: 'The resource to operate on.', + default: 'account', + description: 'Resource to consume', }, + ...accountOperations, + ...accountFields, + ...contactOperations, + ...contactFields, + ...dealOperations, + ...dealFields, + ...invoiceOperations, + ...invoiceFields, ...leadOperations, ...leadFields, + ...productOperations, + ...productFields, + ...purchaseOrderOperations, + ...purchaseOrderFields, + ...quoteOperations, + ...quoteFields, + ...salesOrderOperations, + ...salesOrderFields, + ...vendorOperations, + ...vendorFields, ], }; methods = { loadOptions: { - // Get all the available users to display them to user so that he can - // select them easily - async getUsers(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const { users } = await zohoApiRequest.call(this, 'GET', '/users', {}, { type: 'AllUsers' }); - for (const user of users) { - const userName = `${user.first_name} ${user.last_name}`; - const userId = user.profile.id; - returnData.push({ - name: userName, - value: userId, - }); - } - return returnData; + // ---------------------------------------- + // resources + // ---------------------------------------- + + async getAccounts(this: ILoadOptionsFunctions) { + const accounts = await zohoApiRequestAllItems.call(this, 'GET', '/accounts') as LoadedAccounts; + return toLoadOptions(accounts, 'Account_Name'); }, - // Get all the available accounts to display them to user so that he can - // select them easily - async getAccounts(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const qs: IDataObject = {}; - qs.sort_by = 'Created_Time'; - qs.sort_order = 'desc'; - const { data } = await zohoApiRequest.call(this, 'GET', '/accounts', {}, qs); - for (const account of data) { - const accountName = account.Account_Name; - const accountId = account.id; - returnData.push({ - name: accountName, - value: accountId, - }); - } - return returnData; + + async getContacts(this: ILoadOptionsFunctions) { + const contacts = await zohoApiRequestAllItems.call(this, 'GET', '/contacts') as LoadedContacts; + return toLoadOptions(contacts, 'Full_Name'); }, - // Get all the available lead statuses to display them to user so that he can - // select them easily - async getLeadStatuses(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const qs: IDataObject = {}; - qs.module = 'leads'; - const { fields } = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, qs); - for (const field of fields) { - if (field.api_name === 'Lead_Status') { - for (const value of field.pick_list_values) { - const valueName = value.display_value; - const valueId = value.actual_value; - returnData.push({ - name: valueName, - value: valueId, - }); - return returnData; - } - } - } - return returnData; + + async getDeals(this: ILoadOptionsFunctions) { + const deals = await zohoApiRequestAllItems.call(this, 'GET', '/deals') as LoadedDeals; + return toLoadOptions(deals, 'Deal_Name'); }, - // Get all the available lead sources to display them to user so that he can - // select them easily - async getLeadSources(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const qs: IDataObject = {}; - qs.module = 'leads'; - const { fields } = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, qs); - for (const field of fields) { - if (field.api_name === 'Lead_Source') { - for (const value of field.pick_list_values) { - const valueName = value.display_value; - const valueId = value.actual_value; - returnData.push({ - name: valueName, - value: valueId, - }); - return returnData; - } - } - } - return returnData; + + async getProducts(this: ILoadOptionsFunctions) { + const products = await zohoApiRequestAllItems.call(this, 'GET', '/products') as LoadedProducts; + return toLoadOptions(products, 'Product_Name'); }, - // Get all the available industries to display them to user so that he can - // select them easily - async getIndustries(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const qs: IDataObject = {}; - qs.module = 'leads'; - const { fields } = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, qs); - for (const field of fields) { - if (field.api_name === 'Industry') { - for (const value of field.pick_list_values) { - const valueName = value.display_value; - const valueId = value.actual_value; - returnData.push({ - name: valueName, - value: valueId, - }); - return returnData; - } - } - } - return returnData; + + async getVendors(this: ILoadOptionsFunctions) { + const vendors = await zohoApiRequestAllItems.call(this, 'GET', '/vendors') as LoadedVendors; + return toLoadOptions(vendors, 'Vendor_Name'); }, - // Get all the available lead fields to display them to user so that he can - // select them easily - async getLeadFields(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const qs: IDataObject = {}; - qs.module = 'leads'; - const { fields } = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, qs); - for (const field of fields) { - returnData.push({ - name: field.field_label, - value: field.api_name, - }); - } - return returnData; + + // ---------------------------------------- + // resource fields + // ---------------------------------------- + + // standard fields - called from `makeGetAllFields` + + async getAccountFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'account'); + }, + + async getContactFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'contact'); + }, + + async getDealFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'deal'); + }, + + async getInvoiceFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'invoice'); + }, + + async getLeadFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'lead'); + }, + + async getProductFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'product'); + }, + + async getPurchaseOrderFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'purchase_order'); + }, + + async getVendorOrderFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'vendor'); + }, + + async getQuoteFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'quote'); + }, + + async getSalesOrderFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'sales_order'); + }, + + async getVendorFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'vendor'); + }, + + // custom fields + + async getCustomAccountFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'account', { onlyCustom: true }); + }, + + async getCustomContactFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'contact', { onlyCustom: true }); + }, + + async getCustomDealFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'deal', { onlyCustom: true }); + }, + + async getCustomInvoiceFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'invoice', { onlyCustom: true }); + }, + + async getCustomLeadFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'lead', { onlyCustom: true }); + }, + + async getCustomProductFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'product', { onlyCustom: true }); + }, + + async getCustomPurchaseOrderFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'purchase_order', { onlyCustom: true }); + }, + + async getCustomVendorOrderFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'vendor', { onlyCustom: true }); + }, + + async getCustomQuoteFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'quote', { onlyCustom: true }); + }, + + async getCustomSalesOrderFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'sales_order', { onlyCustom: true }); + }, + + async getCustomVendorFields(this: ILoadOptionsFunctions) { + return getFields.call(this, 'vendor', { onlyCustom: true }); + }, + + // ---------------------------------------- + // resource picklist options + // ---------------------------------------- + + async getAccountType(this: ILoadOptionsFunctions) { + return getPicklistOptions.call(this, 'account', 'Account_Type'); + }, + + async getDealStage(this: ILoadOptionsFunctions) { + return getPicklistOptions.call(this, 'deal', 'Stage'); + }, + + async getPurchaseOrderStatus(this: ILoadOptionsFunctions) { + return getPicklistOptions.call(this, 'purchaseOrder', 'Status'); + }, + + async getSalesOrderStatus(this: ILoadOptionsFunctions) { + return getPicklistOptions.call(this, 'salesOrder', 'Status'); + }, + + async getQuoteStage(this: ILoadOptionsFunctions) { + return getPicklistOptions.call(this, 'quote', 'Quote_Stage'); }, }, }; @@ -188,265 +319,1147 @@ export class ZohoCrm implements INodeType { async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; - const length = items.length as unknown as number; - const qs: IDataObject = {}; + + const resource = this.getNodeParameter('resource', 0) as CamelCaseResource; + const operation = this.getNodeParameter('operation', 0) as string; + const resolveData = this.getNodeParameter('resolveData', 0, false) as boolean; + let responseData; - for (let i = 0; i < length; i++) { - const resource = this.getNodeParameter('resource', 0) as string; - const operation = this.getNodeParameter('operation', 0) as string; - if (resource === 'lead') { - //https://www.zoho.com/crm/developer/docs/api/insert-records.html - if (operation === 'create') { - const lastName = this.getNodeParameter('lastName', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - const body: ILead = { - Last_Name: lastName, - }; - if (additionalFields.owner) { - body.Lead_Owner = additionalFields.owner as string; - } - if (additionalFields.company) { - body.Company = additionalFields.company as string; - } - if (additionalFields.firstName) { - body.First_Name = additionalFields.firstName as string; - } - if (additionalFields.email) { - body.Email = additionalFields.email as string; - } - if (additionalFields.title) { - body.Designation = additionalFields.title as string; - } - if (additionalFields.phone) { - body.Phone = additionalFields.phone as string; - } - if (additionalFields.mobile) { - body.Mobile = additionalFields.mobile as string; - } - if (additionalFields.leadStatus) { - body.Lead_Status = additionalFields.leadStatus as string; - } - if (additionalFields.fax) { - body.Fax = additionalFields.fax as string; - } - if (additionalFields.website) { - body.Website = additionalFields.website as string; - } - if (additionalFields.leadSource) { - body.Lead_Source = additionalFields.leadSource as string; - } - if (additionalFields.industry) { - body.Industry = additionalFields.industry as string; - } - if (additionalFields.numberOfEmployees) { - body.No_of_Employees = additionalFields.numberOfEmployees as number; - } - if (additionalFields.annualRevenue) { - body.Annual_Revenue = additionalFields.annualRevenue as number; - } - if (additionalFields.emailOptOut) { - body.Email_Opt_Out = additionalFields.emailOptOut as boolean; - } - if (additionalFields.skypeId) { - body.Skype_ID = additionalFields.skypeId as string; - } - if (additionalFields.salutation) { - body.Salutation = additionalFields.salutation as string; - } - if (additionalFields.secondaryEmail) { - body.Secondary_Email = additionalFields.secondaryEmail as string; - } - if (additionalFields.twitter) { - body.Twitter = additionalFields.twitter as string; - } - if (additionalFields.isRecordDuplicate) { - body.Is_Record_Duplicate = additionalFields.isRecordDuplicate as boolean; - } - if (additionalFields.description) { - body.Description = additionalFields.description as string; - } - const address = (this.getNodeParameter('addressUi', i) as IDataObject).addressValues as IAddress; - if (address) { - if (address.country) { - body.Country = address.country as string; - } - if (address.city) { - body.City = address.city as string; - } - if (address.state) { - body.State = address.state as string; - } - if (address.street) { - body.Street = address.street as string; - } - if (address.zipCode) { - body.Zip_Code = address.zipCode as string; - } - } - responseData = await zohoApiRequest.call(this, 'POST', '/leads', body); - responseData = responseData.data; - if (responseData.length) { - responseData = responseData[0].details; - } - } - //https://www.zoho.com/crm/developer/docs/api/update-specific-record.html - if (operation === 'update') { - const leadId = this.getNodeParameter('leadId', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - const body: ILead = {}; - if (additionalFields.lastName) { - body.Last_Name = additionalFields.lastName as string; - } - if (additionalFields.owner) { - body.Lead_Owner = additionalFields.owner as string; - } - if (additionalFields.company) { - body.Company = additionalFields.company as string; - } - if (additionalFields.firstName) { - body.First_Name = additionalFields.firstName as string; - } - if (additionalFields.email) { - body.Email = additionalFields.email as string; - } - if (additionalFields.title) { - body.Designation = additionalFields.title as string; - } - if (additionalFields.phone) { - body.Phone = additionalFields.phone as string; - } - if (additionalFields.mobile) { - body.Mobile = additionalFields.mobile as string; - } - if (additionalFields.leadStatus) { - body.Lead_Status = additionalFields.leadStatus as string; - } - if (additionalFields.fax) { - body.Fax = additionalFields.fax as string; - } - if (additionalFields.website) { - body.Website = additionalFields.website as string; - } - if (additionalFields.leadSource) { - body.Lead_Source = additionalFields.leadSource as string; - } - if (additionalFields.industry) { - body.Industry = additionalFields.industry as string; - } - if (additionalFields.numberOfEmployees) { - body.No_of_Employees = additionalFields.numberOfEmployees as number; - } - if (additionalFields.annualRevenue) { - body.Annual_Revenue = additionalFields.annualRevenue as number; - } - if (additionalFields.emailOptOut) { - body.Email_Opt_Out = additionalFields.emailOptOut as boolean; - } - if (additionalFields.skypeId) { - body.Skype_ID = additionalFields.skypeId as string; - } - if (additionalFields.salutation) { - body.Salutation = additionalFields.salutation as string; - } - if (additionalFields.secondaryEmail) { - body.Secondary_Email = additionalFields.secondaryEmail as string; - } - if (additionalFields.twitter) { - body.Twitter = additionalFields.twitter as string; - } - if (additionalFields.isRecordDuplicate) { - body.Is_Record_Duplicate = additionalFields.isRecordDuplicate as boolean; - } - if (additionalFields.description) { - body.Description = additionalFields.description as string; - } - const address = (this.getNodeParameter('addressUi', i) as IDataObject).addressValues as IAddress; - if (address) { - if (address.country) { - body.Country = address.country as string; - } - if (address.city) { - body.City = address.city as string; - } - if (address.state) { - body.State = address.state as string; - } - if (address.street) { - body.Street = address.street as string; - } - if (address.zipCode) { - body.Zip_Code = address.zipCode as string; - } - } - responseData = await zohoApiRequest.call(this, 'PUT', `/leads/${leadId}`, body); - responseData = responseData.data; + for (let i = 0; i < items.length; i++) { - if (responseData.length) { - responseData = responseData[0].details; - } - } - //https://www.zoho.com/crm/developer/docs/api/update-specific-record.html - if (operation === 'get') { - const leadId = this.getNodeParameter('leadId', i) as string; - responseData = await zohoApiRequest.call(this, 'GET', `/leads/${leadId}`); - if (responseData !== undefined) { + // https://www.zoho.com/crm/developer/docs/api/insert-records.html + // https://www.zoho.com/crm/developer/docs/api/get-records.html + // https://www.zoho.com/crm/developer/docs/api/update-specific-record.html + // https://www.zoho.com/crm/developer/docs/api/delete-specific-record.html + // https://www.zoho.com/crm/developer/docs/api/v2/upsert-records.html + + try { + + if (resource === 'account') { + + // ********************************************************************** + // account + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/accounts-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Accounts + + if (operation === 'create') { + + // ---------------------------------------- + // account: create + // ---------------------------------------- + + const body: IDataObject = { + Account_Name: this.getNodeParameter('accountName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustAccountPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/accounts', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // account: delete + // ---------------------------------------- + + const accountId = this.getNodeParameter('accountId', i); + + const endpoint = `/accounts/${accountId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // account: get + // ---------------------------------------- + + const accountId = this.getNodeParameter('accountId', i); + + const endpoint = `/accounts/${accountId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // account: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/accounts', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // account: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustAccountPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const accountId = this.getNodeParameter('accountId', i); + + const endpoint = `/accounts/${accountId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // account: upsert + // ---------------------------------------- + + const body: IDataObject = { + Account_Name: this.getNodeParameter('accountName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustAccountPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/accounts/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'contact') { + + // ********************************************************************** + // contact + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/contacts-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Contacts + + if (operation === 'create') { + + // ---------------------------------------- + // contact: create + // ---------------------------------------- + + const body: IDataObject = { + Last_Name: this.getNodeParameter('lastName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustContactPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/contacts', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // contact: delete + // ---------------------------------------- + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // contact: get + // ---------------------------------------- + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // contact: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/contacts', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // contact: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustContactPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const contactId = this.getNodeParameter('contactId', i); + + const endpoint = `/contacts/${contactId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // contact: upsert + // ---------------------------------------- + + const body: IDataObject = { + Last_Name: this.getNodeParameter('lastName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustContactPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/contacts/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'deal') { + + // ********************************************************************** + // deal + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/deals-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Deals + + if (operation === 'create') { + + // ---------------------------------------- + // deal: create + // ---------------------------------------- + + const body: IDataObject = { + Deal_Name: this.getNodeParameter('dealName', i), + Stage: this.getNodeParameter('stage', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustDealPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/deals', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // deal: delete + // ---------------------------------------- + + const dealId = this.getNodeParameter('dealId', i); + + responseData = await zohoApiRequest.call(this, 'DELETE', `/deals/${dealId}`); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // deal: get + // ---------------------------------------- + + const dealId = this.getNodeParameter('dealId', i); + + responseData = await zohoApiRequest.call(this, 'GET', `/deals/${dealId}`); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // deal: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/deals', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // deal: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustDealPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const dealId = this.getNodeParameter('dealId', i); + + responseData = await zohoApiRequest.call(this, 'PUT', `/deals/${dealId}`, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // deal: upsert + // ---------------------------------------- + + const body: IDataObject = { + Deal_Name: this.getNodeParameter('dealName', i), + Stage: this.getNodeParameter('stage', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustDealPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/deals/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'invoice') { + + // ********************************************************************** + // invoice + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/invoices-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Invoices + + if (operation === 'create') { + + // ---------------------------------------- + // invoice: create + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + throwOnMissingProducts.call(this, resource, productDetails); + + const body: IDataObject = { + Subject: this.getNodeParameter('subject', i), + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustInvoicePayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/invoices', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // invoice: delete + // ---------------------------------------- + + const invoiceId = this.getNodeParameter('invoiceId', i); + + const endpoint = `/invoices/${invoiceId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // invoice: get + // ---------------------------------------- + + const invoiceId = this.getNodeParameter('invoiceId', i); + + const endpoint = `/invoices/${invoiceId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // invoice: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/invoices', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // invoice: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustInvoicePayloadOnUpdate(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const invoiceId = this.getNodeParameter('invoiceId', i); + + const endpoint = `/invoices/${invoiceId}`; + + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // invoice: upsert + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + const body: IDataObject = { + Subject: this.getNodeParameter('subject', i), + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustInvoicePayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/invoices/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'lead') { + + // ********************************************************************** + // lead + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/leads-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Leads + + if (operation === 'create') { + + // ---------------------------------------- + // lead: create + // ---------------------------------------- + + const body: IDataObject = { + Company: this.getNodeParameter('Company', i), + Last_Name: this.getNodeParameter('lastName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustLeadPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/leads', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // lead: delete + // ---------------------------------------- + + const leadId = this.getNodeParameter('leadId', i); + + responseData = await zohoApiRequest.call(this, 'DELETE', `/leads/${leadId}`); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // lead: get + // ---------------------------------------- + + const leadId = this.getNodeParameter('leadId', i); + + responseData = await zohoApiRequest.call(this, 'GET', `/leads/${leadId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // lead: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/leads', {}, qs); + + } else if (operation === 'getFields') { + + // ---------------------------------------- + // lead: getFields + // ---------------------------------------- + + responseData = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, { module: 'leads' }); + responseData = responseData.fields; + + } else if (operation === 'update') { + + // ---------------------------------------- + // lead: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustLeadPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const leadId = this.getNodeParameter('leadId', i); + + responseData = await zohoApiRequest.call(this, 'PUT', `/leads/${leadId}`, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // lead: upsert + // ---------------------------------------- + + const body: IDataObject = { + Company: this.getNodeParameter('Company', i), + Last_Name: this.getNodeParameter('lastName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustLeadPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/leads/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'product') { + + // ********************************************************************** + // product + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/products-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Products + + if (operation === 'create') { + + // ---------------------------------------- + // product: create + // ---------------------------------------- + + const body: IDataObject = { + Product_Name: this.getNodeParameter('productName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustProductPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/products', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // product: delete + // ---------------------------------------- + + const productId = this.getNodeParameter('productId', i); + + const endpoint = `/products/${productId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // product: get + // ---------------------------------------- + + const productId = this.getNodeParameter('productId', i); + + const endpoint = `/products/${productId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // product: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/products', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // product: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustProductPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const productId = this.getNodeParameter('productId', i); + + const endpoint = `/products/${productId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // product: upsert + // ---------------------------------------- + + const body: IDataObject = { + Product_Name: this.getNodeParameter('productName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustProductPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/products/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'purchaseOrder') { + + // ********************************************************************** + // purchaseOrder + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/purchase-orders-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Purchase_Order + + if (operation === 'create') { + + // ---------------------------------------- + // purchaseOrder: create + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + throwOnMissingProducts.call(this, resource, productDetails); + + const body: IDataObject = { + Subject: this.getNodeParameter('subject', i), + Vendor_Name: { id: this.getNodeParameter('vendorId', i) }, + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustPurchaseOrderPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/purchase_orders', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // purchaseOrder: delete + // ---------------------------------------- + + const purchaseOrderId = this.getNodeParameter('purchaseOrderId', i); + + const endpoint = `/purchase_orders/${purchaseOrderId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // purchaseOrder: get + // ---------------------------------------- + + const purchaseOrderId = this.getNodeParameter('purchaseOrderId', i); + + const endpoint = `/purchase_orders/${purchaseOrderId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // purchaseOrder: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/purchase_orders', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // purchaseOrder: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustPurchaseOrderPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const purchaseOrderId = this.getNodeParameter('purchaseOrderId', i); + + const endpoint = `/purchase_orders/${purchaseOrderId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // purchaseOrder: upsert + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + const body: IDataObject = { + Subject: this.getNodeParameter('subject', i), + Vendor_Name: { id: this.getNodeParameter('vendorId', i) }, + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustPurchaseOrderPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/purchase_orders/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'quote') { + + // ********************************************************************** + // quote + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/quotes-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Quotes + + if (operation === 'create') { + + // ---------------------------------------- + // quote: create + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + throwOnMissingProducts.call(this, resource, productDetails); + + const body: IDataObject = { + Subject: this.getNodeParameter('subject', i), + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustQuotePayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/quotes', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // quote: delete + // ---------------------------------------- + + const quoteId = this.getNodeParameter('quoteId', i); + + responseData = await zohoApiRequest.call(this, 'DELETE', `/quotes/${quoteId}`); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // quote: get + // ---------------------------------------- + + const quoteId = this.getNodeParameter('quoteId', i); + + responseData = await zohoApiRequest.call(this, 'GET', `/quotes/${quoteId}`); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // quote: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/quotes', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // quote: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustQuotePayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const quoteId = this.getNodeParameter('quoteId', i); + + responseData = await zohoApiRequest.call(this, 'PUT', `/quotes/${quoteId}`, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // quote: upsert + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + const body: IDataObject = { + Subject: this.getNodeParameter('subject', i), + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustQuotePayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/quotes/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'salesOrder') { + + // ********************************************************************** + // salesOrder + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/sales-orders-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Sales_Orders + + if (operation === 'create') { + + // ---------------------------------------- + // salesOrder: create + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + const body: IDataObject = { + Account_Name: { id: this.getNodeParameter('accountId', i) }, + Subject: this.getNodeParameter('subject', i), + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustSalesOrderPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/sales_orders', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // salesOrder: delete + // ---------------------------------------- + + const salesOrderId = this.getNodeParameter('salesOrderId', i); + + const endpoint = `/sales_orders/${salesOrderId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // salesOrder: get + // ---------------------------------------- + + const salesOrderId = this.getNodeParameter('salesOrderId', i); + + const endpoint = `/sales_orders/${salesOrderId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // salesOrder: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/sales_orders', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // salesOrder: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustSalesOrderPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const salesOrderId = this.getNodeParameter('salesOrderId', i); + + const endpoint = `/sales_orders/${salesOrderId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // salesOrder: upsert + // ---------------------------------------- + + const productDetails = this.getNodeParameter('Product_Details', i) as ProductDetails; + + const body: IDataObject = { + Account_Name: { id: this.getNodeParameter('accountId', i) }, + Subject: this.getNodeParameter('subject', i), + Product_Details: adjustProductDetails(productDetails), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustSalesOrderPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/sales_orders/upsert', body); + responseData = responseData.data[0].details; + + } + + } else if (resource === 'vendor') { + + // ********************************************************************** + // vendor + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/vendors-response.html + // https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-fields/articles/standard-modules-fields#Vendors + + if (operation === 'create') { + + // ---------------------------------------- + // vendor: create + // ---------------------------------------- + + const body: IDataObject = { + Vendor_Name: this.getNodeParameter('vendorName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustVendorPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/vendors', body); + responseData = responseData.data[0].details; + + } else if (operation === 'delete') { + + // ---------------------------------------- + // vendor: delete + // ---------------------------------------- + + const vendorId = this.getNodeParameter('vendorId', i); + + const endpoint = `/vendors/${vendorId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + responseData = responseData.data[0].details; + + } else if (operation === 'get') { + + // ---------------------------------------- + // vendor: get + // ---------------------------------------- + + const vendorId = this.getNodeParameter('vendorId', i); + + const endpoint = `/vendors/${vendorId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + responseData = responseData.data; + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // vendor: getAll + // ---------------------------------------- + + const qs: IDataObject = {}; + const options = this.getNodeParameter('options', i) as GetAllFilterOptions; + + addGetAllFilterOptions(qs, options); + + responseData = await handleListing.call(this, 'GET', '/vendors', {}, qs); + + } else if (operation === 'update') { + + // ---------------------------------------- + // vendor: update + // ---------------------------------------- + + const body: IDataObject = {}; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, adjustVendorPayload(updateFields)); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const vendorId = this.getNodeParameter('vendorId', i); + + const endpoint = `/vendors/${vendorId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + responseData = responseData.data[0].details; + + } else if (operation === 'upsert') { + + // ---------------------------------------- + // vendor: upsert + // ---------------------------------------- + + const body: IDataObject = { + Vendor_Name: this.getNodeParameter('vendorName', i), + }; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, adjustVendorPayload(additionalFields)); + } + + responseData = await zohoApiRequest.call(this, 'POST', '/vendors/upsert', body); + responseData = responseData.data[0].details; + } } - //https://www.zoho.com/crm/developer/docs/api/get-records.html - if (operation === 'getAll') { - const returnAll = this.getNodeParameter('returnAll', i) as boolean; - const options = this.getNodeParameter('options', i) as IDataObject; - if (options.fields) { - qs.fields = (options.fields as string[]).join(','); - } - if (options.approved) { - qs.approved = options.approved as boolean; - } - if (options.converted) { - qs.converted = options.converted as boolean; - } - if (options.includeChild) { - qs.include_child = options.includeChild as boolean; - } - if (options.sortOrder) { - qs.sort_order = options.sortOrder as string; - } - if (options.sortBy) { - qs.sort_by = options.sortBy as string; - } - if (options.territoryId) { - qs.territory_id = options.territoryId as string; - } - if (returnAll) { - responseData = await zohoApiRequestAllItems.call(this, 'data', 'GET', '/leads', {}, qs); - } else { - qs.per_page = this.getNodeParameter('limit', i) as number; - responseData = await zohoApiRequest.call(this, 'GET', '/leads', {}, qs); - responseData = responseData.data; - } - } - //https://www.zoho.com/crm/developer/docs/api/delete-specific-record.html - if (operation === 'delete') { - const leadId = this.getNodeParameter('leadId', i) as string; - responseData = await zohoApiRequest.call(this, 'DELETE', `/leads/${leadId}`); - responseData = responseData.data; - } - //https://www.zoho.com/crm/developer/docs/api/field-meta.html - if (operation === 'getFields') { - qs.module = 'leads'; - responseData = await zohoApiRequest.call(this, 'GET', '/settings/fields', {}, qs); - responseData = responseData.fields; + + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ error: error.message }); + continue; } + + throw error; } - if (Array.isArray(responseData)) { - returnData.push.apply(returnData, responseData as IDataObject[]); - } else if (responseData !== undefined) { - returnData.push(responseData as IDataObject); - } + + Array.isArray(responseData) + ? returnData.push(...responseData) + : returnData.push(responseData); } + return [this.helpers.returnJsonArray(returnData)]; } } diff --git a/packages/nodes-base/nodes/Zoho/descriptions/AccountDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/AccountDescription.ts new file mode 100644 index 0000000000..6351d49f4d --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/AccountDescription.ts @@ -0,0 +1,415 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + billingAddress, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, + shippingAddress, +} from './SharedFields'; + +export const accountOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'account', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an account', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an account', + }, + { + name: 'Get', + value: 'get', + description: 'Get an account', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all accounts', + }, + { + name: 'Update', + value: 'update', + description: 'Update an account', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const accountFields = [ + // ---------------------------------------- + // account: create + // ---------------------------------------- + { + displayName: 'Account Name', + name: 'accountName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // account: upsert + // ---------------------------------------- + { + displayName: 'Account Name', + name: 'accountName', + description: 'Name of the account. If a record with this account name exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // account: create + upsert + // ---------------------------------------- + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Account Number', + name: 'Account_Number', + type: 'string', + default: '', + }, + { + displayName: 'Account Site', + name: 'Account_Site', + type: 'string', + default: '', + description: 'Name of the account’s location, e.g. Headquarters or London.', + }, + { + displayName: 'Account Type', + name: 'Account_Type', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAccountType', + }, + default: [], + }, + { + displayName: 'Annual Revenue', + name: 'Annual_Revenue', + type: 'number', + default: '', + }, + billingAddress, + { + displayName: 'Contact Details', + name: 'Contact_Details', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('account'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Employees', + name: 'Employees', + type: 'number', + default: '', + description: 'Number of employees in the account’s company.', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: '', + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'Industry', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + shippingAddress, + { + displayName: 'Ticker Symbol', + name: 'Ticker_Symbol', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // account: delete + // ---------------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + description: 'ID of the account to delete. Can be found at the end of the URL.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // account: get + // ---------------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + description: 'ID of the account to retrieve. Can be found at the end of the URL.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // account: getAll + // ---------------------------------------- + ...makeGetAllFields('account'), + + // ---------------------------------------- + // account: update + // ---------------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + description: 'ID of the account to update. Can be found at the end of the URL.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'account', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Account Name', + name: 'Account_Name', + type: 'string', + default: '', + }, + { + displayName: 'Account Number', + name: 'Account_Number', + type: 'string', + default: '', + }, + { + displayName: 'Account Site', + name: 'Account_Site', + type: 'string', + default: '', + description: 'Name of the account’s location, e.g. Headquarters or London.', + }, + { + displayName: 'Account Type', + name: 'Account_Type', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getAccountType', + }, + default: [], + }, + { + displayName: 'Annual Revenue', + name: 'Annual_Revenue', + type: 'number', + default: '', + }, + billingAddress, + { + displayName: 'Contact Details', + name: 'Contact_Details', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('account'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Employees', + name: 'Employees', + type: 'number', + default: '', + description: 'Number of employees in the account’s company.', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: '', + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'Industry', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + shippingAddress, + { + displayName: 'Ticker Symbol', + name: 'Ticker_Symbol', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/ContactDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/ContactDescription.ts new file mode 100644 index 0000000000..fc58c8f6f4 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/ContactDescription.ts @@ -0,0 +1,590 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + currencies, + mailingAddress, + makeCustomFieldsFixedCollection, + makeGetAllFields, + otherAddress, +} from './SharedFields'; + +export const contactOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'contact', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a contact', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact', + }, + { + name: 'Get', + value: 'get', + description: 'Get a contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all contacts', + }, + { + name: 'Update', + value: 'update', + description: 'Update a contact', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const contactFields = [ + // ---------------------------------------- + // contact: create + // ---------------------------------------- + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assistant', + name: 'Assistant', + type: 'string', + default: '', + description: 'Name of the contact’s assistant.', + }, + makeCustomFieldsFixedCollection('contact'), + { + displayName: 'Date of Birth', + name: 'Date_of_Birth', + type: 'dateTime', + default: '', + }, + { + displayName: 'Department', + name: 'Department', + type: 'string', + default: '', + description: 'Company department to which the contact belongs.', + }, + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Email (Primary)', + name: 'Email', + type: 'string', + default: '', + }, + { + displayName: 'Email (Secondary)', + name: 'Secondary_Email', + type: 'string', + default: '', + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'First Name', + name: 'First_Name', + type: 'string', + default: '', + }, + { + displayName: 'Full Name', + name: 'Full_Name', + type: 'string', + default: '', + }, + mailingAddress, + { + displayName: 'Mobile', + name: 'Mobile', + type: 'string', + default: '', + }, + otherAddress, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Phone (Assistant)', + name: 'Asst_Phone', + type: 'string', + default: '', + description: 'Phone number of the contact’s assistant.', + }, + { + displayName: 'Phone (Home)', + name: 'Home_Phone', + type: 'string', + default: '', + }, + { + displayName: 'Phone (Other)', + name: 'Other_Phone', + type: 'string', + default: '', + }, + { + displayName: 'Salutation', + name: 'Salutation', + type: 'string', + default: '', + }, + { + displayName: 'Skype ID', + name: 'Skype_ID', + type: 'string', + default: '', + }, + { + displayName: 'Title', + name: 'Title', + type: 'string', + default: '', + description: 'Position of the contact at their company.', + }, + { + displayName: 'Twitter', + name: 'Twitter', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // contact: upsert + // ---------------------------------------- + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Assistant', + name: 'Assistant', + type: 'string', + default: '', + description: 'Name of the contact’s assistant.', + }, + makeCustomFieldsFixedCollection('contact'), + { + displayName: 'Date of Birth', + name: 'Date_of_Birth', + type: 'dateTime', + default: '', + }, + { + displayName: 'Department', + name: 'Department', + type: 'string', + default: '', + description: 'Company department to which the contact belongs.', + }, + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Email (Primary)', + name: 'Email', + type: 'string', + default: '', + description: 'Email of the contact. If a record with this email exists it will be updated, otherwise a new one will be created.', + }, + { + displayName: 'Email (Secondary)', + name: 'Secondary_Email', + type: 'string', + default: '', + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'First Name', + name: 'First_Name', + type: 'string', + default: '', + }, + { + displayName: 'Full Name', + name: 'Full_Name', + type: 'string', + default: '', + }, + mailingAddress, + { + displayName: 'Mobile', + name: 'Mobile', + type: 'string', + default: '', + }, + otherAddress, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Phone (Assistant)', + name: 'Asst_Phone', + type: 'string', + default: '', + description: 'Phone number of the contact’s assistant.', + }, + { + displayName: 'Phone (Home)', + name: 'Home_Phone', + type: 'string', + default: '', + }, + { + displayName: 'Phone (Other)', + name: 'Other_Phone', + type: 'string', + default: '', + }, + { + displayName: 'Salutation', + name: 'Salutation', + type: 'string', + default: '', + }, + { + displayName: 'Skype ID', + name: 'Skype_ID', + type: 'string', + default: '', + }, + { + displayName: 'Title', + name: 'Title', + type: 'string', + default: '', + description: 'Position of the contact at their company.', + }, + { + displayName: 'Twitter', + name: 'Twitter', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // contact: delete + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // contact: get + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // contact: getAll + // ---------------------------------------- + ...makeGetAllFields('contact'), + + // ---------------------------------------- + // contact: update + // ---------------------------------------- + { + displayName: 'Contact ID', + name: 'contactId', + description: 'ID of the contact to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'contact', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assistant', + name: 'Assistant', + type: 'string', + default: '', + }, + { + displayName: 'Assistant’s Phone', + name: 'Asst_Phone', + type: 'string', + default: '', + description: 'Phone number of the contact’s assistant.', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('contact'), + { + displayName: 'Date of Birth', + name: 'Date_of_Birth', + type: 'dateTime', + default: '', + }, + { + displayName: 'Department', + name: 'Department', + type: 'string', + default: '', + }, + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Email (Primary)', + name: 'Email', + type: 'string', + default: '', + }, + { + displayName: 'Email (Secondary)', + name: 'Secondary_Email', + type: 'string', + default: '', + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'First Name', + name: 'First_Name', + type: 'string', + default: '', + }, + { + displayName: 'Full Name', + name: 'Full_Name', + type: 'string', + default: '', + }, + { + displayName: 'Home Phone', + name: 'Home_Phone', + type: 'string', + default: '', + }, + { + displayName: 'Last Name', + name: 'Last_Name', + type: 'string', + default: '', + }, + mailingAddress, + { + displayName: 'Mobile', + name: 'Mobile', + type: 'string', + default: '', + }, + otherAddress, + { + displayName: 'Other Phone', + name: 'Other_Phone', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Salutation', + name: 'Salutation', + type: 'string', + default: '', + }, + { + displayName: 'Skype ID', + name: 'Skype_ID', + type: 'string', + default: '', + }, + { + displayName: 'Title', + name: 'Title', + type: 'string', + default: '', + description: 'Position of the contact at their company.', + }, + { + displayName: 'Twitter', + name: 'Twitter', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/DealDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/DealDescription.ts new file mode 100644 index 0000000000..312c9b855b --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/DealDescription.ts @@ -0,0 +1,385 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, +} from './SharedFields'; + +export const dealOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'deal', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a deal', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a contact', + }, + { + name: 'Get', + value: 'get', + description: 'Get a contact', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all contacts', + }, + { + name: 'Update', + value: 'update', + description: 'Update a contact', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const dealFields = [ + // ---------------------------------------- + // deal: create + // ---------------------------------------- + { + displayName: 'Deal Name', + name: 'dealName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // deal: upsert + // ---------------------------------------- + { + displayName: 'Deal Name', + name: 'dealName', + description: 'Name of the deal. If a record with this deal name exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + // ---------------------------------------- + // deal: create + upsert + // ---------------------------------------- + { + displayName: 'Stage', + name: 'stage', + type: 'options', + required: true, + default: [], + typeOptions: { + loadOptionsMethod: 'getDealStage', + }, + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Amount', + name: 'Amount', + type: 'number', + default: '', + description: 'Monetary amount of the deal.', + }, + { + displayName: 'Closing Date', + name: 'Closing_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('deal'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Lead Conversion Time', + name: 'Lead_Conversion_Time', + type: 'number', + default: '', + description: 'Averge number of days to convert the lead into a deal.', + }, + { + displayName: 'Next Step', + name: 'Next_Step', + type: 'string', + default: '', + description: 'Description of the next step in the sales process.', + }, + { + displayName: 'Overall Sales Duration', + name: 'Overall_Sales_Duration', + type: 'number', + default: '', + description: 'Averge number of days to convert the lead into a deal and to win the deal.', + }, + { + displayName: 'Probability', + name: 'Probability', + type: 'number', + typeOptions: { + minValue: 0, + maxValue: 100, + }, + default: '', + description: 'Probability of deal closure as a percentage. For example, enter 12 for 12%.', + }, + { + displayName: 'Sales Cycle Duration', + name: 'Sales_Cycle_Duration', + type: 'number', + default: 0, + description: 'Averge number of days for the deal to be won.', + }, + ], + }, + + // ---------------------------------------- + // deal: delete + // ---------------------------------------- + { + displayName: 'Deal ID', + name: 'dealId', + description: 'ID of the deal to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // deal: get + // ---------------------------------------- + { + displayName: 'Deal ID', + name: 'dealId', + description: 'ID of the deal to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // deal: getAll + // ---------------------------------------- + ...makeGetAllFields('deal'), + + // ---------------------------------------- + // deal: update + // ---------------------------------------- + { + displayName: 'Deal ID', + name: 'dealId', + description: 'ID of the deal to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'deal', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Amount', + name: 'Amount', + type: 'number', + default: '', + description: 'Monetary amount of the deal.', + }, + { + displayName: 'Closing Date', + name: 'Closing_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'string', + default: '', + description: 'Symbol of the currency in which revenue is generated.', + }, + makeCustomFieldsFixedCollection('deal'), + { + displayName: 'Deal Name', + name: 'Deal_Name', + type: 'string', + default: '', + }, + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Lead Conversion Time', + name: 'Lead_Conversion_Time', + type: 'number', + default: '', + description: 'Averge number of days to convert the lead into a deal.', + }, + { + displayName: 'Next Step', + name: 'Next_Step', + type: 'string', + default: '', + description: 'Description of the next step in the sales process.', + }, + { + displayName: 'Overall Sales Duration', + name: 'Overall_Sales_Duration', + type: 'number', + default: '', + description: 'Averge number of days to convert the lead into a deal and to win the deal.', + }, + { + displayName: 'Probability', + name: 'Probability', + type: 'number', + typeOptions: { + minValue: 0, + maxValue: 100, + }, + default: '', + description: 'Probability of deal closure as a percentage. For example, enter 12 for 12%.', + }, + { + displayName: 'Sales Cycle Duration', + name: 'Sales_Cycle_Duration', + type: 'number', + default: 0, + description: 'Averge number of days to win the deal.', + }, + { + displayName: 'Stage', + name: 'Stage', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDealStage', + }, + default: [], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/InvoiceDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/InvoiceDescription.ts new file mode 100644 index 0000000000..4ede26523f --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/InvoiceDescription.ts @@ -0,0 +1,463 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + billingAddress, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, + productDetailsOptions, + shippingAddress, +} from './SharedFields'; + +export const invoiceOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an invoice', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an invoice', + }, + { + name: 'Get', + value: 'get', + description: 'Get an invoice', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all invoices', + }, + { + name: 'Update', + value: 'update', + description: 'Update an invoice', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const invoiceFields = [ + // ---------------------------------------- + // invoice: create + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the invoice.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // invoice: upsert + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the invoice. If a record with this subject exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // invoice: create + upsert + // ---------------------------------------- + { + displayName: 'Products', + name: 'Product_Details', + type: 'collection', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Product', + }, + default: {}, + placeholder: 'Add Field', + options: productDetailsOptions, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Account ID', + name: 'accountId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getAccounts', + }, + description: 'ID of the account associated with this invoice.', + }, + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: '', + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('invoice'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Due Date', + name: 'Due_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: '', + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: '', + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'Invoice Date', + name: 'Invoice_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Invoice Number', + name: 'Invoice_Number', + type: 'string', + default: '', + }, + { + displayName: 'Sales Commission', + name: 'Sales_Commission', + type: 'number', + default: '', + description: 'Commission of sales person on deal closure as a percentage. For example, enter 12 for 12%.', + }, + shippingAddress, + { + displayName: 'Status', + name: 'Status', + type: 'string', + default: '', + }, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: '', + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: '', + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the invoice.', + }, + ], + }, + + // ---------------------------------------- + // invoice: delete + // ---------------------------------------- + { + displayName: 'Invoice ID', + name: 'invoiceId', + description: 'ID of the invoice to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // invoice: get + // ---------------------------------------- + { + displayName: 'Invoice ID', + name: 'invoiceId', + description: 'ID of the invoice to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // invoice: getAll + // ---------------------------------------- + ...makeGetAllFields('invoice'), + + // ---------------------------------------- + // invoice: update + // ---------------------------------------- + { + displayName: 'Invoice ID', + name: 'invoiceId', + description: 'ID of the invoice to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'invoice', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Account ID', + name: 'accountId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getAccounts', + }, + description: 'ID of the account associated with this invoice.', + }, + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: '', + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('invoice'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Due Date', + name: 'Due_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: '', + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: '', + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'Invoice Date', + name: 'Invoice_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Invoice Number', + name: 'Invoice_Number', + type: 'string', + default: '', + }, + { + displayName: 'Products', + name: 'Product_Details', + type: 'collection', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Product', + }, + default: {}, + placeholder: 'Add Field', + options: productDetailsOptions, + }, + { + displayName: 'Sales Commission', + name: 'Sales_Commission', + type: 'number', + default: '', + description: 'Commission of sales person on deal closure as a percentage. For example, enter 12 for 12%.', + }, + shippingAddress, + { + displayName: 'Status', + name: 'Status', + type: 'string', + default: '', + }, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: '', + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Subject', + name: 'Subject', + type: 'string', + default: '', + description: 'Subject or title of the invoice.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: '', + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the invoice.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/LeadDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/LeadDescription.ts new file mode 100644 index 0000000000..1f26dd04e7 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/LeadDescription.ts @@ -0,0 +1,694 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + address, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, +} from './SharedFields'; + +export const leadOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'lead', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a lead', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a lead', + }, + { + name: 'Get', + value: 'get', + description: 'Get a lead', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all leads', + }, + { + name: 'Get Fields', + value: 'getFields', + description: 'Get lead fields', + }, + { + name: 'Update', + value: 'update', + description: 'Update a lead', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const leadFields = [ + // ---------------------------------------- + // lead: create + // ---------------------------------------- + { + displayName: 'Company', + name: 'Company', + description: 'Company at which the lead works.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + address, + { + displayName: 'Annual Revenue', + name: 'Annual_Revenue', + type: 'number', + default: '', + description: 'Annual revenue of the lead’s company.', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('lead'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Designation', + name: 'Designation', + type: 'string', + default: '', + description: 'Position of the lead at their company.', + }, + { + displayName: 'Email', + name: 'Email', + type: 'string', + default: '', + }, + { + displayName: 'Email Opt Out', + name: 'Email_Opt_Out', + type: 'boolean', + default: false, + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'First Name', + name: 'First_Name', + type: 'string', + default: '', + }, + { + displayName: 'Full Name', + name: 'Full_Name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'Industry', + type: 'string', + default: '', + description: 'Industry to which the lead belongs.', + }, + { + displayName: 'Industry Type', + name: 'Industry_Type', + type: 'string', + default: '', + description: 'Type of industry to which the lead belongs.', + }, + { + displayName: 'Lead Source', + name: 'Lead_Source', + type: 'string', + default: '', + description: 'Source from which the lead was created.', + }, + { + displayName: 'Lead Status', + name: 'Lead_Status', + type: 'string', + default: '', + }, + { + displayName: 'Mobile', + name: 'Mobile', + type: 'string', + default: '', + }, + { + displayName: 'Number of Employees', + name: 'No_of_Employees', + type: 'number', + default: '', + description: 'Number of employees in the lead’s company.', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Salutation', + name: 'Salutation', + type: 'string', + default: '', + }, + { + displayName: 'Secondary Email', + name: 'Secondary_Email', + type: 'string', + default: '', + }, + { + displayName: 'Skype ID', + name: 'Skype_ID', + type: 'string', + default: '', + }, + { + displayName: 'Twitter', + name: 'Twitter', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // lead: upsert + // ---------------------------------------- + { + displayName: 'Company', + name: 'Company', + description: 'Company at which the lead works.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + { + displayName: 'Last Name', + name: 'lastName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'upsert', + ], + }, + }, + options: [ + address, + { + displayName: 'Annual Revenue', + name: 'Annual_Revenue', + type: 'number', + default: '', + description: 'Annual revenue of the lead’s company.', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('lead'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Designation', + name: 'Designation', + type: 'string', + default: '', + description: 'Position of the lead at their company.', + }, + { + displayName: 'Email', + name: 'Email', + type: 'string', + default: '', + description: 'Email of the lead. If a record with this email exists it will be updated, otherwise a new one will be created.', + }, + { + displayName: 'Email Opt Out', + name: 'Email_Opt_Out', + type: 'boolean', + default: false, + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'First Name', + name: 'First_Name', + type: 'string', + default: '', + }, + { + displayName: 'Full Name', + name: 'Full_Name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'Industry', + type: 'string', + default: '', + description: 'Industry to which the lead belongs.', + }, + { + displayName: 'Industry Type', + name: 'Industry_Type', + type: 'string', + default: '', + description: 'Type of industry to which the lead belongs.', + }, + { + displayName: 'Lead Source', + name: 'Lead_Source', + type: 'string', + default: '', + description: 'Source from which the lead was created.', + }, + { + displayName: 'Lead Status', + name: 'Lead_Status', + type: 'string', + default: '', + }, + { + displayName: 'Mobile', + name: 'Mobile', + type: 'string', + default: '', + }, + { + displayName: 'Number of Employees', + name: 'No_of_Employees', + type: 'number', + default: '', + description: 'Number of employees in the lead’s company.', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Salutation', + name: 'Salutation', + type: 'string', + default: '', + }, + { + displayName: 'Secondary Email', + name: 'Secondary_Email', + type: 'string', + default: '', + }, + { + displayName: 'Skype ID', + name: 'Skype_ID', + type: 'string', + default: '', + }, + { + displayName: 'Twitter', + name: 'Twitter', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // lead: delete + // ---------------------------------------- + { + displayName: 'Lead ID', + name: 'leadId', + description: 'ID of the lead to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // lead: get + // ---------------------------------------- + { + displayName: 'Lead ID', + name: 'leadId', + description: 'ID of the lead to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // lead: getAll + // ---------------------------------------- + ...makeGetAllFields('lead'), + + // ---------------------------------------- + // lead: update + // ---------------------------------------- + { + displayName: 'Lead ID', + name: 'leadId', + description: 'ID of the lead to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'lead', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + address, + { + displayName: 'Annual Revenue', + name: 'Annual_Revenue', + type: 'number', + default: '', + description: 'Annual revenue of the lead’s company.', + }, + { + displayName: 'Company', + name: 'Company', + type: 'string', + default: '', + description: 'Company at which the lead works.', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('lead'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Designation', + name: 'Designation', + type: 'string', + default: '', + description: 'Position of the lead at their company.', + }, + { + displayName: 'Email', + name: 'Email', + type: 'string', + default: '', + }, + { + displayName: 'Email Opt Out', + name: 'Email_Opt_Out', + type: 'boolean', + default: false, + }, + { + displayName: 'Fax', + name: 'Fax', + type: 'string', + default: '', + }, + { + displayName: 'First Name', + name: 'First_Name', + type: 'string', + default: '', + }, + { + displayName: 'Full Name', + name: 'Full_Name', + type: 'string', + default: '', + }, + { + displayName: 'Industry', + name: 'Industry', + type: 'string', + default: '', + description: 'Industry to which the lead belongs.', + }, + { + displayName: 'Industry Type', + name: 'Industry_Type', + type: 'string', + default: '', + description: 'Type of industry to which the lead belongs.', + }, + { + displayName: 'Last Name', + name: 'Last_Name', + type: 'string', + default: '', + }, + { + displayName: 'Lead Source', + name: 'Lead_Source', + type: 'string', + default: '', + description: 'Source from which the lead was created.', + }, + { + displayName: 'Lead Status', + name: 'Lead_Status', + type: 'string', + default: '', + }, + { + displayName: 'Mobile', + name: 'Mobile', + type: 'string', + default: '', + }, + { + displayName: 'Number of Employees', + name: 'No_of_Employees', + type: 'number', + default: '', + description: 'Number of employees in the lead’s company.', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Salutation', + name: 'Salutation', + type: 'string', + default: '', + }, + { + displayName: 'Secondary Email', + name: 'Secondary_Email', + type: 'string', + default: '', + }, + { + displayName: 'Skype ID', + name: 'Skype_ID', + type: 'string', + default: '', + }, + { + displayName: 'Twitter', + name: 'Twitter', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/ProductDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/ProductDescription.ts new file mode 100644 index 0000000000..19b6f880c6 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/ProductDescription.ts @@ -0,0 +1,352 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + makeCustomFieldsFixedCollection, + makeGetAllFields, +} from './SharedFields'; + +export const productOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'product', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a product', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a product', + }, + { + name: 'Get', + value: 'get', + description: 'Get a product', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all products', + }, + { + name: 'Update', + value: 'update', + description: 'Update a product', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const productFields = [ + // ---------------------------------------- + // product: create + // ---------------------------------------- + { + displayName: 'Product Name', + name: 'productName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // product: upsert + // ---------------------------------------- + { + displayName: 'Product Name', + name: 'productName', + description: 'Name of the product. If a record with this product name exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // product: create + upsert + // ---------------------------------------- + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Commission Rate', + name: 'Commission_Rate', + type: 'number', + description: 'Commission rate for the product. For example, enter 12 for 12%.', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + makeCustomFieldsFixedCollection('product'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'Manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Product Active', + name: 'Product_Active', + type: 'boolean', + default: false, + }, + { + displayName: 'Product Category', + name: 'Product_Category', + type: 'string', + default: '', + }, + { + displayName: 'Quantity in Demand', + name: 'Qty_in_Demand', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Quantity in Stock', + name: 'Qty_in_Stock', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Taxable', + name: 'Taxable', + type: 'boolean', + default: false, + }, + { + displayName: 'Unit Price', + name: 'Unit_Price', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + ], + }, + + // ---------------------------------------- + // product: delete + // ---------------------------------------- + { + displayName: 'Product ID', + name: 'productId', + description: 'ID of the product to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // product: get + // ---------------------------------------- + { + displayName: 'Product ID', + name: 'productId', + description: 'ID of the product to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // product: getAll + // ---------------------------------------- + ...makeGetAllFields('product'), + + // ---------------------------------------- + // product: update + // ---------------------------------------- + { + displayName: 'Product ID', + name: 'productId', + description: 'ID of the product to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'product', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Commission Rate', + name: 'Commission_Rate', + type: 'number', + description: 'Commission rate for the product. For example, enter 12 for 12%.', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + makeCustomFieldsFixedCollection('product'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Manufacturer', + name: 'Manufacturer', + type: 'string', + default: '', + }, + { + displayName: 'Product Active', + name: 'Product_Active', + type: 'boolean', + default: false, + }, + { + displayName: 'Product Category', + name: 'Product_Category', + type: 'string', + default: '', + }, + { + displayName: 'Quantity in Demand', + name: 'Qty_in_Demand', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Quantity in Stock', + name: 'Qty_in_Stock', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + { + displayName: 'Taxable', + name: 'Taxable', + type: 'boolean', + default: false, + }, + { + displayName: 'Unit Price', + name: 'Unit_Price', + type: 'number', + typeOptions: { + minValue: 0, + }, + default: 0, + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/PurchaseOrderDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/PurchaseOrderDescription.ts new file mode 100644 index 0000000000..2ef0b573f8 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/PurchaseOrderDescription.ts @@ -0,0 +1,590 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + billingAddress, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, + productDetailsOptions, + shippingAddress, +} from './SharedFields'; + +export const purchaseOrderOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a purchase order', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a purchase order', + }, + { + name: 'Get', + value: 'get', + description: 'Get a purchase order', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all purchase orders', + }, + { + name: 'Update', + value: 'update', + description: 'Update a purchase order', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const purchaseOrderFields = [ + // ---------------------------------------- + // purchaseOrder: create + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the purchase order.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // purchaseOrder: upsert + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the purchase order. If a record with this subject exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // purchaseOrder: create + upsert + // ---------------------------------------- + { + displayName: 'Vendor ID', + name: 'vendorId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getVendors', + }, + description: 'ID of the vendor associated with the purchase order.', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + { + displayName: 'Products', + name: 'Product_Details', + type: 'collection', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Product', + }, + default: {}, + placeholder: 'Add Field', + options: productDetailsOptions, + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Adjustment in the grand total, if any.', + }, + { + displayName: 'Billing Address', + name: 'Billing_Address', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Billing Address Field', + options: [ + { + displayName: 'Billing Address Fields', + name: 'billing_address_fields', + values: [ + { + displayName: 'Billing City', + name: 'Billing_City', + type: 'string', + default: '', + }, + { + displayName: 'Billing Code', + name: 'Billing_Code', + type: 'string', + default: '', + }, + { + displayName: 'Billing Country', + name: 'Billing_Country', + type: 'string', + default: '', + }, + { + displayName: 'Billing State', + name: 'Billing_State', + type: 'string', + default: '', + }, + { + displayName: 'Billing Street', + name: 'Billing_Street', + type: 'string', + default: '', + }, + ], + }, + ], + }, + { + displayName: 'Carrier', + name: 'Carrier', + type: 'string', + default: '', + description: 'Name of the carrier.', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('purchaseOrder'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Discount', + name: 'Discount', + type: 'number', + description: 'Discount applied to the purchase order. For example, enter 12 for 12%.', + default: 0, + typeOptions: { + minValue: 0, + }, + }, + { + displayName: 'Due Date', + name: 'Due_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'PO Date', + name: 'PO_Date', + type: 'dateTime', + default: '', + description: 'Date on which the purchase order was issued.', + }, + { + displayName: 'PO Number', + name: 'PO_Number', + type: 'string', + default: '', + description: 'ID of the purchase order after creating a case.', + }, + { + displayName: 'Sales Commission', + name: 'Sales_Commission', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Commission of sales person on deal closure as a percentage. For example, enter 12 for 12%.', + }, + shippingAddress, + { + displayName: 'Status', + name: 'Status', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getPurchaseOrderStatus', + }, + description: 'Status of the purchase order.', + }, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the purchase order.', + }, + { + displayName: 'Tracking Number', + name: 'Tracking_Number', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // purchaseOrder: delete + // ---------------------------------------- + { + displayName: 'Purchase Order ID', + name: 'purchaseOrderId', + description: 'ID of the purchase order to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // purchaseOrder: get + // ---------------------------------------- + { + displayName: 'Purchase Order ID', + name: 'purchaseOrderId', + description: 'ID of the purchase order to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // purchaseOrder: getAll + // ---------------------------------------- + ...makeGetAllFields('purchaseOrder'), + + // ---------------------------------------- + // purchaseOrder: update + // ---------------------------------------- + { + displayName: 'Purchase Order ID', + name: 'purchaseOrderId', + description: 'ID of the purchase order to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'purchaseOrder', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Carrier', + name: 'Carrier', + type: 'string', + default: '', + description: 'Name of the carrier.', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('purchaseOrder'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Discount', + name: 'Discount', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + }, + { + displayName: 'Due Date', + name: 'Due_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'PO Date', + name: 'PO_Date', + type: 'dateTime', + default: '', + description: 'Date on which the purchase order was issued.', + }, + { + displayName: 'PO Number', + name: 'PO_Number', + type: 'string', + default: '', + description: 'ID of the purchase order after creating a case.', + }, + // productDetails('purchaseOrder', 'update'), + { + displayName: 'Sales Commission', + name: 'Sales_Commission', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Commission of sales person on deal closure as a percentage. For example, enter 12 for 12%.', + }, + shippingAddress, + { + displayName: 'Status', + name: 'Status', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getPurchaseOrderStatus', + }, + description: 'Status of the purchase order.', + }, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Subject', + name: 'Subject', + type: 'string', + default: '', + description: 'Subject or title of the purchase order.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the purchase order.', + }, + { + displayName: 'Tracking Number', + name: 'Tracking_Number', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/QuoteDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/QuoteDescription.ts new file mode 100644 index 0000000000..0ecf64adaf --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/QuoteDescription.ts @@ -0,0 +1,459 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + billingAddress, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, + productDetailsOptions, + shippingAddress, +} from './SharedFields'; + +export const quoteOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'quote', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a quote', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a quote', + }, + { + name: 'Get', + value: 'get', + description: 'Get a quote', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all quotes', + }, + { + name: 'Update', + value: 'update', + description: 'Update a quote', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const quoteFields = [ + // ---------------------------------------- + // quote: create + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the quote.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // quote: upsert + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the quote. If a record with this subject exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // quote: create + upsert + // ---------------------------------------- + { + displayName: 'Products', + name: 'Product_Details', + type: 'collection', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Product', + }, + default: {}, + placeholder: 'Add Field', + options: productDetailsOptions, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Carrier', + name: 'Carrier', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('quote'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'Quote Stage', + name: 'Quote_Stage', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getQuoteStage', + }, + description: 'Stage of the quote.', + }, + shippingAddress, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Team', + name: 'Team', + type: 'string', + default: '', + description: 'Team for whom the quote is created.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the quote.', + }, + { + displayName: 'Valid Till', + name: 'Valid_Till', + type: 'dateTime', + default: '', + description: 'Date until when the quote is valid.', + }, + ], + }, + + // ---------------------------------------- + // quote: delete + // ---------------------------------------- + { + displayName: 'Quote ID', + name: 'quoteId', + description: 'ID of the quote to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // quote: get + // ---------------------------------------- + { + displayName: 'Quote ID', + name: 'quoteId', + description: 'ID of the quote to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // quote: getAll + // ---------------------------------------- + ...makeGetAllFields('quote'), + + // ---------------------------------------- + // quote: update + // ---------------------------------------- + { + displayName: 'Quote ID', + name: 'quoteId', + description: 'ID of the quote to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'quote', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Carrier', + name: 'Carrier', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('quote'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'Quote Stage', + name: 'Quote_Stage', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getQuoteStage', + }, + description: 'Stage of the quote.', + }, + shippingAddress, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Subject', + name: 'Subject', + type: 'string', + default: '', + description: 'Subject or title of the quote.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Team', + name: 'Team', + type: 'string', + default: '', + description: 'Team for whom the quote is created.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the quote.', + }, + { + displayName: 'Valid Till', + name: 'Valid_Till', + type: 'dateTime', + default: '', + description: 'Date until when the quote is valid.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/SalesOrderDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/SalesOrderDescription.ts new file mode 100644 index 0000000000..35547664cf --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/SalesOrderDescription.ts @@ -0,0 +1,569 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + billingAddress, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, + productDetailsOptions, + shippingAddress, +} from './SharedFields'; + +export const salesOrderOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a sales order', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a sales order', + }, + { + name: 'Get', + value: 'get', + description: 'Get a sales order', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all sales orders', + }, + { + name: 'Update', + value: 'update', + description: 'Update a sales order', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const salesOrderFields = [ + // ---------------------------------------- + // salesOrder: create + upsert + // ---------------------------------------- + { + displayName: 'Account ID', + name: 'accountId', + required: true, + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getAccounts', + }, + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // salesOrder: create + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the sales order.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // salesOrder: upsert + // ---------------------------------------- + { + displayName: 'Subject', + name: 'subject', + description: 'Subject or title of the sales order. If a record with this subject exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // salesOrder: create + upsert + // ---------------------------------------- + { + displayName: 'Products', + name: 'Product_Details', + type: 'collection', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Product', + }, + default: {}, + placeholder: 'Add Field', + options: productDetailsOptions, + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Carrier', + name: 'Carrier', + type: 'string', + default: '', + description: 'Name of the carrier.', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getContacts', + }, + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('salesOrder'), + { + displayName: 'Deal ID', + name: 'dealId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getDeals', + }, + }, + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Discount', + name: 'Discount', + type: 'number', + description: 'Discount applied to the sales order. For example, enter 12 for 12%.', + default: 0, + typeOptions: { + minValue: 0, + }, + }, + { + displayName: 'Due Date', + name: 'Due_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'Sales Order Number', + name: 'SO_Number', + type: 'string', + default: '', + description: 'ID of the sales order after creating a case.', + }, + { + displayName: 'Sales Commission', + name: 'Sales_Commission', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Commission of sales person on deal closure as a percentage. For example, enter 12 for 12%.', + }, + shippingAddress, + { + displayName: 'Status', + name: 'Status', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getSalesOrderStatus', + }, + description: 'Status of the sales order.', + }, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the purchase order.', + }, + ], + }, + + // ---------------------------------------- + // salesOrder: delete + // ---------------------------------------- + { + displayName: 'Sales Order ID', + name: 'salesOrderId', + description: 'ID of the sales order to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // salesOrder: get + // ---------------------------------------- + { + displayName: 'Sales Order ID', + name: 'salesOrderId', + description: 'ID of the sales order to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // salesOrder: getAll + // ---------------------------------------- + ...makeGetAllFields('salesOrder'), + + // ---------------------------------------- + // salesOrder: update + // ---------------------------------------- + { + displayName: 'Sales Order ID', + name: 'salesOrderId', + description: 'ID of the sales order to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'salesOrder', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Account ID', + name: 'accountId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getAccounts', + }, + description: 'ID of the account associated with this invoice.', + }, + { + displayName: 'Adjustment', + name: 'Adjustment', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Adjustment in the grand total, if any.', + }, + billingAddress, + { + displayName: 'Carrier', + name: 'Carrier', + type: 'string', + default: '', + description: 'Name of the carrier.', + }, + { + displayName: 'Contact ID', + name: 'contactId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getContacts', + }, + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + description: 'Symbol of the currency in which revenue is generated.', + options: currencies, + }, + makeCustomFieldsFixedCollection('salesOrder'), + { + displayName: 'Deal ID', + name: 'dealId', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getDeals', + }, + }, + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Discount', + name: 'Discount', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + }, + { + displayName: 'Due Date', + name: 'Due_Date', + type: 'dateTime', + default: '', + }, + { + displayName: 'Exchange Rate', + name: 'Exchange_Rate', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Exchange rate of the default currency to the home currency.', + }, + { + displayName: 'Grand Total', + name: 'Grand_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product after deducting tax and discounts.', + }, + { + displayName: 'Sales Order Number', + name: 'SO_Number', + type: 'string', + default: '', + description: 'ID of the sales order after creating a case.', + }, + { + displayName: 'Sales Commission', + name: 'Sales_Commission', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Commission of sales person on deal closure as a percentage. For example, enter 12 for 12%.', + }, + shippingAddress, + { + displayName: 'Status', + name: 'Status', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getSalesOrderStatus', + }, + description: 'Status of the sales order.', + }, + { + displayName: 'Sub Total', + name: 'Sub_Total', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Total amount for the product excluding tax.', + }, + { + displayName: 'Subject', + name: 'Subject', + type: 'string', + default: '', + description: 'Subject or title of the sales order.', + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + typeOptions: { + minValue: 0, + }, + description: 'Tax amount as the sum of sales tax and value-added tax.', + }, + { + displayName: 'Terms and Conditions', + name: 'Terms_and_Conditions', + type: 'string', + default: '', + description: 'Terms and conditions associated with the purchase order.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/SharedFields.ts b/packages/nodes-base/nodes/Zoho/descriptions/SharedFields.ts new file mode 100644 index 0000000000..e7030ba28f --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/SharedFields.ts @@ -0,0 +1,577 @@ +import { capitalizeInitial } from '../GenericFunctions'; +import { CamelCaseResource } from '../types'; + +export const billingAddress = { + displayName: 'Billing Address', + name: 'Billing_Address', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Billing Address Field', + options: [ + { + displayName: 'Billing Address Fields', + name: 'address_fields', + values: [ + { + displayName: 'Street', + name: 'Billing_Street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'Billing_City', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'Billing_State', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'Billing_Country', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'Billing_Code', + type: 'string', + default: '', + }, + ], + }, + ], +}; + +export const shippingAddress = { + displayName: 'Shipping Address', + name: 'Shipping_Address', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Shipping Address Field', + options: [ + { + displayName: 'Shipping Address Fields', + name: 'address_fields', + values: [ + { + displayName: 'Street', + name: 'Shipping_Street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'Shipping_City', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'Shipping_State', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'Shipping_Country', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'Shipping_Code', + type: 'string', + default: '', + }, + ], + }, + ], +}; + +export const mailingAddress = { + displayName: 'Mailing Address', + name: 'Mailing_Address', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Mailing Address Field', + options: [ + { + displayName: 'Mailing Address Fields', + name: 'address_fields', + values: [ + { + displayName: 'Street', + name: 'Mailing_Street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'Mailing_City', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'Mailing_State', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'Mailing_Country', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'Mailing_Zip', + type: 'string', + default: '', + }, + ], + }, + ], +}; + +export const otherAddress = { + displayName: 'Other Address', + name: 'Other_Address', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Other Address Field', + options: [ + { + displayName: 'Other Address Fields', + name: 'address_fields', + values: [ + { + displayName: 'Street', + name: 'Other_Street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'Other_City', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'Other_State', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'Other_Zip', + type: 'string', + default: '', + }, + ], + }, + ], +}; + +export const address = { + displayName: 'Address', + name: 'Address', + type: 'fixedCollection', + default: {}, + placeholder: 'Add Address Field', + options: [ + { + displayName: 'Address Fields', + name: 'address_fields', + values: [ + { + displayName: 'Street', + name: 'Street', + type: 'string', + default: '', + }, + { + displayName: 'City', + name: 'City', + type: 'string', + default: '', + }, + { + displayName: 'State', + name: 'State', + type: 'string', + default: '', + }, + { + displayName: 'Country', + name: 'Country', + type: 'string', + default: '', + }, + { + displayName: 'Zip Code', + name: 'Zip_Code', + type: 'string', + default: '', + }, + ], + }, + ], +}; + +// displayName: 'Products', +// name: 'Product_Details', +// type: 'collection', +// typeOptions: { +// multipleValues: true, +// multipleValueButtonText: 'Add Product', +// }, +// default: {}, +// placeholder: 'Add Field', +// displayOptions: { +// show: { +// resource: [ +// resource, +// ], +// operation: [ +// operation, +// ], +// }, +// }, + +export const productDetailsOptions = [ + { + displayName: 'List Price', + name: 'list_price', + type: 'number', + default: '', + }, + { + displayName: 'Product ID', + name: 'id', + type: 'options', + default: [], + typeOptions: { + loadOptionsMethod: 'getProducts', + }, + }, + { + displayName: 'Product Description', + name: 'product_description', + type: 'string', + default: '', + }, + { + displayName: 'Quantity', + name: 'quantity', + type: 'number', + default: 1, + }, + { + displayName: 'Quantity in Stock', + name: 'quantity_in_stock', + type: 'number', + default: 0, + }, + { + displayName: 'Tax', + name: 'Tax', + type: 'number', + default: 0, + }, + { + displayName: 'Total', + name: 'total', + type: 'number', + default: 0, + }, + { + displayName: 'Total After Discount', + name: 'total_after_discount', + type: 'number', + default: 0, + }, + { + displayName: 'Total (Net)', + name: 'net_total', + type: 'number', + default: 0, + }, + { + displayName: 'Unit Price', + name: 'unit_price', + type: 'number', + default: 0, + }, +]; + +export const makeGetAllFields = (resource: CamelCaseResource) => { + const loadOptionsMethod = `get${capitalizeInitial(resource)}Fields`; + + return [ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Return all results.', + displayOptions: { + show: { + resource: [ + resource, + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 5, + description: 'The number of results to return.', + typeOptions: { + minValue: 1, + maxValue: 1000, + }, + displayOptions: { + show: { + resource: [ + resource, + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + resource, + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Approved', + name: 'approved', + type: 'boolean', + default: true, + description: 'Retrieve only approved records. Defaults to true.', + }, + { + displayName: 'Converted', + name: 'converted', + type: 'boolean', + default: false, + description: 'Retrieve only converted records. Defaults to false.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod, + }, + default: [], + description: 'Return only these fields.', + }, + { + displayName: 'Include Child', + name: 'include_child', + type: 'boolean', + default: false, + description: 'Retrieve only records from child territories.', + }, + { + displayName: 'Sort By', + name: 'sort_by', + type: 'options', + typeOptions: { + loadOptionsMethod, + }, + default: [], + description: 'Field to sort records by.', + }, + { + displayName: 'Sort Order', + name: 'sort_order', + type: 'options', + options: [ + { + name: 'Ascending', + value: 'asc', + }, + { + name: 'Descending', + value: 'desc', + }, + ], + default: 'desc', + description: 'Ascending or descending order sort order.', + }, + { + displayName: 'Territory ID', + name: 'territory_id', + type: 'string', + default: '', + description: 'Retrieve only records from this territory.', + }, + ], + }, + ]; +}; + +export const makeCustomFieldsFixedCollection = (resource: CamelCaseResource) => { + const loadOptionsMethod = `getCustom${capitalizeInitial(resource)}Fields`; + + return { + displayName: 'Custom Fields', + name: 'customFields', + placeholder: 'Add Custom Field', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + description: 'Filter by custom fields.', + default: {}, + options: [ + { + name: 'customFields', + displayName: 'Custom Field', + values: [ + { + displayName: 'Field ID', + name: 'fieldId', + type: 'options', + typeOptions: { + loadOptionsMethod, + }, + default: '', + description: 'Custom field to set a value to.', + }, + { + displayName: 'Value', + name: 'value', + type: 'string', + default: '', + description: 'Value to set on custom field.', + }, + ], + }, + ], + }; +}; + +// https://www.zoho.com/subscriptions/help/supported-currencies.html + +export const currencies = [ + { name: 'US Dollar', value: 'USD' }, + { name: 'Euro', value: 'EUR' }, + { name: 'UAE Dirham', value: 'AED' }, + { name: 'Afghani', value: 'AFN' }, + { name: 'Lek', value: 'ALL' }, + { name: 'Argentine Peso', value: 'ARS' }, + { name: 'Australian Dollar', value: 'AUD' }, + { name: 'Azerbaijan Manat', value: 'AZN' }, + { name: 'Barbados Dollar', value: 'BBD' }, + { name: 'Taka', value: 'BDT' }, + { name: 'Bulgarian Lev', value: 'BGN' }, + { name: 'Bermudian Dollar', value: 'BMD' }, + { name: 'Brunei Dollar', value: 'BND' }, + { name: 'Boliviano', value: 'BOB' }, + { name: 'Brazilian Real', value: 'BRL' }, + { name: 'Bahamian Dollar', value: 'BSD' }, + { name: 'Pula', value: 'BWP' }, + { name: 'Belize Dollar', value: 'BZD' }, + { name: 'Canadian Dollar', value: 'CAD' }, + { name: 'Swiss Franc', value: 'CHF' }, + { name: 'Chilean Peso', value: 'CLP' }, + { name: 'Yuan Renminbi', value: 'CNY' }, + { name: 'Colombian Peso', value: 'COP' }, + { name: 'Costa Rican Colon', value: 'CRC' }, + { name: 'Czech Koruna', value: 'CZK' }, + { name: 'Danish Krone', value: 'DKK' }, + { name: 'Dominican Peso', value: 'DOP' }, + { name: 'Algerian Dinar', value: 'DZD' }, + { name: 'Egyptian Pound', value: 'EGP' }, + { name: 'Fiji Dollar', value: 'FJD' }, + { name: 'Pound Sterling', value: 'GBP' }, + { name: 'Quetzal', value: 'GTQ' }, + { name: 'Hong Kong Dollar', value: 'HKD' }, + { name: 'Lempira', value: 'HNL' }, + { name: 'Kuna', value: 'HRK' }, + { name: 'Forint', value: 'HUF' }, + { name: 'Rupiah', value: 'IDR' }, + { name: 'New Israeli Sheqel', value: 'ILS' }, + { name: 'Indian Rupee', value: 'INR' }, + { name: 'Jamaican Dollar', value: 'JMD' }, + { name: 'Yen', value: 'JPY' }, + { name: 'Kenyan Shilling', value: 'KES' }, + { name: 'Won', value: 'KRW' }, + { name: 'Tenge', value: 'KZT' }, + { name: 'Lao Kip', value: 'LAK' }, + { name: 'Lebanese Pound', value: 'LBP' }, + { name: 'Sri Lanka Rupee', value: 'LKR' }, + { name: 'Liberian Dollar', value: 'LRD' }, + { name: 'Moroccan Dirham', value: 'MAD' }, + { name: 'Kyat', value: 'MMK' }, + { name: 'Pataca', value: 'MOP' }, + { name: 'Ouguiya', value: 'MRO' }, + { name: 'Mauritius Rupee', value: 'MUR' }, + { name: 'Rufiyaa', value: 'MVR' }, + { name: 'Mexican Peso', value: 'MXN' }, + { name: 'Malaysian Ringgit', value: 'MYR' }, + { name: 'Cordoba Oro', value: 'NIO' }, + { name: 'Norwegian Krone', value: 'NOK' }, + { name: 'Nepalese Rupee', value: 'NPR' }, + { name: 'New Zealand Dollar', value: 'NZD' }, + { name: 'Sol', value: 'PEN' }, + { name: 'Kina', value: 'PGK' }, + { name: 'Philippine Peso', value: 'PHP' }, + { name: 'Pakistan Rupee', value: 'PKR' }, + { name: 'Zloty', value: 'PLN' }, + { name: 'Qatari Rial', value: 'QAR' }, + { name: 'Romanian Leu', value: 'RON' }, + { name: 'Russian Ruble', value: 'RUB' }, + { name: 'Saudi Riyal', value: 'SAR' }, + { name: 'Solomon Islands Dollar ', value: 'SBD' }, + { name: 'Seychelles Rupee', value: 'SCR' }, + { name: 'Swedish Krona', value: 'SEK' }, + { name: 'Singapore Dollar', value: 'SGD' }, + { name: 'Syrian Pound', value: 'SYP' }, + { name: 'Baht', value: 'THB' }, + { name: 'Pa’anga', value: 'TOP' }, + { name: 'Turkish Lira', value: 'TRY' }, + { name: 'Trinidad and Tobago Dollar', value: 'TTD' }, + { name: 'New Taiwan Dollar', value: 'TWD' }, + { name: 'Hryvnia', value: 'UAH' }, + { name: 'Dong', value: 'VND' }, + { name: 'Vatu', value: 'VUV' }, + { name: 'Tala', value: 'WST' }, + { name: 'East Caribbean Dollar', value: 'XCD' }, + { name: 'West African CFA Franc', value: 'XOF' }, + { name: 'Yemeni Rial', value: 'YER' }, + { name: 'Rand', value: 'ZAR' }, +]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/VendorDescription.ts b/packages/nodes-base/nodes/Zoho/descriptions/VendorDescription.ts new file mode 100644 index 0000000000..ff15df5ea7 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/VendorDescription.ts @@ -0,0 +1,301 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + address, + currencies, + makeCustomFieldsFixedCollection, + makeGetAllFields, +} from './SharedFields'; + +export const vendorOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a vendor', + }, + { + name: 'Create or Update', + value: 'upsert', + description: 'Create a new record, or update the current one if it already exists (upsert)', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a vendor', + }, + { + name: 'Get', + value: 'get', + description: 'Get a vendor', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all vendors', + }, + { + name: 'Update', + value: 'update', + description: 'Update a vendor', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const vendorFields = [ + // ---------------------------------------- + // vendor: create + // ---------------------------------------- + { + displayName: 'Vendor Name', + name: 'vendorName', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'create', + ], + }, + }, + }, + + // ---------------------------------------- + // vendor: upsert + // ---------------------------------------- + { + displayName: 'Vendor Name', + name: 'vendorName', + description: 'Name of the vendor. If a record with this vendor name exists it will be updated, otherwise a new one will be created.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'upsert', + ], + }, + }, + }, + + // ---------------------------------------- + // vendor: create + upsert + // ---------------------------------------- + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'create', + 'upsert', + ], + }, + }, + options: [ + address, + { + displayName: 'Category', + name: 'Category', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'options', + default: 'USD', + options: currencies, + }, + makeCustomFieldsFixedCollection('vendor'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Email', + name: 'Email', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, + + // ---------------------------------------- + // vendor: delete + // ---------------------------------------- + { + displayName: 'Vendor ID', + name: 'vendorId', + description: 'ID of the vendor to delete.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // vendor: get + // ---------------------------------------- + { + displayName: 'Vendor ID', + name: 'vendorId', + description: 'ID of the vendor to retrieve.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // vendor: getAll + // ---------------------------------------- + ...makeGetAllFields('vendor'), + + // ---------------------------------------- + // vendor: update + // ---------------------------------------- + { + displayName: 'Vendor ID', + name: 'vendorId', + description: 'ID of the vendor to update.', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + address, + { + displayName: 'Category', + name: 'Category', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'string', + default: '', + }, + makeCustomFieldsFixedCollection('vendor'), + { + displayName: 'Description', + name: 'Description', + type: 'string', + default: '', + }, + { + displayName: 'Email', + name: 'Email', + type: 'string', + default: '', + }, + { + displayName: 'Phone', + name: 'Phone', + type: 'string', + default: '', + }, + { + displayName: 'Vendor Name', + name: 'Vendor_Name', + type: 'string', + default: '', + }, + { + displayName: 'Website', + name: 'Website', + type: 'string', + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Zoho/descriptions/index.ts b/packages/nodes-base/nodes/Zoho/descriptions/index.ts new file mode 100644 index 0000000000..152b54ef2b --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/index.ts @@ -0,0 +1,10 @@ +export * from './AccountDescription'; +export * from './ContactDescription'; +export * from './DealDescription'; +export * from './InvoiceDescription'; +export * from './LeadDescription'; +export * from './ProductDescription'; +export * from './PurchaseOrderDescription'; +export * from './QuoteDescription'; +export * from './SalesOrderDescription'; +export * from './VendorDescription'; diff --git a/packages/nodes-base/nodes/Zoho/types.d.ts b/packages/nodes-base/nodes/Zoho/types.d.ts new file mode 100644 index 0000000000..46da58379c --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/types.d.ts @@ -0,0 +1,103 @@ +import { IDataObject } from "n8n-workflow"; + +// ---------------------------------------- +// for generic functions +// ---------------------------------------- + +type Resource = 'account' | 'contact' | 'deal' | 'invoice' | 'lead' | 'product' | 'quote' | 'vendor'; + +export type CamelCaseResource = Resource | 'purchaseOrder' | 'salesOrder'; + +export type SnakeCaseResource = Resource | 'purchase_order' | 'sales_order'; + +export type GetAllFilterOptions = { + fields: string[], + [otherOptions: string]: unknown; +}; + +// ---------------------------------------- +// for auth +// ---------------------------------------- + +export type ZohoOAuth2ApiCredentials = { + oauthTokenData: { + api_domain: string; + }; +}; + +// ---------------------------------------- +// for field adjusters +// ---------------------------------------- + +export type IdType = 'accountId' | 'contactId' | 'dealId' | 'purchaseOrderId'; + +export type NameType = 'Account_Name' | 'Full_Name' | 'Deal_Name' | 'Product_Name' | 'Vendor_Name'; + +type LocationType = 'Address' | 'Billing_Address' | 'Mailing_Address' | 'Shipping_Address' | 'Other_Address'; + +type DateType = 'Date_of_Birth' | 'Closing_Date' | 'Due_Date' | 'Invoice_Date' | 'PO_Date' | 'Valid_Till'; + +export type AllFields = + { [Date in DateType]?: string } & + { [Location in LocationType]?: { address_fields: { [key: string]: string } } } & + { Account?: { subfields: { id: string; name: string; } } } & + { [key in 'accountId' | 'contactId' | 'dealId']?: string } & + { customFields?: { customFields: Array<{ fieldId: string; value: string; }> } } & + { Product_Details?: ProductDetails } & + IDataObject; + +export type ProductDetails = Array<{ id: string, quantity: number }>; + +export type ResourceItems = Array<{ [key: string]: string }>; + +// ---------------------------------------- +// for resource loaders +// ---------------------------------------- + +export type LoadedAccounts = Array<{ + Account_Name: string; + id: string; +}>; + +export type LoadedContacts = Array<{ + Full_Name: string; + id: string; +}>; + +export type LoadedDeals = Array<{ + Deal_Name: string; + id: string; +}>; + +export type LoadedFields = { + fields: Array<{ + field_label: string; + api_name: string; + custom_field: boolean; + }> +}; + +export type LoadedVendors = Array<{ + Vendor_Name: string; + id: string; +}>; + +export type LoadedProducts = Array<{ + Product_Name: string; + id: string; +}>; + +export type LoadedLayouts = { + layouts: Array<{ + sections: Array<{ + api_name: string; + fields: Array<{ + api_name: string; + pick_list_values: Array<{ + display_value: string; + actual_value: string; + }> + }> + }> + }> +} diff --git a/packages/nodes-base/nodes/Zoho/zoho.svg b/packages/nodes-base/nodes/Zoho/zoho.svg index 9fc3141978..203761c270 100644 --- a/packages/nodes-base/nodes/Zoho/zoho.svg +++ b/packages/nodes-base/nodes/Zoho/zoho.svg @@ -1 +1 @@ - \ No newline at end of file +