342 lines
10 KiB
JavaScript
342 lines
10 KiB
JavaScript
const axios = require('axios');
|
|
const ZohoBulkReadRepository = require('../data/repositories/zohoBulkReadRepository');
|
|
const userAuthTokenRepo = require('../data/repositories/userAuthTokenRepository');
|
|
const { decrypt } = require('../utils/crypto');
|
|
const logger = require('../utils/logger');
|
|
|
|
class BulkReadService {
|
|
constructor() {
|
|
this.baseUrl = 'https://www.zohoapis.com';
|
|
}
|
|
|
|
/**
|
|
* Initiate a bulk read job for a specific module
|
|
* @param {string} userId - User UUID
|
|
* @param {string} module - Zoho module name
|
|
* @param {Array} fields - Fields to fetch
|
|
* @param {Object} options - Additional options
|
|
* @param {string} accessToken - Frontend access token for callback URL
|
|
* @returns {Promise<Object>} Job details
|
|
*/
|
|
async initiateBulkRead(userId, module, fields, options = {}, accessToken = null) {
|
|
try {
|
|
console.log(`🚀 Initiating bulk read for user ${userId}, module ${module}`);
|
|
|
|
// Get access token
|
|
const tokenRecord = await userAuthTokenRepo.findByUserAndService(userId, 'zoho');
|
|
if (!tokenRecord) {
|
|
throw new Error('No Zoho access token found for user');
|
|
}
|
|
|
|
const zohoAccessToken = decrypt(tokenRecord.accessToken);
|
|
|
|
// Prepare bulk read request
|
|
const baseUrl = process.env.API_BASE_URL || process.env.MY_BASE_URL || 'http://localhost:3000';
|
|
let callbackUrl = `${baseUrl}/api/v1/integrations/webhooks/zoho/bulkread`;
|
|
|
|
// Add access token to callback URL if provided
|
|
if (accessToken) {
|
|
callbackUrl += `?access_token=${accessToken}`;
|
|
}
|
|
|
|
const bulkReadData = {
|
|
callback: {
|
|
url: callbackUrl,
|
|
method: 'post'
|
|
},
|
|
query: {
|
|
module: module,
|
|
fields: fields,
|
|
page: options.page || 1,
|
|
limit: options.limit || 200000
|
|
}
|
|
};
|
|
|
|
console.log('📋 Bulk read request:', JSON.stringify(bulkReadData, null, 2));
|
|
|
|
// Make API call to Zoho
|
|
const response = await axios.post(
|
|
`${this.baseUrl}/crm/bulk/v2/read`,
|
|
bulkReadData,
|
|
{
|
|
headers: {
|
|
'Authorization': `Zoho-oauthtoken ${zohoAccessToken}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
}
|
|
);
|
|
|
|
const jobData = response.data;
|
|
console.log('✅ Bulk read job response from Zoho:', JSON.stringify(jobData, null, 2));
|
|
|
|
// Extract job ID from nested response structure
|
|
const jobDetails = jobData.data && jobData.data[0] && jobData.data[0].details;
|
|
const jobId = jobDetails?.id;
|
|
|
|
if (!jobId) {
|
|
console.log('⚠️ No job ID found in response structure');
|
|
console.log('Response structure:', JSON.stringify(jobData, null, 2));
|
|
throw new Error('Invalid response structure from Zoho API - no job ID found');
|
|
}
|
|
|
|
console.log('📋 Job ID received from Zoho:', jobId);
|
|
console.log('📋 Job details:', jobDetails);
|
|
|
|
// Store job in database with real job ID
|
|
const jobRecord = await ZohoBulkReadRepository.createBulkReadJob({
|
|
id: jobId,
|
|
user_uuid: userId,
|
|
module: module,
|
|
operation: jobDetails.operation || 'read',
|
|
state: jobDetails.state || 'ADDED',
|
|
file_type: 'csv',
|
|
records_count: 0,
|
|
status: 'pending'
|
|
});
|
|
|
|
logger.info('Bulk read job created and stored', {
|
|
userId,
|
|
module,
|
|
jobId: jobId,
|
|
zohoState: jobDetails.state || 'ADDED'
|
|
});
|
|
|
|
return {
|
|
jobId: jobId,
|
|
status: 'created',
|
|
message: `Bulk read job created for ${module}. Processing will be handled asynchronously via webhook.`,
|
|
zohoState: jobDetails.state || 'ADDED',
|
|
operation: jobDetails.operation || 'read',
|
|
createdBy: jobDetails.created_by,
|
|
createdTime: jobDetails.created_time,
|
|
estimatedTime: this.getEstimatedTime(module, options.limit)
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error initiating bulk read:', error.message);
|
|
logger.error('Bulk read initiation failed', {
|
|
userId,
|
|
module,
|
|
error: error.message
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get bulk read job status
|
|
* @param {string} userId - User UUID
|
|
* @param {string} jobId - Job ID
|
|
* @returns {Promise<Object>} Job status
|
|
*/
|
|
async getBulkReadJobStatus(userId, jobId) {
|
|
try {
|
|
const job = await ZohoBulkReadRepository.getBulkReadJob(jobId);
|
|
|
|
if (!job) {
|
|
throw new Error('Job not found');
|
|
}
|
|
|
|
if (job.user_uuid !== userId) {
|
|
throw new Error('Unauthorized access to job');
|
|
}
|
|
|
|
return {
|
|
jobId: job.id,
|
|
module: job.module,
|
|
status: job.status,
|
|
state: job.state,
|
|
recordsCount: job.records_count,
|
|
processedCount: job.processed_count,
|
|
createdAt: job.created_at,
|
|
updatedAt: job.updated_at,
|
|
errorMessage: job.error_message
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error getting job status:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user's bulk read jobs
|
|
* @param {string} userId - User UUID
|
|
* @param {Object} options - Query options
|
|
* @returns {Promise<Array>} Job list
|
|
*/
|
|
async getUserBulkReadJobs(userId, options = {}) {
|
|
try {
|
|
const jobs = await ZohoBulkReadRepository.getUserBulkReadJobs(userId, options);
|
|
|
|
return jobs.map(job => ({
|
|
jobId: job.id,
|
|
module: job.module,
|
|
status: job.status,
|
|
state: job.state,
|
|
recordsCount: job.records_count,
|
|
processedCount: job.processed_count,
|
|
createdAt: job.created_at,
|
|
updatedAt: job.updated_at
|
|
}));
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error getting user jobs:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get bulk read data for a module
|
|
* @param {string} userId - User UUID
|
|
* @param {string} module - Module name
|
|
* @param {Object} options - Query options
|
|
* @returns {Promise<Object>} Data and pagination info
|
|
*/
|
|
async getBulkReadData(userId, module, options = {}) {
|
|
try {
|
|
const { page = 1, limit = 100, orderBy = 'created_time', orderDirection = 'DESC' } = options;
|
|
|
|
const data = await ZohoBulkReadRepository.getUserData(userId, module, {
|
|
limit: parseInt(limit),
|
|
offset: (parseInt(page) - 1) * parseInt(limit),
|
|
orderBy,
|
|
orderDirection
|
|
});
|
|
|
|
const totalCount = await ZohoBulkReadRepository.getUserDataCount(userId, module);
|
|
|
|
return {
|
|
data: data,
|
|
pagination: {
|
|
page: parseInt(page),
|
|
limit: parseInt(limit),
|
|
total: totalCount,
|
|
pages: Math.ceil(totalCount / parseInt(limit))
|
|
}
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error getting bulk read data:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get estimated processing time for a module
|
|
* @param {string} module - Module name
|
|
* @param {number} limit - Record limit
|
|
* @returns {string} Estimated time
|
|
*/
|
|
getEstimatedTime(module, limit = 200000) {
|
|
const baseTime = {
|
|
'contacts': 2,
|
|
'leads': 2,
|
|
'accounts': 1,
|
|
'tasks': 3,
|
|
'deals': 2,
|
|
'vendors': 1,
|
|
'invoices': 2,
|
|
'sales_orders': 2,
|
|
'purchase_orders': 2
|
|
};
|
|
|
|
const minutes = baseTime[module.toLowerCase()] || 2;
|
|
const adjustedMinutes = Math.ceil(minutes * (limit / 100000));
|
|
|
|
return `${adjustedMinutes} minutes`;
|
|
}
|
|
|
|
/**
|
|
* Get available modules for bulk read
|
|
* @returns {Array} Available modules
|
|
*/
|
|
getAvailableModules() {
|
|
return [
|
|
{
|
|
name: 'contacts',
|
|
displayName: 'Contacts',
|
|
description: 'Customer contact information',
|
|
fields: [
|
|
'id', 'First_Name', 'Last_Name', 'Email', 'Phone', 'Mobile',
|
|
'Lead_Source', 'Account_Name.Account_Name', 'Owner', 'Created_Time', 'Modified_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'leads',
|
|
displayName: 'Leads',
|
|
description: 'Sales lead information',
|
|
fields: [
|
|
'id', 'First_Name', 'Last_Name', 'Company', 'Lead_Source',
|
|
'Lead_Status', 'Owner', 'Email', 'Phone', 'Created_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'accounts',
|
|
displayName: 'Accounts',
|
|
description: 'Account information',
|
|
fields: [
|
|
'id', 'Account_Name', 'Phone', 'Website', 'Industry',
|
|
'Ownership', 'Annual_Revenue', 'Owner', 'Created_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'tasks',
|
|
displayName: 'Tasks',
|
|
description: 'Task information',
|
|
fields: [
|
|
'id', 'Subject', 'Owner', 'Status', 'Priority',
|
|
'Due_Date', 'What_Id', 'Created_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'deals',
|
|
displayName: 'Deals',
|
|
description: 'Sales deal information',
|
|
fields: [
|
|
'id', 'Deal_Name', 'Stage', 'Amount', 'Closing_Date',
|
|
'Account_Name', 'Contact_Name', 'Pipeline', 'Probability',
|
|
'Lead_Source', 'Owner', 'Created_Time', 'Modified_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'vendors',
|
|
displayName: 'Vendors',
|
|
description: 'Vendor information',
|
|
fields: [
|
|
'id', 'Vendor_Name', 'Email', 'Phone', 'Website', 'Owner', 'Created_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'invoices',
|
|
displayName: 'Invoices',
|
|
description: 'Invoice information',
|
|
fields: [
|
|
'id', 'Invoice_Number', 'Invoice_Date', 'Due_Date', 'Status',
|
|
'Grand_Total', 'Account_Name.Account_Name', 'Owner', 'Created_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'sales_orders',
|
|
displayName: 'Sales Orders',
|
|
description: 'Sales order information',
|
|
fields: [
|
|
'id', 'Subject', 'Status', 'Due_Date', 'Grand_Total',
|
|
'Account_Name.Account_Name', 'Owner', 'Created_Time'
|
|
]
|
|
},
|
|
{
|
|
name: 'purchase_orders',
|
|
displayName: 'Purchase Orders',
|
|
description: 'Purchase order information',
|
|
fields: [
|
|
'id', 'Subject', 'Vendor_Name.Vendor_Name', 'Status',
|
|
'Due_Date', 'Grand_Total', 'Owner', 'Created_Time'
|
|
]
|
|
}
|
|
];
|
|
}
|
|
}
|
|
|
|
module.exports = BulkReadService;
|