diff --git a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts index cb795f3f91..104b259e18 100644 --- a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts @@ -24,6 +24,7 @@ import { LocationType, NameType, ProductDetails, + ResourceItems, ZohoOAuth2ApiCredentials, } from './types'; @@ -58,7 +59,6 @@ export async function zohoApiRequest( } try { - console.log(JSON.stringify(options, null, 2)); return await this.helpers.requestOAuth2?.call(this, 'zohoOAuth2Api', options); } catch (error) { throw new NodeApiError(this.getNode(), error); @@ -118,7 +118,7 @@ export async function handleListing( } // ---------------------------------------- -// field adjusters +// required field adjusters // ---------------------------------------- /** @@ -133,6 +133,10 @@ export const adjustProductDetails = (productDetails: ProductDetails) => { }); }; +// ---------------------------------------- +// additional field adjusters +// ---------------------------------------- + /** * Place a location field's contents at the top level of the payload. */ @@ -191,20 +195,24 @@ const adjustAccountIdField = adjustIdField('accountId', 'Account_Name'); const adjustContactIdField = adjustIdField('contactId', 'Full_Name'); const adjustDealIdField = adjustIdField('dealId', 'Deal_Name'); -export const adjustAccountFields = flow( +// ---------------------------------------- +// payload adjusters +// ---------------------------------------- + +export const adjustAccountPayload = flow( adjustBillingAddressFields, adjustShippingAddressFields, ); -export const adjustContactFields = flow( +export const adjustContactPayload = flow( adjustMailingAddressFields, adjustOtherAddressFields, adjustDateOfBirthField, ); -export const adjustDealFields = adjustClosingDateField; +export const adjustDealPayload = adjustClosingDateField; -export const adjustInvoiceFields = flow( +export const adjustInvoicePayload = flow( adjustBillingAddressFields, adjustShippingAddressFields, adjustInvoiceDateField, @@ -212,22 +220,22 @@ export const adjustInvoiceFields = flow( adjustAccountIdField, ); -export const adjustLeadFields = adjustAddressFields; +export const adjustLeadPayload = adjustAddressFields; -export const adjustPurchaseOrderFields = flow( +export const adjustPurchaseOrderPayload = flow( adjustBillingAddressFields, adjustShippingAddressFields, adjustDueDateField, adjustPurchaseOrderDateField, ); -export const adjustQuoteFields = flow( +export const adjustQuotePayload = flow( adjustBillingAddressFields, adjustShippingAddressFields, adjustValidTillField, ); -export const adjustSalesOrderFields = flow( +export const adjustSalesOrderPayload = flow( adjustBillingAddressFields, adjustShippingAddressFields, adjustDueDateField, @@ -236,11 +244,19 @@ export const adjustSalesOrderFields = flow( adjustDealIdField, ); +export const adjustVendorPayload = adjustAddressFields; + // ---------------------------------------- // helpers // ---------------------------------------- -const omit = (keyToOmit: string, { [keyToOmit]: _, ...omittedPropObj }) => omittedPropObj; +/** + * Create a copy of an object without a specific property. + */ +const omit = (propertyToOmit: string, { [propertyToOmit]: _, ...remainingObject }) => remainingObject; -export const toLoadOptions = (resourceItems: Array<{ [key: string]: string }>, nameProperty: NameType) => - resourceItems.map((item) => ({ name: item[nameProperty], value: item.id })); +/** + * 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 })); diff --git a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts index b7d9457731..b8ecae852c 100644 --- a/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts +++ b/packages/nodes-base/nodes/Zoho/ZohoCrm.node.ts @@ -11,15 +11,16 @@ import { } from 'n8n-workflow'; import { - adjustAccountFields, - adjustContactFields, - adjustDealFields, - adjustInvoiceFields, - adjustLeadFields, + adjustAccountPayload, + adjustContactPayload, + adjustDealPayload, + adjustInvoicePayload, + adjustLeadPayload, adjustProductDetails, - adjustPurchaseOrderFields, - adjustQuoteFields, - adjustSalesOrderFields, + adjustPurchaseOrderPayload, + adjustQuotePayload, + adjustSalesOrderPayload, + adjustVendorPayload, handleListing, toLoadOptions, zohoApiRequest, @@ -54,6 +55,8 @@ import { quoteOperations, salesOrderFields, salesOrderOperations, + vendorFields, + vendorOperations, } from './descriptions'; export class ZohoCrm implements INodeType { @@ -119,6 +122,10 @@ export class ZohoCrm implements INodeType { name: 'Sales Order', value: 'salesOrder', }, + { + name: 'Vendor', + value: 'vendor', + }, ], default: 'account', description: 'Resource to consume', @@ -141,6 +148,8 @@ export class ZohoCrm implements INodeType { ...quoteFields, ...salesOrderOperations, ...salesOrderFields, + ...vendorOperations, + ...vendorFields, ], }; @@ -210,7 +219,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustAccountFields(additionalFields)); + Object.assign(body, adjustAccountPayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/accounts', body); @@ -255,7 +264,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustAccountFields(updateFields)); + Object.assign(body, adjustAccountPayload(updateFields)); } const accountId = this.getNodeParameter('accountId', i); @@ -286,7 +295,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustContactFields(additionalFields)); + Object.assign(body, adjustContactPayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/contacts', body); @@ -331,7 +340,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustContactFields(updateFields)); + Object.assign(body, adjustContactPayload(updateFields)); } const contactId = this.getNodeParameter('contactId', i); @@ -363,7 +372,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustDealFields(additionalFields)); + Object.assign(body, adjustDealPayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/deals', body); @@ -406,7 +415,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustDealFields(updateFields)); + Object.assign(body, adjustDealPayload(updateFields)); } const dealId = this.getNodeParameter('dealId', i); @@ -439,7 +448,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustInvoiceFields(additionalFields)); + Object.assign(body, adjustInvoicePayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/invoices', body); @@ -484,7 +493,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustInvoiceFields(updateFields)); + Object.assign(body, adjustInvoicePayload(updateFields)); } const invoiceId = this.getNodeParameter('invoiceId', i); @@ -516,7 +525,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustLeadFields(additionalFields)); + Object.assign(body, adjustLeadPayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/leads', body); @@ -559,7 +568,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustLeadFields(updateFields)); + Object.assign(body, adjustLeadPayload(updateFields)); } const leadId = this.getNodeParameter('leadId', i); @@ -669,7 +678,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustPurchaseOrderFields(additionalFields)); + Object.assign(body, adjustPurchaseOrderPayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/purchase_orders', body); @@ -714,7 +723,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustPurchaseOrderFields(updateFields)); + Object.assign(body, adjustPurchaseOrderPayload(updateFields)); } const purchaseOrderId = this.getNodeParameter('purchaseOrderId', i); @@ -748,7 +757,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustQuoteFields(additionalFields)); + Object.assign(body, adjustQuotePayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/quotes', body); @@ -791,7 +800,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustQuoteFields(updateFields)); + Object.assign(body, adjustQuotePayload(updateFields)); } const quoteId = this.getNodeParameter('quoteId', i); @@ -825,7 +834,7 @@ export class ZohoCrm implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; if (Object.keys(additionalFields).length) { - Object.assign(body, adjustSalesOrderFields(additionalFields)); + Object.assign(body, adjustSalesOrderPayload(additionalFields)); } responseData = await zohoApiRequest.call(this, 'POST', '/sales_orders', body); @@ -870,7 +879,7 @@ export class ZohoCrm implements INodeType { const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; if (Object.keys(updateFields).length) { - Object.assign(body, adjustSalesOrderFields(updateFields)); + Object.assign(body, adjustSalesOrderPayload(updateFields)); } const salesOrderId = this.getNodeParameter('salesOrderId', i); @@ -880,6 +889,82 @@ export class ZohoCrm implements INodeType { } + } else if (resource === 'vendor') { + + // ********************************************************************** + // vendor + // ********************************************************************** + + // https://www.zoho.com/crm/developer/docs/api/v2/vendors-response.html + + 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); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // vendor: delete + // ---------------------------------------- + + const vendorId = this.getNodeParameter('vendorId', i); + + const endpoint = `/vendors/${vendorId}`; + responseData = await zohoApiRequest.call(this, 'DELETE', endpoint); + + } else if (operation === 'get') { + + // ---------------------------------------- + // vendor: get + // ---------------------------------------- + + const vendorId = this.getNodeParameter('vendorId', i); + + const endpoint = `/vendors/${vendorId}`; + responseData = await zohoApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // vendor: getAll + // ---------------------------------------- + + responseData = await handleListing.call(this, 'GET', '/vendors'); + + } 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)); + } + + const vendorId = this.getNodeParameter('vendorId', i); + + const endpoint = `/vendors/${vendorId}`; + responseData = await zohoApiRequest.call(this, 'PUT', endpoint, body); + + } + } Array.isArray(responseData) 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..b60f8f7d18 --- /dev/null +++ b/packages/nodes-base/nodes/Zoho/descriptions/VendorDescription.ts @@ -0,0 +1,259 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +import { + address, + makeGetAllFields, +} from './SharedFields'; + +export const vendorOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'vendor', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + }, + { + name: 'Delete', + value: 'delete', + }, + { + name: 'Get', + value: 'get', + }, + { + name: 'Get All', + value: 'getAll', + }, + { + name: 'Update', + value: 'update', + }, + ], + 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', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'vendor', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + address, + { + displayName: 'Category', + name: 'Category', + type: 'string', + default: '', + }, + { + displayName: 'Currency', + name: 'Currency', + type: 'string', + default: '', + }, + { + 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: '', + }, + { + 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 index 20b1584c60..152b54ef2b 100644 --- a/packages/nodes-base/nodes/Zoho/descriptions/index.ts +++ b/packages/nodes-base/nodes/Zoho/descriptions/index.ts @@ -7,3 +7,4 @@ 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 index 7c6b5ce88f..dd7592c0fb 100644 --- a/packages/nodes-base/nodes/Zoho/types.d.ts +++ b/packages/nodes-base/nodes/Zoho/types.d.ts @@ -31,6 +31,8 @@ export type AllFields = export type ProductDetails = Array<{ id: string, quantity: number }>; +export type ResourceItems = Array<{ [key: string]: string }>; + // ---------------------------------------- // for resource loaders // ----------------------------------------