bulk report api restructured with more modules

This commit is contained in:
yashwin-foxy 2025-09-25 19:02:08 +05:30
parent 6c0902cd57
commit ec68e5ca39
5 changed files with 559 additions and 38 deletions

View File

@ -962,7 +962,7 @@ const scheduleBulkReadJobs = async (req, res) => {
const accessToken = req.headers.authorization?.replace('Bearer ', '') || null; const accessToken = req.headers.authorization?.replace('Bearer ', '') || null;
console.log(`🚀 Scheduling bulk read jobs for user: ${userId}`); console.log(`🚀 Scheduling bulk read jobs for user: ${userId}`);
// Define the 5 modules with their specific fields // Define the 8 modules with their specific fields
const modules = [ const modules = [
{ {
name: 'Accounts', name: 'Accounts',
@ -999,6 +999,27 @@ const scheduleBulkReadJobs = async (req, res) => {
'id', 'First_Name', 'Last_Name', 'Company', 'Lead_Source', 'id', 'First_Name', 'Last_Name', 'Company', 'Lead_Source',
'Lead_Status', 'Owner', 'Email', 'Phone', 'Created_Time' 'Lead_Status', 'Owner', 'Email', 'Phone', 'Created_Time'
] ]
},
{
name: 'Invoices',
fields: [
'id', 'Invoice_Number', 'Invoice_Date', 'Due_Date', 'Status',
'Grand_Total', 'Account_Name.Account_Name', 'Owner', 'Created_Time'
]
},
{
name: 'Purchase_Orders',
fields: [
'id', 'Subject', 'Vendor_Name.Vendor_Name', 'Status',
'Due_Date', 'Grand_Total', 'Owner', 'Created_Time'
]
},
{
name: 'Sales_Orders',
fields: [
'id', 'Subject', 'Status', 'Due_Date', 'Grand_Total',
'Account_Name.Account_Name', 'Owner', 'Created_Time'
]
} }
]; ];
@ -1049,7 +1070,11 @@ const scheduleBulkReadJobs = async (req, res) => {
successful: results.length, successful: results.length,
failed: errors.length failed: errors.length
}, },
note: 'Jobs are now being processed by Zoho. You will receive webhook notifications when each job completes.' note: 'Jobs are now being processed by Zoho. You will receive webhook notifications when each job completes.',
modules: [
'Accounts', 'Deals', 'Contacts', 'Tasks', 'Leads',
'Invoices', 'Purchase_Orders', 'Sales_Orders'
]
}; };
if (errors.length > 0) { if (errors.length > 0) {

View File

@ -1,11 +1,18 @@
const { Op } = require('sequelize'); const { Op } = require('sequelize');
const ZohoLeadsBulk = require('../../data/models/zohoLeadsBulk'); const ZohoLeadsBulk = require('../../data/models/zohoLeadsBulk');
const ZohoTasksBulk = require('../../data/models/zohoTasksBulk'); const ZohoTasksBulk = require('../../data/models/zohoTasksBulk');
const ZohoContactsBulk = require('../../data/models/zohoContactsBulk');
const ZohoAccountsBulk = require('../../data/models/zohoAccountsBulk');
const ZohoDealsBulk = require('../../data/models/zohoDealsBulk');
const ZohoVendorsBulk = require('../../data/models/zohoVendorsBulk');
const ZohoInvoicesBulk = require('../../data/models/zohoInvoicesBulk');
const ZohoSalesOrdersBulk = require('../../data/models/zohoSalesOrdersBulk');
const ZohoPurchaseOrdersBulk = require('../../data/models/zohoPurchaseOrdersBulk');
const { success, failure } = require('../../utils/response'); const { success, failure } = require('../../utils/response');
class ReportsController { class ReportsController {
/** /**
* Get comprehensive CRM KPIs combining leads and tasks data * Get comprehensive CRM KPIs combining all CRM modules data
*/ */
async getCrmKPIs(req, res) { async getCrmKPIs(req, res) {
try { try {
@ -26,35 +33,70 @@ class ReportsController {
...ownerFilter ...ownerFilter
}; };
// Fetch data in parallel // Fetch data from all CRM modules in parallel
const [leadsData, tasksData] = await Promise.all([ const [
leadsData,
tasksData,
contactsData,
accountsData,
dealsData,
vendorsData,
invoicesData,
salesOrdersData,
purchaseOrdersData
] = await Promise.all([
ZohoLeadsBulk.findAll({ ZohoLeadsBulk.findAll({
where: baseFilters, where: baseFilters,
attributes: [ attributes: ['lead_source', 'lead_status', 'owner', 'created_time', 'company', 'email', 'phone']
'lead_source',
'lead_status',
'owner',
'created_time',
'company'
]
}), }),
ZohoTasksBulk.findAll({ ZohoTasksBulk.findAll({
where: baseFilters, where: baseFilters,
attributes: [ attributes: ['status', 'priority', 'owner', 'created_time', 'due_date', 'subject', 'what_id']
'status', }),
'priority', ZohoContactsBulk.findAll({
'owner', where: baseFilters,
'created_time', attributes: ['first_name', 'last_name', 'email', 'phone', 'lead_source', 'account_name', 'owner', 'created_time']
'due_date', }),
'subject' ZohoAccountsBulk.findAll({
] where: baseFilters,
attributes: ['account_name', 'phone', 'website', 'industry', 'ownership', 'annual_revenue', 'owner', 'created_time']
}),
ZohoDealsBulk.findAll({
where: baseFilters,
attributes: ['deal_name', 'stage', 'amount', 'closing_date', 'account_name', 'contact_name', 'probability', 'lead_source', 'owner', 'created_time']
}),
ZohoVendorsBulk.findAll({
where: baseFilters,
attributes: ['vendor_name', 'email', 'phone', 'website', 'owner', 'created_time']
}),
ZohoInvoicesBulk.findAll({
where: baseFilters,
attributes: ['invoice_number', 'invoice_date', 'due_date', 'status', 'total', 'account_name', 'owner', 'created_time']
}),
ZohoSalesOrdersBulk.findAll({
where: baseFilters,
attributes: ['subject', 'status', 'due_date', 'total', 'account_name', 'owner', 'created_time']
}),
ZohoPurchaseOrdersBulk.findAll({
where: baseFilters,
attributes: ['subject', 'vendor_name', 'status', 'due_date', 'total', 'owner', 'created_time']
}) })
]); ]);
// Calculate KPIs // Calculate comprehensive KPIs
const kpis = this.calculateCrmKPIs(leadsData, tasksData); const kpis = this.calculateComprehensiveCrmKPIs({
leads: leadsData,
tasks: tasksData,
contacts: contactsData,
accounts: accountsData,
deals: dealsData,
vendors: vendorsData,
invoices: invoicesData,
salesOrders: salesOrdersData,
purchaseOrders: purchaseOrdersData
});
return res.json(success('CRM KPIs retrieved successfully', kpis)); return res.json(success('Comprehensive CRM KPIs retrieved successfully', kpis));
} catch (error) { } catch (error) {
console.error('Error fetching CRM KPIs:', error); console.error('Error fetching CRM KPIs:', error);
return res.status(500).json(failure('Failed to fetch CRM KPIs', 'INTERNAL_ERROR')); return res.status(500).json(failure('Failed to fetch CRM KPIs', 'INTERNAL_ERROR'));
@ -62,7 +104,91 @@ class ReportsController {
} }
/** /**
* Calculate CEO-level CRM KPIs focused on business statistics * Calculate comprehensive CRM KPIs from all modules
*/
calculateComprehensiveCrmKPIs(data) {
const now = new Date();
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
// Module counts
const moduleCounts = {
leads: data.leads.length,
contacts: data.contacts.length,
accounts: data.accounts.length,
deals: data.deals.length,
tasks: data.tasks.length,
vendors: data.vendors.length,
invoices: data.invoices.length,
salesOrders: data.salesOrders.length,
purchaseOrders: data.purchaseOrders.length
};
// Revenue and financial metrics
const revenueMetrics = this.calculateRevenueMetrics(data);
// Lead and sales pipeline metrics
const pipelineMetrics = this.calculatePipelineMetrics(data, thirtyDaysAgo, sevenDaysAgo);
// Operational efficiency metrics
const operationalMetrics = this.calculateOperationalMetrics(data, now);
// Customer and vendor metrics
const customerMetrics = this.calculateCustomerMetrics(data, thirtyDaysAgo);
// Business growth trends
const growthMetrics = this.calculateGrowthMetrics(data, thirtyDaysAgo, ninetyDaysAgo);
// Top performers and insights
const insights = this.calculateBusinessInsights(data, revenueMetrics, pipelineMetrics);
return {
businessOverview: {
totalRecords: Object.values(moduleCounts).reduce((sum, count) => sum + count, 0),
moduleCounts,
revenueMetrics,
growthMetrics
},
salesPipeline: {
...pipelineMetrics,
dealStages: this.getDistribution(data.deals, 'stage'),
leadSources: this.getDistribution(data.leads, 'lead_source'),
conversionFunnel: this.calculateConversionFunnel(data)
},
operationalEfficiency: {
...operationalMetrics,
taskStatus: this.getDistribution(data.tasks, 'status'),
invoiceStatus: this.getDistribution(data.invoices, 'status'),
orderStatus: {
salesOrders: this.getDistribution(data.salesOrders, 'status'),
purchaseOrders: this.getDistribution(data.purchaseOrders, 'status')
}
},
customerRelationships: {
...customerMetrics,
industryDistribution: this.getDistribution(data.accounts, 'industry'),
contactSources: this.getDistribution(data.contacts, 'lead_source'),
accountOwnership: this.getDistribution(data.accounts, 'owner')
},
financialHealth: {
totalRevenue: revenueMetrics.totalRevenue,
totalInvoices: revenueMetrics.totalInvoices,
averageInvoiceValue: revenueMetrics.averageInvoiceValue,
overdueAmount: revenueMetrics.overdueAmount,
revenueByMonth: revenueMetrics.revenueByMonth,
topRevenueAccounts: revenueMetrics.topRevenueAccounts
},
businessInsights: {
...insights,
recommendations: this.generateRecommendations(data, revenueMetrics, pipelineMetrics, operationalMetrics)
},
generatedAt: new Date().toISOString()
};
}
/**
* Calculate CEO-level CRM KPIs focused on business statistics (Legacy method)
*/ */
calculateCrmKPIs(leadsData, tasksData) { calculateCrmKPIs(leadsData, tasksData) {
const now = new Date(); const now = new Date();
@ -283,6 +409,366 @@ class ReportsController {
}; };
} }
/**
* Calculate revenue metrics from invoices and deals
*/
calculateRevenueMetrics(data) {
const totalRevenue = data.invoices.reduce((sum, invoice) =>
sum + (parseFloat(invoice.total) || 0), 0);
const totalDealValue = data.deals.reduce((sum, deal) =>
sum + (parseFloat(deal.amount) || 0), 0);
const overdueInvoices = data.invoices.filter(invoice => {
if (!invoice.due_date) return false;
return new Date(invoice.due_date) < new Date() &&
invoice.status && !invoice.status.toLowerCase().includes('paid');
});
const overdueAmount = overdueInvoices.reduce((sum, invoice) =>
sum + (parseFloat(invoice.total) || 0), 0);
const averageInvoiceValue = data.invoices.length > 0 ?
totalRevenue / data.invoices.length : 0;
// Revenue by month (last 12 months)
const revenueByMonth = this.calculateRevenueByMonth(data.invoices);
// Top revenue accounts
const accountRevenue = {};
data.invoices.forEach(invoice => {
const account = invoice.account_name || 'Unknown';
const amount = parseFloat(invoice.total) || 0;
accountRevenue[account] = (accountRevenue[account] || 0) + amount;
});
const topRevenueAccounts = Object.entries(accountRevenue)
.map(([account, revenue]) => ({ account, revenue }))
.sort((a, b) => b.revenue - a.revenue)
.slice(0, 10);
return {
totalRevenue,
totalDealValue,
totalInvoices: data.invoices.length,
averageInvoiceValue,
overdueAmount,
overdueCount: overdueInvoices.length,
revenueByMonth,
topRevenueAccounts
};
}
/**
* Calculate pipeline metrics from leads and deals
*/
calculatePipelineMetrics(data, thirtyDaysAgo, sevenDaysAgo) {
const totalLeads = data.leads.length;
const totalDeals = data.deals.length;
const qualifiedLeads = data.leads.filter(lead =>
lead.lead_status && (
lead.lead_status.toLowerCase().includes('qualified') ||
lead.lead_status.toLowerCase().includes('hot') ||
lead.lead_status.toLowerCase().includes('warm')
)
).length;
const convertedLeads = data.leads.filter(lead =>
lead.lead_status && lead.lead_status.toLowerCase().includes('converted')
).length;
const openDeals = data.deals.filter(deal =>
deal.stage && !deal.stage.toLowerCase().includes('closed')
);
const closedWonDeals = data.deals.filter(deal =>
deal.stage && deal.stage.toLowerCase().includes('closed won')
);
const totalPipelineValue = openDeals.reduce((sum, deal) =>
sum + (parseFloat(deal.amount) || 0), 0);
const conversionRate = totalLeads > 0 ? (convertedLeads / totalLeads * 100).toFixed(2) : 0;
const qualificationRate = totalLeads > 0 ? (qualifiedLeads / totalLeads * 100).toFixed(2) : 0;
const winRate = totalDeals > 0 ? (closedWonDeals.length / totalDeals * 100).toFixed(2) : 0;
return {
totalLeads,
totalDeals,
qualifiedLeads,
convertedLeads,
openDeals: openDeals.length,
closedWonDeals: closedWonDeals.length,
totalPipelineValue,
conversionRate: `${conversionRate}%`,
qualificationRate: `${qualificationRate}%`,
winRate: `${winRate}%`
};
}
/**
* Calculate operational efficiency metrics
*/
calculateOperationalMetrics(data, now) {
const totalTasks = data.tasks.length;
const completedTasks = data.tasks.filter(task =>
task.status && task.status.toLowerCase().includes('completed')
).length;
const overdueTasks = data.tasks.filter(task =>
task.due_date && new Date(task.due_date) < now &&
task.status && !task.status.toLowerCase().includes('completed')
).length;
const highPriorityTasks = data.tasks.filter(task =>
task.priority && task.priority.toLowerCase().includes('high')
).length;
const taskCompletionRate = totalTasks > 0 ? (completedTasks / totalTasks * 100).toFixed(2) : 0;
const overdueRate = totalTasks > 0 ? (overdueTasks / totalTasks * 100).toFixed(2) : 0;
return {
totalTasks,
completedTasks,
overdueTasks,
highPriorityTasks,
taskCompletionRate: `${taskCompletionRate}%`,
overdueRate: `${overdueRate}%`
};
}
/**
* Calculate customer relationship metrics
*/
calculateCustomerMetrics(data, thirtyDaysAgo) {
const totalContacts = data.contacts.length;
const totalAccounts = data.accounts.length;
const totalVendors = data.vendors.length;
const recentContacts = data.contacts.filter(contact =>
contact.created_time && new Date(contact.created_time) >= thirtyDaysAgo
).length;
const accountsWithRevenue = new Set(data.invoices.map(invoice => invoice.account_name)).size;
const contactToAccountRatio = totalAccounts > 0 ? (totalContacts / totalAccounts).toFixed(2) : 0;
return {
totalContacts,
totalAccounts,
totalVendors,
recentContacts,
accountsWithRevenue,
contactToAccountRatio
};
}
/**
* Calculate growth metrics
*/
calculateGrowthMetrics(data, thirtyDaysAgo, ninetyDaysAgo) {
const recentLeads = data.leads.filter(lead =>
lead.created_time && new Date(lead.created_time) >= thirtyDaysAgo
).length;
const previousLeads = data.leads.filter(lead =>
lead.created_time &&
new Date(lead.created_time) >= ninetyDaysAgo &&
new Date(lead.created_time) < thirtyDaysAgo
).length;
const recentDeals = data.deals.filter(deal =>
deal.created_time && new Date(deal.created_time) >= thirtyDaysAgo
).length;
const previousDeals = data.deals.filter(deal =>
deal.created_time &&
new Date(deal.created_time) >= ninetyDaysAgo &&
new Date(deal.created_time) < thirtyDaysAgo
).length;
const leadGrowthRate = previousLeads > 0 ?
((recentLeads - previousLeads) / previousLeads * 100).toFixed(2) : 0;
const dealGrowthRate = previousDeals > 0 ?
((recentDeals - previousDeals) / previousDeals * 100).toFixed(2) : 0;
return {
recentLeads,
previousLeads,
recentDeals,
previousDeals,
leadGrowthRate: `${leadGrowthRate}%`,
dealGrowthRate: `${dealGrowthRate}%`
};
}
/**
* Calculate business insights
*/
calculateBusinessInsights(data, revenueMetrics, pipelineMetrics) {
const now = new Date();
const leadSourcePerformance = this.getLeadSourcePerformance(data.leads);
const topPerformingSources = Object.entries(leadSourcePerformance)
.map(([source, data]) => ({
source: source || 'Unknown',
totalLeads: data.total,
qualifiedLeads: data.qualified,
conversionRate: data.total > 0 ? (data.converted / data.total * 100).toFixed(2) : 0,
avgQuality: data.total > 0 ? (data.qualified / data.total * 100).toFixed(2) : 0
}))
.sort((a, b) => b.totalLeads - a.totalLeads)
.slice(0, 5);
const topRevenueOwners = this.getOwnerPerformance(data.invoices, 'total')
.slice(0, 5);
const mostActiveOwners = this.getOwnerPerformance(data.tasks, null, 'count')
.slice(0, 5);
return {
topPerformingSources,
topRevenueOwners,
mostActiveOwners,
businessHealth: this.calculateBusinessHealth(data.leads, data.tasks, now)
};
}
/**
* Calculate conversion funnel
*/
calculateConversionFunnel(data) {
const totalLeads = data.leads.length;
const qualifiedLeads = data.leads.filter(lead =>
lead.lead_status && (
lead.lead_status.toLowerCase().includes('qualified') ||
lead.lead_status.toLowerCase().includes('hot') ||
lead.lead_status.toLowerCase().includes('warm')
)
).length;
const convertedLeads = data.leads.filter(lead =>
lead.lead_status && lead.lead_status.toLowerCase().includes('converted')
).length;
const totalDeals = data.deals.length;
const closedWonDeals = data.deals.filter(deal =>
deal.stage && deal.stage.toLowerCase().includes('closed won')
).length;
return {
leads: { count: totalLeads, percentage: 100 },
qualified: { count: qualifiedLeads, percentage: totalLeads > 0 ? (qualifiedLeads / totalLeads * 100).toFixed(2) : 0 },
converted: { count: convertedLeads, percentage: totalLeads > 0 ? (convertedLeads / totalLeads * 100).toFixed(2) : 0 },
deals: { count: totalDeals, percentage: convertedLeads > 0 ? (totalDeals / convertedLeads * 100).toFixed(2) : 0 },
closedWon: { count: closedWonDeals, percentage: totalDeals > 0 ? (closedWonDeals / totalDeals * 100).toFixed(2) : 0 }
};
}
/**
* Calculate revenue by month
*/
calculateRevenueByMonth(invoices) {
const monthlyRevenue = {};
const now = new Date();
// Initialize last 12 months
for (let i = 11; i >= 0; i--) {
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
monthlyRevenue[monthKey] = 0;
}
invoices.forEach(invoice => {
if (invoice.created_time) {
const date = new Date(invoice.created_time);
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
if (monthlyRevenue.hasOwnProperty(monthKey)) {
monthlyRevenue[monthKey] += parseFloat(invoice.total) || 0;
}
}
});
return Object.entries(monthlyRevenue).map(([month, revenue]) => ({ month, revenue }));
}
/**
* Get owner performance metrics
*/
getOwnerPerformance(data, valueField = null, metric = 'revenue') {
const ownerStats = {};
data.forEach(item => {
const owner = item.owner || 'Unassigned';
if (!ownerStats[owner]) {
ownerStats[owner] = { count: 0, total: 0 };
}
ownerStats[owner].count++;
if (valueField && item[valueField]) {
ownerStats[owner].total += parseFloat(item[valueField]) || 0;
}
});
return Object.entries(ownerStats)
.map(([owner, stats]) => ({
owner,
count: stats.count,
[metric === 'revenue' ? 'total' : 'value']: stats.total
}))
.sort((a, b) => (metric === 'revenue' ? b.total - a.total : b.count - a.count));
}
/**
* Generate business recommendations
*/
generateRecommendations(data, revenueMetrics, pipelineMetrics, operationalMetrics) {
const recommendations = [];
// Revenue recommendations
if (revenueMetrics.overdueAmount > revenueMetrics.totalRevenue * 0.2) {
recommendations.push({
category: 'Financial',
priority: 'High',
title: 'Improve Collections',
description: `${revenueMetrics.overdueCount} overdue invoices worth $${revenueMetrics.overdueAmount.toFixed(2)} need attention.`,
action: 'Review and follow up on overdue invoices immediately.'
});
}
// Pipeline recommendations
if (parseFloat(pipelineMetrics.conversionRate) < 10) {
recommendations.push({
category: 'Sales',
priority: 'High',
title: 'Improve Lead Conversion',
description: `Current conversion rate is ${pipelineMetrics.conversionRate}. Industry average is 15-20%.`,
action: 'Review lead qualification process and follow-up procedures.'
});
}
// Operational recommendations
if (parseFloat(operationalMetrics.overdueRate) > 20) {
recommendations.push({
category: 'Operations',
priority: 'Medium',
title: 'Reduce Task Overdue Rate',
description: `${operationalMetrics.overdueRate} of tasks are overdue.`,
action: 'Implement better task prioritization and deadline management.'
});
}
// Growth recommendations
if (data.leads.length < 50) {
recommendations.push({
category: 'Growth',
priority: 'Medium',
title: 'Increase Lead Generation',
description: `Only ${data.leads.length} leads in the system. Consider expanding marketing efforts.`,
action: 'Invest in lead generation campaigns and improve website conversion.'
});
}
return recommendations;
}
/** /**
* Build date filter based on parameters * Build date filter based on parameters
*/ */

View File

@ -49,7 +49,6 @@ class ZohoBulkReadRepository {
console.log(`💾 Bulk inserting ${data.length} records for ${module}`); console.log(`💾 Bulk inserting ${data.length} records for ${module}`);
const result = await model.bulkCreate(data, { const result = await model.bulkCreate(data, {
ignoreDuplicates: true,
validate: true validate: true
}); });
@ -65,19 +64,19 @@ class ZohoBulkReadRepository {
* Clear existing data for a user and module * Clear existing data for a user and module
* @param {string} userId - User UUID * @param {string} userId - User UUID
* @param {string} module - Module name * @param {string} module - Module name
* @param {string} jobId - Bulk job ID * @param {string} jobId - Bulk job ID (optional, used for logging)
* @returns {Promise<number>} Number of deleted records * @returns {Promise<number>} Number of deleted records
*/ */
async clearUserData(userId, module, jobId) { async clearUserData(userId, module, jobId = null) {
try { try {
const model = this.getModel(module); const model = this.getModel(module);
console.log(`🗑️ Clearing existing data for user ${userId}, module ${module}, job ${jobId}`); console.log(`🗑️ Clearing ALL existing data for user ${userId}, module ${module}${jobId ? ` (new job: ${jobId})` : ''}`);
const result = await model.destroy({ const result = await model.destroy({
where: { where: {
user_uuid: userId, user_uuid: userId,
provider: 'zoho', provider: 'zoho'
bulk_job_id: jobId // Removed bulk_job_id filter to clear ALL data for the user and module
} }
}); });

View File

@ -234,6 +234,7 @@ class BulkReadService {
'leads': 2, 'leads': 2,
'accounts': 1, 'accounts': 1,
'tasks': 3, 'tasks': 3,
'deals': 2,
'vendors': 1, 'vendors': 1,
'invoices': 2, 'invoices': 2,
'sales_orders': 2, 'sales_orders': 2,
@ -288,6 +289,16 @@ class BulkReadService {
'Due_Date', 'What_Id', 'Created_Time' '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', name: 'vendors',
displayName: 'Vendors', displayName: 'Vendors',
@ -302,7 +313,7 @@ class BulkReadService {
description: 'Invoice information', description: 'Invoice information',
fields: [ fields: [
'id', 'Invoice_Number', 'Invoice_Date', 'Due_Date', 'Status', 'id', 'Invoice_Number', 'Invoice_Date', 'Due_Date', 'Status',
'Total', 'Balance', 'Account_Name.Account_Name', 'Owner', 'Created_Time' 'Grand_Total', 'Account_Name.Account_Name', 'Owner', 'Created_Time'
] ]
}, },
{ {
@ -310,8 +321,8 @@ class BulkReadService {
displayName: 'Sales Orders', displayName: 'Sales Orders',
description: 'Sales order information', description: 'Sales order information',
fields: [ fields: [
'id', 'Sales_Order_Number', 'Subject', 'Status', 'Due_Date', 'id', 'Subject', 'Status', 'Due_Date', 'Grand_Total',
'Total', 'Account_Name.Account_Name', 'Owner', 'Created_Time' 'Account_Name.Account_Name', 'Owner', 'Created_Time'
] ]
}, },
{ {
@ -319,8 +330,8 @@ class BulkReadService {
displayName: 'Purchase Orders', displayName: 'Purchase Orders',
description: 'Purchase order information', description: 'Purchase order information',
fields: [ fields: [
'id', 'Purchase_Order_Number', 'Subject', 'Vendor_Name.Vendor_Name', 'id', 'Subject', 'Vendor_Name.Vendor_Name', 'Status',
'Status', 'Due_Date', 'Total', 'Owner', 'Created_Time' 'Due_Date', 'Grand_Total', 'Owner', 'Created_Time'
] ]
} }
]; ];

View File

@ -261,7 +261,7 @@ class CsvService {
invoice_date: this.parseDate(record.Invoice_Date), invoice_date: this.parseDate(record.Invoice_Date),
due_date: this.parseDate(record.Due_Date), due_date: this.parseDate(record.Due_Date),
status: record.Status, status: record.Status,
total: this.parseDecimal(record.Total), total: this.parseDecimal(record.Grand_Total),
balance: this.parseDecimal(record.Balance), balance: this.parseDecimal(record.Balance),
account_name: record['Account_Name.Account_Name'] || record.Account_Name, account_name: record['Account_Name.Account_Name'] || record.Account_Name,
owner: record.Owner, owner: record.Owner,
@ -277,7 +277,7 @@ class CsvService {
subject: record.Subject, subject: record.Subject,
status: record.Status, status: record.Status,
due_date: this.parseDate(record.Due_Date), due_date: this.parseDate(record.Due_Date),
total: this.parseDecimal(record.Total), total: this.parseDecimal(record.Grand_Total),
account_name: record['Account_Name.Account_Name'] || record.Account_Name, account_name: record['Account_Name.Account_Name'] || record.Account_Name,
owner: record.Owner, owner: record.Owner,
created_time: this.parseDate(record.Created_Time) created_time: this.parseDate(record.Created_Time)
@ -293,7 +293,7 @@ class CsvService {
vendor_name: record['Vendor_Name.Vendor_Name'] || record.Vendor_Name, vendor_name: record['Vendor_Name.Vendor_Name'] || record.Vendor_Name,
status: record.Status, status: record.Status,
due_date: this.parseDate(record.Due_Date), due_date: this.parseDate(record.Due_Date),
total: this.parseDecimal(record.Total), total: this.parseDecimal(record.Grand_Total),
owner: record.Owner, owner: record.Owner,
created_time: this.parseDate(record.Created_Time) created_time: this.parseDate(record.Created_Time)
}; };