invoices purchase order sales order added for crm

This commit is contained in:
yashwin-foxy 2025-09-15 20:00:28 +05:30
parent 3df5c84d3a
commit 8ac686c13e
5 changed files with 230 additions and 3 deletions

View File

@ -203,4 +203,79 @@ async function getAllProjectPhases(req, res) {
} }
} }
module.exports = { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases }; async function getSalesOrders(req, res) {
try {
const { provider, page, limit, filters } = req.query;
if (provider !== 'zoho') {
return res.status(400).json(failure('Sales Orders are only available for Zoho provider', 'INVALID_PROVIDER'));
}
const integrationService = new IntegrationService(req.user.id);
const params = { page, limit };
if (filters) {
try {
params.filters = JSON.parse(filters);
} catch (e) {
return res.status(400).json(failure('Invalid filters format', 'INVALID_FILTERS'));
}
}
const salesOrders = await integrationService.getSalesOrders(provider, params);
res.json(success('Zoho Sales Orders retrieved successfully', salesOrders));
} catch (error) {
res.status(400).json(failure(error.message, 'INTEGRATION_ERROR'));
}
}
async function getPurchaseOrders(req, res) {
try {
const { provider, page, limit, filters } = req.query;
if (provider !== 'zoho') {
return res.status(400).json(failure('Purchase Orders are only available for Zoho provider', 'INVALID_PROVIDER'));
}
const integrationService = new IntegrationService(req.user.id);
const params = { page, limit };
if (filters) {
try {
params.filters = JSON.parse(filters);
} catch (e) {
return res.status(400).json(failure('Invalid filters format', 'INVALID_FILTERS'));
}
}
const purchaseOrders = await integrationService.getPurchaseOrders(provider, params);
res.json(success('Zoho Purchase Orders retrieved successfully', purchaseOrders));
} catch (error) {
res.status(400).json(failure(error.message, 'INTEGRATION_ERROR'));
}
}
async function getInvoices(req, res) {
try {
const { provider, page, limit, filters } = req.query;
if (provider !== 'zoho') {
return res.status(400).json(failure('Invoices are only available for Zoho provider', 'INVALID_PROVIDER'));
}
const integrationService = new IntegrationService(req.user.id);
const params = { page, limit };
if (filters) {
try {
params.filters = JSON.parse(filters);
} catch (e) {
return res.status(400).json(failure('Invalid filters format', 'INVALID_FILTERS'));
}
}
const invoices = await integrationService.getInvoices(provider, params);
res.json(success('Zoho Invoices retrieved successfully', invoices));
} catch (error) {
res.status(400).json(failure(error.message, 'INTEGRATION_ERROR'));
}
}
module.exports = { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases, getSalesOrders, getPurchaseOrders, getInvoices };

View File

