diff --git a/src/api/controllers/integrationController.js b/src/api/controllers/integrationController.js index 8af586c..c124136 100644 --- a/src/api/controllers/integrationController.js +++ b/src/api/controllers/integrationController.js @@ -676,12 +676,62 @@ async function getContacts(req, res) { } const contacts = await integrationService.getContacts(provider, params); + res.json(success('Zoho CRM Contacts retrieved successfully', contacts)); + } catch (error) { + res.status(400).json(failure(error.message, 'INTEGRATION_ERROR')); + } +} + +async function getBooksContacts(req, res) { + try { + const { provider, page, limit, filters } = req.query; + + if (provider !== 'zoho') { + return res.status(400).json(failure('Books Contacts are only available for Zoho provider', 'INVALID_PROVIDER')); + } + + const integrationService = new IntegrationService(req.user.uuid); + 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 contacts = await integrationService.getBooksContacts(provider, params); res.json(success('Zoho Books Contacts retrieved successfully', contacts)); } catch (error) { res.status(400).json(failure(error.message, 'INTEGRATION_ERROR')); } } +async function getBooksInvoices(req, res) { + try { + const { provider, page, limit, filters } = req.query; + + if (provider !== 'zoho') { + return res.status(400).json(failure('Books Invoices are only available for Zoho provider', 'INVALID_PROVIDER')); + } + + const integrationService = new IntegrationService(req.user.uuid); + 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.getBooksInvoices(provider, params); + res.json(success('Zoho Books Invoices retrieved successfully', invoices)); + } catch (error) { + res.status(400).json(failure(error.message, 'INTEGRATION_ERROR')); + } +} + // Zoho People Forms API controllers async function getEmployeeForms(req, res) { try { @@ -859,6 +909,6 @@ module.exports = { getPurchaseOrders, getInvoices, getDepartments, getLeaveRequests, getAttendance, getOrganizations, getCustomers, getVendors, getItems, getEstimates, getBills, getExpenses, getBankAccounts, getBankTransactions, getReports, getBooksSalesOrders, - getBooksPurchaseOrders, getContacts, getEmployeeForms, getEmployeeById, + getBooksPurchaseOrders, getContacts, getBooksContacts, getBooksInvoices, getEmployeeForms, getEmployeeById, getAttendanceEntries, getShiftConfiguration, getLeaveData, getGoalsData, getPerformanceData }; diff --git a/src/api/routes/integrationRoutes.js b/src/api/routes/integrationRoutes.js index 9f3e0a5..f3fe38a 100644 --- a/src/api/routes/integrationRoutes.js +++ b/src/api/routes/integrationRoutes.js @@ -6,7 +6,7 @@ const { getPurchaseOrders, getInvoices, getDepartments, getLeaveRequests, getAttendance, getOrganizations, getCustomers, getVendors, getItems, getEstimates, getBills, getExpenses, getBankAccounts, getBankTransactions, getReports, getBooksSalesOrders, - getBooksPurchaseOrders, getContacts, getEmployeeForms, getEmployeeById, + getBooksPurchaseOrders, getContacts, getBooksContacts, getBooksInvoices, getEmployeeForms, getEmployeeById, getAttendanceEntries, getShiftConfiguration, getLeaveData, getGoalsData, getPerformanceData } = require('../controllers/integrationController'); const auth = require('../middlewares/auth'); @@ -155,6 +155,7 @@ const invoicesSchema = Joi.object({ }); router.get('/zoho/crm/invoices', auth, validate(invoicesSchema), getInvoices); +router.get('/zoho/crm/contacts', auth, validate(invoicesSchema), getContacts); // Zoho People specific routes const departmentsSchema = Joi.object({ @@ -331,11 +332,11 @@ const booksPurchaseOrdersSchema = Joi.object({ // Zoho Books specific routes with clear service identification router.get('/zoho/books/organizations', auth, validate(organizationsSchema), getOrganizations); -router.get('/zoho/books/contacts', auth, validate(customersSchema), getContacts); +router.get('/zoho/books/contacts', auth, validate(customersSchema), getBooksContacts); router.get('/zoho/books/vendors', auth, validate(vendorsSchema), getVendors); router.get('/zoho/books/items', auth, validate(itemsSchema), getItems); router.get('/zoho/books/customers', auth, validate(customersSchema), getCustomers); -router.get('/zoho/books/invoices', auth, validate(invoicesSchema), getInvoices); +router.get('/zoho/books/invoices', auth, validate(invoicesSchema), getBooksInvoices); router.get('/zoho/books/estimates', auth, validate(estimatesSchema), getEstimates); router.get('/zoho/books/bills', auth, validate(billsSchema), getBills); router.get('/zoho/books/expenses', auth, validate(expensesSchema), getExpenses); diff --git a/src/integrations/zoho/client.js b/src/integrations/zoho/client.js index e44920b..dca7b78 100644 --- a/src/integrations/zoho/client.js +++ b/src/integrations/zoho/client.js @@ -203,7 +203,7 @@ class ZohoClient { const response = await this.makeRequest('/books/v3/contacts', { params :{ ...params, contact_type: 'customer' }}, 'books'); return ZohoMapper.mapApiResponse(response, 'contacts'); } - async getContacts(params = {}) { + async getBooksContacts(params = {}) { const response = await this.makeRequest('/books/v3/contacts', { params}, 'books'); return ZohoMapper.mapApiResponse(response, 'contacts'); } @@ -218,7 +218,7 @@ class ZohoClient { return ZohoMapper.mapApiResponse(response, 'items'); } - async getInvoices(params = {}) { + async getBooksInvoices(params = {}) { const response = await this.makeRequest('/books/v3/invoices', { params }, 'books'); return ZohoMapper.mapApiResponse(response, 'invoices'); } diff --git a/src/integrations/zoho/mapper.js b/src/integrations/zoho/mapper.js index 7c67829..f219ab9 100644 --- a/src/integrations/zoho/mapper.js +++ b/src/integrations/zoho/mapper.js @@ -641,12 +641,22 @@ class ZohoMapper { case 'contacts': // Books response structure for contacts + if (zohoResponse.data && zohoResponse.info) { + // Books response structure + records = zohoResponse.data || []; + pageInfo = { + count: zohoResponse.info?.count || records.length, + moreRecords: zohoResponse.info?.more_records || false, + page: zohoResponse.info?.page || 1 + }; + } else { records = zohoResponse.contacts || []; pageInfo = { count: zohoResponse.page_context?.count || records.length, moreRecords: zohoResponse.page_context?.more_records || false, page: zohoResponse.page_context?.page || 1 }; + } break; case 'vendors': diff --git a/src/services/integration/integrationService.js b/src/services/integration/integrationService.js index be7972f..046faa3 100644 --- a/src/services/integration/integrationService.js +++ b/src/services/integration/integrationService.js @@ -366,6 +366,28 @@ class IntegrationService { return await client.getContacts(params); } + async getBooksContacts(provider, params = {}) { + const client = this.clients[provider]; + if (!client) { + throw new Error(`Provider ${provider} not supported`); + } + if (provider !== 'zoho') { + throw new Error('Books Contacts are only available for Zoho provider'); + } + return await client.getBooksContacts(params); + } + + async getBooksInvoices(provider, params = {}) { + const client = this.clients[provider]; + if (!client) { + throw new Error(`Provider ${provider} not supported`); + } + if (provider !== 'zoho') { + throw new Error('Books Invoices are only available for Zoho provider'); + } + return await client.getBooksInvoices(params); + } + // Zoho People Forms API methods async getEmployeeForms(provider, params = {}) { const client = this.clients[provider];