UNPKG

@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

394 lines 15.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.taxPercentageCache = void 0; exports.getTaxPercentage = getTaxPercentage; exports.adjustRateForTax = adjustRateForTax; exports.validateAndNormalizeSalesOrder = validateAndNormalizeSalesOrder; exports.createValidationError = createValidationError; exports.generateSampleSalesOrder = generateSampleSalesOrder; exports.sanitizeSalesOrderPayload = sanitizeSalesOrderPayload; const n8n_workflow_1 = require("n8n-workflow"); exports.taxPercentageCache = new Map(); async function getTaxPercentage(taxId, zohoApiRequest, context) { if (exports.taxPercentageCache.has(taxId)) { return exports.taxPercentageCache.get(taxId); } try { const taxes = await zohoApiRequest.call(context, 'GET', '/settings/taxes'); for (const tax of taxes) { const percentage = parseFloat(tax.tax_percentage) || 0; exports.taxPercentageCache.set(tax.tax_id, percentage); } return exports.taxPercentageCache.get(taxId) || 0; } catch (error) { console.warn(`Failed to fetch tax percentage for tax_id ${taxId}:`, error); return 0; } } function adjustRateForTax(item, taxPercentage, isGlobalTaxInclusive = false) { const isTaxInclusive = item.rate_includes_tax ?? isGlobalTaxInclusive; if (!isTaxInclusive || !item.tax_id || taxPercentage <= 0) { return item; } const netRate = +(item.rate / (1 + taxPercentage / 100)).toFixed(2); return { ...item, rate: netRate, rate_includes_tax: undefined }; } function validateAndNormalizeSalesOrder(inputData, _context = 'Sales Order Creation') { const result = { isValid: false, errors: [], warnings: [], }; try { if (!inputData || typeof inputData !== 'object') { result.errors.push('Input data must be a valid object'); return result; } const normalized = { customer_id: '', line_items: [], }; if (!inputData.customer_id) { result.errors.push('customer_id is required'); } else { normalized.customer_id = String(inputData.customer_id).trim(); if (!normalized.customer_id) { result.errors.push('customer_id cannot be empty'); } } if (!inputData.line_items || !Array.isArray(inputData.line_items)) { result.errors.push('line_items must be an array'); } else if (inputData.line_items.length === 0) { result.errors.push('At least one line item is required'); } else { normalized.line_items = []; for (let i = 0; i < inputData.line_items.length; i++) { const item = inputData.line_items[i]; const itemValidation = validateLineItem(item, i); if (!itemValidation.isValid) { result.errors.push(...itemValidation.errors); } else if (itemValidation.normalizedItem) { normalized.line_items.push(itemValidation.normalizedItem); } } } if (inputData.salesorder_number !== undefined) { normalized.salesorder_number = String(inputData.salesorder_number).trim(); } if (inputData.date !== undefined) { const dateStr = String(inputData.date).trim(); if (dateStr && !isValidDate(dateStr)) { result.warnings.push(`Invalid date format for 'date': ${dateStr}. Expected YYYY-MM-DD format.`); } else { normalized.date = dateStr; } } if (inputData.reference_number !== undefined) { normalized.reference_number = String(inputData.reference_number).trim(); } if (inputData.billing_address_id !== undefined) { normalized.billing_address_id = String(inputData.billing_address_id).trim(); } if (inputData.shipping_address_id !== undefined) { normalized.shipping_address_id = String(inputData.shipping_address_id).trim(); } if (inputData.currency_id !== undefined) { normalized.currency_id = String(inputData.currency_id).trim(); } if (inputData.exchange_rate !== undefined) { const rate = parseFloat(inputData.exchange_rate); if (isNaN(rate) || rate <= 0) { result.warnings.push('Invalid exchange_rate. Must be a positive number.'); } else { normalized.exchange_rate = rate; } } if (inputData.shipping_charge !== undefined) { const charge = parseFloat(inputData.shipping_charge); if (isNaN(charge) || charge < 0) { result.warnings.push('Invalid shipping_charge. Must be a non-negative number.'); } else { normalized.shipping_charge = charge; } } if (inputData.adjustment !== undefined) { const adjustment = parseFloat(inputData.adjustment); if (isNaN(adjustment)) { result.warnings.push('Invalid adjustment. Must be a number.'); } else { normalized.adjustment = adjustment; } } if (inputData.adjustment_description !== undefined) { normalized.adjustment_description = String(inputData.adjustment_description).trim(); } if (inputData.notes !== undefined) { normalized.notes = String(inputData.notes).trim(); } if (inputData.terms !== undefined) { normalized.terms = String(inputData.terms).trim(); } if (inputData.subject !== undefined) { normalized.subject = String(inputData.subject).trim(); } if (inputData.discount !== undefined) { const discount = parseFloat(inputData.discount); if (isNaN(discount) || discount < 0) { result.warnings.push('Invalid discount. Must be a non-negative number.'); } else { normalized.discount = discount; } } if (inputData.discount_type !== undefined) { if (!['entity_level', 'item_level'].includes(inputData.discount_type)) { result.warnings.push('Invalid discount_type. Must be "entity_level" or "item_level".'); } else { normalized.discount_type = inputData.discount_type; } } if (inputData.is_discount_before_tax !== undefined) { normalized.is_discount_before_tax = Boolean(inputData.is_discount_before_tax); } if (inputData.template_id !== undefined) { normalized.template_id = String(inputData.template_id).trim(); } if (inputData.salesperson_name !== undefined) { normalized.salesperson_name = String(inputData.salesperson_name).trim(); } if (inputData.delivery_date !== undefined) { const dateStr = String(inputData.delivery_date).trim(); if (dateStr && !isValidDate(dateStr)) { result.warnings.push(`Invalid date format for 'delivery_date': ${dateStr}. Expected YYYY-MM-DD format.`); } else { normalized.delivery_date = dateStr; } } if (inputData.shipment_date !== undefined) { const dateStr = String(inputData.shipment_date).trim(); if (dateStr && !isValidDate(dateStr)) { result.warnings.push(`Invalid date format for 'shipment_date': ${dateStr}. Expected YYYY-MM-DD format.`); } else { normalized.shipment_date = dateStr; } } if (inputData.place_of_supply !== undefined) { normalized.place_of_supply = String(inputData.place_of_supply).trim(); } if (inputData.gst_treatment !== undefined) { if (!['business_gst', 'business_none', 'overseas', 'consumer'].includes(inputData.gst_treatment)) { result.warnings.push('Invalid gst_treatment. Must be one of: business_gst, business_none, overseas, consumer.'); } else { normalized.gst_treatment = inputData.gst_treatment; } } if (inputData.gst_no !== undefined) { normalized.gst_no = String(inputData.gst_no).trim(); } if (inputData.custom_fields !== undefined) { if (!Array.isArray(inputData.custom_fields)) { result.warnings.push('custom_fields must be an array'); } else { normalized.custom_fields = []; for (const field of inputData.custom_fields) { if (field && field.customfield_id && field.value !== undefined) { normalized.custom_fields.push({ customfield_id: String(field.customfield_id).trim(), value: String(field.value).trim(), }); } else { result.warnings.push('Invalid custom field. Must have customfield_id and value.'); } } } } if (result.errors.length === 0) { result.isValid = true; result.normalizedData = normalized; } } catch (error) { result.errors.push(`Unexpected error during validation: ${error instanceof Error ? error.message : String(error)}`); } return result; } function validateLineItem(item, index) { const errors = []; const normalizedItem = { quantity: 0, rate: 0, }; if (item.quantity === undefined || item.quantity === null) { errors.push(`Line item ${index + 1}: quantity is required`); } else { const quantity = Number(item.quantity); if (isNaN(quantity) || quantity <= 0) { errors.push(`Line item ${index + 1}: quantity must be a positive number`); } else { normalizedItem.quantity = quantity; } } if (item.rate === undefined || item.rate === null) { errors.push(`Line item ${index + 1}: rate is required`); } else { const rate = Number(item.rate); if (isNaN(rate) || rate < 0) { errors.push(`Line item ${index + 1}: rate must be a non-negative number`); } else { normalizedItem.rate = rate; } } if (item.item_id !== undefined) { normalizedItem.item_id = String(item.item_id).trim(); } if (item.name !== undefined) { normalizedItem.name = String(item.name).trim(); } if (item.description !== undefined) { normalizedItem.description = String(item.description).trim(); } if (item.tax_id !== undefined) { normalizedItem.tax_id = String(item.tax_id).trim(); } if (item.discount !== undefined) { const discount = Number(item.discount); if (!isNaN(discount) && discount >= 0) { normalizedItem.discount = discount; } else { errors.push(`Line item ${index + 1}: discount should be a non-negative number`); } } if (item.unit !== undefined) { normalizedItem.unit = String(item.unit).trim(); } if (item.item_total !== undefined) { const total = Number(item.item_total); if (!isNaN(total) && total >= 0) { normalizedItem.item_total = total; } else { errors.push(`Line item ${index + 1}: item_total should be a non-negative number`); } } if (item.rate_includes_tax !== undefined) { normalizedItem.rate_includes_tax = Boolean(item.rate_includes_tax); } return { isValid: errors.length === 0, errors, normalizedItem: errors.length === 0 ? normalizedItem : undefined, }; } function isValidDate(dateString) { const dateRegex = /^\d{4}-\d{2}-\d{2}$/; if (!dateRegex.test(dateString)) { return false; } const date = new Date(dateString); return date instanceof Date && !isNaN(date.getTime()); } function createValidationError(result, context = 'Sales Order') { const errorMessage = result.errors.length > 0 ? `${context} validation failed: ${result.errors.join('; ')}` : `${context} validation failed`; const description = result.warnings.length > 0 ? `Warnings: ${result.warnings.join('; ')}` : 'Please check the input data and try again'; return new n8n_workflow_1.NodeApiError({}, { message: errorMessage, description, httpCode: '400', }); } function generateSampleSalesOrder() { return { customer_id: '123456789', salesorder_number: 'SO-100045', date: '2025-01-15', reference_number: 'WEB-ORDER-8787', line_items: [ { item_id: '432198765', name: 'Blue Widget XL', description: 'Custom engraving', quantity: 2, rate: 29.99, tax_id: '111222333', discount: 0, }, { item_id: '654321987', name: 'Red Widget Pro', quantity: 1, rate: 120.00, tax_id: '111222333', }, { item_id: '987654321', name: 'Premium Widget (Tax-Inclusive)', quantity: 1, rate: 118.00, tax_id: '111222333', rate_includes_tax: true, }, { item_id: '555666777', name: 'Standard Widget (Net Rate)', quantity: 3, rate: 50.00, tax_id: '111222333', rate_includes_tax: false, }, ], shipping_charge: 10.0, adjustment: -5.0, adjustment_description: 'Promo', notes: 'Thank you for your business!', terms: 'Payable on delivery.', }; } function sanitizeSalesOrderPayload(payload) { const sanitized = { ...payload }; Object.keys(sanitized).forEach(key => { if (sanitized[key] === undefined || sanitized[key] === null) { delete sanitized[key]; } }); if (sanitized.line_items && Array.isArray(sanitized.line_items)) { sanitized.line_items = sanitized.line_items.map(item => { const sanitizedItem = { ...item }; Object.keys(sanitizedItem).forEach(key => { if (sanitizedItem[key] === undefined || sanitizedItem[key] === null) { delete sanitizedItem[key]; } }); return sanitizedItem; }); } return sanitized; } //# sourceMappingURL=SalesOrderNormalizer.js.map