@arathron/n8n-nodes-zoho-books
Version:
n8n community nodes for Zoho Books and Zoho Inventory API integration - Complete CRUD operations for Sales Orders, Invoices, Items, Vendors, Credit Notes, Payments, Purchase Orders, Bills, Composite Items, and Assemblies
766 lines • 31.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZohoInventory = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const GenericFunctions_1 = require("./utils/GenericFunctions");
class ZohoInventory {
description = {
displayName: 'Zoho Inventory',
name: 'zohoInventory',
icon: 'file:zoho-inventory.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Zoho Inventory API',
defaults: {
name: 'Zoho Inventory',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'zohoInventoryOAuth2',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Assembly',
value: 'assembly',
},
{
name: 'Composite Item',
value: 'compositeItem',
},
{
name: 'Item',
value: 'item',
},
],
default: 'compositeItem',
required: true,
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['compositeItem'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new composite item',
action: 'Create a composite item',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a composite item',
action: 'Delete a composite item',
},
{
name: 'Get',
value: 'get',
description: 'Get a composite item',
action: 'Get a composite item',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all composite items',
action: 'Get all composite items',
},
{
name: 'Update',
value: 'update',
description: 'Update a composite item',
action: 'Update a composite item',
},
],
default: 'create',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['assembly'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new assembly',
action: 'Create an assembly',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an assembly',
action: 'Delete an assembly',
},
{
name: 'Get',
value: 'get',
description: 'Get an assembly',
action: 'Get an assembly',
},
{
name: 'Get History',
value: 'getHistory',
description: 'Get assembly history',
action: 'Get assembly history',
},
],
default: 'create',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['item'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new item',
action: 'Create an item',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an item',
action: 'Delete an item',
},
{
name: 'Get',
value: 'get',
description: 'Get an item by ID',
action: 'Get an item',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all items',
action: 'Get all items',
},
{
name: 'Get by SKU',
value: 'getBySku',
description: 'Get an item by SKU code',
action: 'Get item by SKU',
},
{
name: 'Update',
value: 'update',
description: 'Update an item',
action: 'Update an item',
},
],
default: 'getAll',
},
{
displayName: 'SKU Code',
name: 'skuCode',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['item'],
operation: ['getBySku'],
},
},
description: 'The SKU code of the item to retrieve',
placeholder: 'e.g., ITEM-001',
},
{
displayName: 'Item ID',
name: 'itemId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['item'],
operation: ['get', 'delete', 'update'],
},
},
description: 'The ID of the item',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['item'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['item'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 200,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Composite Item ID',
name: 'compositeItemId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['get', 'delete', 'update'],
},
},
default: '',
description: 'The ID of the composite item',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['create', 'update'],
},
},
default: '',
description: 'Name of the composite item',
},
{
displayName: 'SKU',
name: 'sku',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['create'],
},
},
default: '',
description: 'Stock Keeping Unit (SKU) of the composite item',
},
{
displayName: 'Rate',
name: 'rate',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
required: true,
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['create', 'update'],
},
},
default: 0,
description: 'Selling price of the composite item',
},
{
displayName: 'Line Items',
name: 'lineItems',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['create', 'update'],
},
},
default: {},
placeholder: 'Add Line Item',
options: [
{
name: 'lineItem',
displayName: 'Line Item',
values: [
{
displayName: 'Item ID',
name: 'item_id',
type: 'string',
default: '',
required: true,
description: 'ID of the component item',
},
{
displayName: 'Quantity',
name: 'quantity',
type: 'number',
typeOptions: {
numberPrecision: 3,
},
default: 1,
required: true,
description: 'Quantity of the component item',
},
],
},
],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['create', 'update'],
},
},
options: [
{
displayName: 'Description',
name: 'description',
type: 'string',
typeOptions: {
rows: 4,
},
default: '',
description: 'Description of the composite item',
},
{
displayName: 'Purchase Rate',
name: 'purchase_rate',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: 0,
description: 'Cost price of the composite item',
},
{
displayName: 'Reorder Level',
name: 'reorder_level',
type: 'number',
default: 0,
description: 'Reorder level of the composite item',
},
{
displayName: 'Initial Stock',
name: 'initial_stock',
type: 'number',
default: 0,
description: 'Initial stock quantity',
},
{
displayName: 'Initial Stock Rate',
name: 'initial_stock_rate',
type: 'number',
typeOptions: {
numberPrecision: 2,
},
default: 0,
description: 'Initial stock rate per unit',
},
],
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['compositeItem'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 200,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Assembly ID',
name: 'assemblyId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['assembly'],
operation: ['get', 'delete'],
},
},
default: '',
description: 'The ID of the assembly',
},
{
displayName: 'Composite Item ID',
name: 'compositeItemId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['assembly'],
operation: ['create', 'getHistory'],
},
},
default: '',
description: 'The ID of the composite item to assemble',
},
{
displayName: 'Quantity',
name: 'quantity',
type: 'number',
required: true,
displayOptions: {
show: {
resource: ['assembly'],
operation: ['create'],
},
},
default: 1,
description: 'Quantity to assemble',
},
{
displayName: 'Date',
name: 'date',
type: 'dateTime',
required: true,
displayOptions: {
show: {
resource: ['assembly'],
operation: ['create'],
},
},
default: '',
description: 'Date of assembly',
},
{
displayName: 'Reference Number',
name: 'reference_number',
type: 'string',
displayOptions: {
show: {
resource: ['assembly'],
operation: ['create'],
},
},
default: '',
description: 'Reference number for the assembly',
},
],
};
methods = {
loadOptions: {
async getItems() {
const returnData = [];
try {
const { items } = await zohoApiRequest.call(this, 'GET', '/items');
for (const item of items) {
returnData.push({
name: `${item.name} (${item.sku})`,
value: item.item_id,
});
}
return returnData;
}
catch (error) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: 'Failed to load items',
description: `Could not retrieve items from Zoho Inventory: ${error.message}`,
});
}
},
async getCompositeItems() {
const returnData = [];
try {
const { composite_items } = await zohoApiRequest.call(this, 'GET', '/compositeitems');
for (const item of composite_items) {
returnData.push({
name: `${item.name} (${item.sku})`,
value: item.composite_item_id,
});
}
return returnData;
}
catch (error) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: 'Failed to load composite items',
description: `Could not retrieve composite items from Zoho Inventory: ${error.message}`,
});
}
},
},
};
async execute() {
const items = this.getInputData();
const returnData = [];
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'compositeItem') {
if (operation === 'create') {
const name = this.getNodeParameter('name', i);
const sku = this.getNodeParameter('sku', i);
const rate = this.getNodeParameter('rate', i);
const lineItems = this.getNodeParameter('lineItems', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
const body = {
name,
sku,
rate,
mapped_items: lineItems.lineItem || [],
...additionalFields,
};
const response = await zohoApiRequest.call(this, 'POST', '/compositeitems', body);
returnData.push({ json: response.composite_item });
}
if (operation === 'get') {
const compositeItemId = this.getNodeParameter('compositeItemId', i);
const response = await zohoApiRequest.call(this, 'GET', `/compositeitems/${compositeItemId}`);
returnData.push({ json: response.composite_item });
}
if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i);
const limit = this.getNodeParameter('limit', i, 50);
const response = await zohoApiRequest.call(this, 'GET', '/compositeitems', {}, { page: 1, per_page: returnAll ? 200 : limit });
if (returnAll) {
returnData.push(...response.composite_items.map(item => ({ json: item })));
let page = 2;
let currentResponse = response;
while (currentResponse.page_context && currentResponse.page_context.has_more_page) {
currentResponse = await zohoApiRequest.call(this, 'GET', '/compositeitems', {}, { page, per_page: 200 });
returnData.push(...currentResponse.composite_items.map(item => ({ json: item })));
page++;
}
}
else {
returnData.push(...response.composite_items.slice(0, limit).map(item => ({ json: item })));
}
}
if (operation === 'update') {
const compositeItemId = this.getNodeParameter('compositeItemId', i);
const name = this.getNodeParameter('name', i);
const rate = this.getNodeParameter('rate', i);
const lineItems = this.getNodeParameter('lineItems', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
const body = {
name,
rate,
mapped_items: lineItems.lineItem || [],
...additionalFields,
};
const response = await zohoApiRequest.call(this, 'PUT', `/compositeitems/${compositeItemId}`, body);
returnData.push({ json: response.composite_item });
}
if (operation === 'delete') {
const compositeItemId = this.getNodeParameter('compositeItemId', i);
await zohoApiRequest.call(this, 'DELETE', `/compositeitems/${compositeItemId}`);
returnData.push({ json: { success: true, message: 'Composite item deleted successfully' } });
}
}
if (resource === 'assembly') {
if (operation === 'create') {
const compositeItemId = this.getNodeParameter('compositeItemId', i);
const quantity = this.getNodeParameter('quantity', i);
const date = this.getNodeParameter('date', i);
const referenceNumber = this.getNodeParameter('reference_number', i, '');
const body = {
composite_item_id: compositeItemId,
quantity_to_assemble: quantity,
date,
};
if (referenceNumber) {
body.reference_number = referenceNumber;
}
const response = await zohoApiRequest.call(this, 'POST', '/compositeitems/assemblies', body);
returnData.push({ json: response.assembly });
}
if (operation === 'get') {
const assemblyId = this.getNodeParameter('assemblyId', i);
const response = await zohoApiRequest.call(this, 'GET', `/compositeitems/assemblies/${assemblyId}`);
returnData.push({ json: response.assembly });
}
if (operation === 'getHistory') {
const compositeItemId = this.getNodeParameter('compositeItemId', i);
const response = await zohoApiRequest.call(this, 'GET', `/compositeitems/${compositeItemId}/assemblies`);
returnData.push(...response.assemblies.map(item => ({ json: item })));
}
if (operation === 'delete') {
const assemblyId = this.getNodeParameter('assemblyId', i);
await zohoApiRequest.call(this, 'DELETE', `/compositeitems/assemblies/${assemblyId}`);
returnData.push({ json: { success: true, message: 'Assembly deleted successfully' } });
}
}
if (resource === 'item') {
if (operation === 'getBySku') {
const skuCode = this.getNodeParameter('skuCode', i);
const item = await GenericFunctions_1.getItemBySku.call(this, skuCode);
returnData.push({ json: item });
}
else if (operation === 'get') {
const itemId = this.getNodeParameter('itemId', i);
const response = await zohoApiRequest.call(this, 'GET', `/items/${itemId}`);
returnData.push({ json: response.item });
}
else if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i, false);
if (returnAll) {
const allItems = await GenericFunctions_1.zohoApiRequestAllItems.call(this, 'GET', '/items');
returnData.push(...allItems.map(item => ({ json: item })));
}
else {
const limit = this.getNodeParameter('limit', i, 50);
const response = await zohoApiRequest.call(this, 'GET', '/items', {}, { per_page: limit });
const items = response.items || [];
returnData.push(...items.slice(0, limit).map((item) => ({ json: item })));
}
}
else if (operation === 'delete') {
const itemId = this.getNodeParameter('itemId', i);
await zohoApiRequest.call(this, 'DELETE', `/items/${itemId}`);
returnData.push({ json: { success: true, message: 'Item deleted successfully' } });
}
else if (operation === 'create') {
const body = this.getNodeParameter('itemData', i, {});
const response = await zohoApiRequest.call(this, 'POST', '/items', body);
returnData.push({ json: response.item });
}
else if (operation === 'update') {
const itemId = this.getNodeParameter('itemId', i);
const body = this.getNodeParameter('itemData', i, {});
const response = await zohoApiRequest.call(this, 'PUT', `/items/${itemId}`, body);
returnData.push({ json: response.item });
}
}
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.ZohoInventory = ZohoInventory;
async function zohoApiRequest(method, endpoint, body = {}, qs = {}) {
const credentials = await this.getCredentials('zohoInventoryOAuth2');
if (!credentials) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: 'Zoho Inventory credentials not found',
description: 'Please configure your Zoho Inventory OAuth2 credentials first.',
httpCode: '401',
});
}
const organizationId = credentials.organizationId;
if (!organizationId || organizationId.trim() === '') {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: 'Organization ID is missing',
description: 'Please configure Organization ID in your Zoho Inventory OAuth2 credentials. You can find it in Zoho Inventory → Settings → Organization Profile.',
httpCode: '400',
});
}
const baseUrl = getBaseUrl(credentials);
const options = {
method,
url: `${baseUrl}${endpoint}`,
qs: {
organization_id: organizationId,
...qs,
},
json: true,
headers: {
'X-com-zoho-inventory-organizationid': organizationId,
},
};
if (Object.keys(body).length > 0) {
options.body = body;
}
try {
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'zohoInventoryOAuth2', options);
if (response.code !== 0) {
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
message: response.message || 'Zoho Inventory API Error',
description: `Error Code: ${response.code}`,
});
}
return response;
}
catch (error) {
if (error instanceof n8n_workflow_1.NodeApiError) {
throw error;
}
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
}
}
function getBaseUrl(credentials) {
const dataCenter = credentials.dataCenter;
if (dataCenter === 'COM') {
return 'https://www.zohoapis.com/inventory/v1';
}
else if (dataCenter === 'AU') {
return 'https://www.zohoapis.com.au/inventory/v1';
}
else if (dataCenter === 'CN') {
return 'https://www.zohoapis.com.cn/inventory/v1';
}
else {
return `https://www.zohoapis.${dataCenter.toLowerCase()}/inventory/v1`;
}
}
//# sourceMappingURL=ZohoInventory.node.js.map