@ -1,6 +1,6 @@
const express = require('express'); const express = require('express');
const Joi = require('joi'); const Joi = require('joi');
const { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases } = require('../controllers/integrationController'); const { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases, getSalesOrders, getPurchaseOrders, getInvoices } = require('../controllers/integrationController');
const auth = require('../middlewares/auth'); const auth = require('../middlewares/auth');
const ZohoHandler = require('../../integrations/zoho/handler'); const ZohoHandler = require('../../integrations/zoho/handler');
@ -116,6 +116,36 @@ const allProjectPhasesSchema = Joi.object({
router.get('/all-project-phases', auth, validate(allProjectPhasesSchema), getAllProjectPhases); router.get('/all-project-phases', auth, validate(allProjectPhasesSchema), getAllProjectPhases);
// Get Zoho Sales Orders
const salesOrdersSchema = Joi.object({
provider: Joi.string().valid('zoho').required(),
page: Joi.number().min(1).default(1),
limit: Joi.number().min(1).max(100).default(20),
filters: Joi.string().optional()
});
router.get('/sales-orders', auth, validate(salesOrdersSchema), getSalesOrders);
// Get Zoho Purchase Orders
const purchaseOrdersSchema = Joi.object({
provider: Joi.string().valid('zoho').required(),
page: Joi.number().min(1).default(1),
limit: Joi.number().min(1).max(100).default(20),
filters: Joi.string().optional()
});
router.get('/purchase-orders', auth, validate(purchaseOrdersSchema), getPurchaseOrders);
// Get Zoho Invoices
const invoicesSchema = Joi.object({
provider: Joi.string().valid('zoho').required(),
page: Joi.number().min(1).default(1),
limit: Joi.number().min(1).max(100).default(20),
filters: Joi.string().optional()
});
router.get('/invoices', auth, validate(invoicesSchema), getInvoices);
// Webhook endpoints (no auth required - uses signature verification) // Webhook endpoints (no auth required - uses signature verification)
const zohoHandler = new ZohoHandler(); const zohoHandler = new ZohoHandler();
router.post('/webhooks/zoho/crm', zohoHandler.handleCrmWebhook.bind(zohoHandler)); router.post('/webhooks/zoho/crm', zohoHandler.handleCrmWebhook.bind(zohoHandler));

View File

@ -112,6 +112,21 @@ class ZohoClient {
return ZohoMapper.mapApiResponse(response, 'tasks'); return ZohoMapper.mapApiResponse(response, 'tasks');
} }
async getSalesOrders(params = {}) {
const response = await this.makeRequest('/crm/v2/Sales_Orders', { params });
return ZohoMapper.mapApiResponse(response, 'sales_orders');
}
async getPurchaseOrders(params = {}) {
const response = await this.makeRequest('/crm/v2/Purchase_Orders', { params });
return ZohoMapper.mapApiResponse(response, 'purchase_orders');
}
async getInvoices(params = {}) {
const response = await this.makeRequest('/crm/v2/Invoices', { params });
return ZohoMapper.mapApiResponse(response, 'invoices');
}
// Zoho People methods // Zoho People methods
async getEmployees(params = {}) { async getEmployees(params = {}) {
const response = await this.makeRequest('/people/api/v1/employees', { params }, 'people'); const response = await this.makeRequest('/people/api/v1/employees', { params }, 'people');
@ -201,7 +216,7 @@ class ZohoClient {
getAvailableResources(service) { getAvailableResources(service) {
const resources = { const resources = {
'crm': ['leads', 'contacts', 'deals', 'tasks'], 'crm': ['leads', 'contacts', 'deals', 'tasks', 'sales_orders', 'purchase_orders', 'invoices'],
'people': ['employees', 'departments'], 'people': ['employees', 'departments'],
'projects': ['projects', 'tasks', 'timesheets'] 'projects': ['projects', 'tasks', 'timesheets']
}; };

View File

@ -54,6 +54,65 @@ class ZohoMapper {
}; };
} }
// Map Zoho CRM Sales Order to standardized format
static mapSalesOrder(zohoSalesOrder) {
return {
id: zohoSalesOrder.id,
subject: zohoSalesOrder.Subject,
orderNumber: zohoSalesOrder.SO_Number,
account: zohoSalesOrder.Account_Name,
contact: zohoSalesOrder.Contact_Name,
deal: zohoSalesOrder.Deal_Name,
grandTotal: zohoSalesOrder.Grand_Total,
status: zohoSalesOrder.Status,
orderDate: zohoSalesOrder.Order_Date,
dueDate: zohoSalesOrder.Due_Date,
createdTime: zohoSalesOrder.Created_Time,
modifiedTime: zohoSalesOrder.Modified_Time,
owner: zohoSalesOrder.Owner?.name,
customFields: this.mapCustomFields(zohoSalesOrder)
};
}
// Map Zoho CRM Purchase Order to standardized format
static mapPurchaseOrder(zohoPurchaseOrder) {
return {
id: zohoPurchaseOrder.id,
subject: zohoPurchaseOrder.Subject,
orderNumber: zohoPurchaseOrder.PO_Number,
vendor: zohoPurchaseOrder.Vendor_Name,
account: zohoPurchaseOrder.Account_Name,
grandTotal: zohoPurchaseOrder.Grand_Total,
status: zohoPurchaseOrder.Status,
orderDate: zohoPurchaseOrder.PO_Date,
dueDate: zohoPurchaseOrder.Expected_Delivery_Date,
createdTime: zohoPurchaseOrder.Created_Time,
modifiedTime: zohoPurchaseOrder.Modified_Time,
owner: zohoPurchaseOrder.Owner?.name,
customFields: this.mapCustomFields(zohoPurchaseOrder)
};
}
// Map Zoho CRM Invoice to standardized format
static mapInvoice(zohoInvoice) {
return {
id: zohoInvoice.id,
subject: zohoInvoice.Subject,
invoiceNumber: zohoInvoice.Invoice_Number,
account: zohoInvoice.Account_Name,
contact: zohoInvoice.Contact_Name,
deal: zohoInvoice.Deal_Name,
grandTotal: zohoInvoice.Grand_Total,
status: zohoInvoice.Status,
invoiceDate: zohoInvoice.Invoice_Date,
dueDate: zohoInvoice.Due_Date,
createdTime: zohoInvoice.Created_Time,
modifiedTime: zohoInvoice.Modified_Time,
owner: zohoInvoice.Owner?.name,
customFields: this.mapCustomFields(zohoInvoice)
};
}
// Map Zoho People Employee to standardized format // Map Zoho People Employee to standardized format
static mapEmployee(zohoEmployee) { static mapEmployee(zohoEmployee) {
return { return {
@ -120,6 +179,9 @@ class ZohoMapper {
'leads': this.mapLead, 'leads': this.mapLead,
'contacts': this.mapContact, 'contacts': this.mapContact,
'deals': this.mapDeal, 'deals': this.mapDeal,
'sales_orders': this.mapSalesOrder,
'purchase_orders': this.mapPurchaseOrder,
'invoices': this.mapInvoice,
'employees': this.mapEmployee, 'employees': this.mapEmployee,
'projects': this.mapProject, 'projects': this.mapProject,
'tasks': this.mapTask 'tasks': this.mapTask
@ -226,6 +288,18 @@ class ZohoMapper {
}; };
break; break;
case 'sales_orders':
case 'purchase_orders':
case 'invoices':
// CRM response structure for sales orders, purchase orders, and invoices
records = zohoResponse.data || [];
pageInfo = {
count: zohoResponse.info?.count || records.length,
moreRecords: zohoResponse.info?.more_records || false,
page: zohoResponse.info?.page || 1
};
break;
default: default:
// Default CRM response structure (leads, contacts, deals, employees) // Default CRM response structure (leads, contacts, deals, employees)
records = zohoResponse.data || []; records = zohoResponse.data || [];

View File

@ -134,6 +134,39 @@ class IntegrationService {
} }
return await client.getAllProjectPhases(portalId, params); return await client.getAllProjectPhases(portalId, params);
} }
async getSalesOrders(provider, params = {}) {
const client = this.clients[provider];
if (!client) {
throw new Error(`Provider ${provider} not supported`);
}
if (provider !== 'zoho') {
throw new Error('Sales Orders are only available for Zoho provider');
}
return await client.getSalesOrders(params);
}
async getPurchaseOrders(provider, params = {}) {
const client = this.clients[provider];
if (!client) {
throw new Error(`Provider ${provider} not supported`);
}
if (provider !== 'zoho') {
throw new Error('Purchase Orders are only available for Zoho provider');
}
return await client.getPurchaseOrders(params);
}
async getInvoices(provider, params = {}) {
const client = this.clients[provider];
if (!client) {
throw new Error(`Provider ${provider} not supported`);
}
if (provider !== 'zoho') {
throw new Error('Invoices are only available for Zoho provider');
}
return await client.getInvoices(params);
}
} }
module.exports = IntegrationService; module.exports = IntegrationService;