diff --git a/src/api/controllers/integrationController.js b/src/api/controllers/integrationController.js index 50557ba..8233040 100644 --- a/src/api/controllers/integrationController.js +++ b/src/api/controllers/integrationController.js @@ -962,7 +962,7 @@ const scheduleBulkReadJobs = async (req, res) => { const accessToken = req.headers.authorization?.replace('Bearer ', '') || null; 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 = [ { name: 'Accounts', @@ -999,6 +999,27 @@ const scheduleBulkReadJobs = async (req, res) => { 'id', 'First_Name', 'Last_Name', 'Company', 'Lead_Source', '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, 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) { diff --git a/src/api/controllers/reportsController.js b/src/api/controllers/reportsController.js index b079d29..82755b8 100644 --- a/src/api/controllers/reportsController.js +++ b/src/api/controllers/reportsController.js @@ -1,11 +1,18 @@ const { Op } = require('sequelize'); const ZohoLeadsBulk = require('../../data/models/zohoLeadsBulk'); 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'); class ReportsController { /** - * Get comprehensive CRM KPIs combining leads and tasks data + * Get comprehensive CRM KPIs combining all CRM modules data */ async getCrmKPIs(req, res) { try { @@ -26,35 +33,70 @@ class ReportsController { ...ownerFilter }; - // Fetch data in parallel - const [leadsData, tasksData] = await Promise.all([ + // Fetch data from all CRM modules in parallel + const [ + leadsData, + tasksData, + contactsData, + accountsData, + dealsData, + vendorsData, + invoicesData, + salesOrdersData, + purchaseOrdersData + ] = await Promise.all([ ZohoLeadsBulk.findAll({ where: baseFilters, - attributes: [ - 'lead_source', - 'lead_status', - 'owner', - 'created_time', - 'company' - ] + attributes: ['lead_source', 'lead_status', 'owner', 'created_time', 'company', 'email', 'phone'] }), ZohoTasksBulk.findAll({ where: baseFilters, - attributes: [ - 'status', - 'priority', - 'owner', - 'created_time', - 'due_date', - 'subject' - ] + attributes: ['status', 'priority', 'owner', 'created_time', 'due_date', 'subject', 'what_id'] + }), + ZohoContactsBulk.findAll({ + where: baseFilters, + attributes: ['first_name', 'last_name', 'email', 'phone', 'lead_source', 'account_name', 'owner', 'created_time'] + }), + 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 - const kpis = this.calculateCrmKPIs(leadsData, tasksData); + // Calculate comprehensive KPIs + 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) { console.error('Error fetching CRM KPIs:', 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) { 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 */ diff --git a/src/data/repositories/zohoBulkReadRepository.js b/src/data/repositories/zohoBulkReadRepository.js index b115f0f..bb807e4 100644 --- a/src/data/repositories/zohoBulkReadRepository.js +++ b/src/data/repositories/zohoBulkReadRepository.js @@ -49,7 +49,6 @@ class ZohoBulkReadRepository { console.log(`💾 Bulk inserting ${data.length} records for ${module}`); const result = await model.bulkCreate(data, { - ignoreDuplicates: true, validate: true }); @@ -65,19 +64,19 @@ class ZohoBulkReadRepository { * Clear existing data for a user and module * @param {string} userId - User UUID * @param {string} module - Module name - * @param {string} jobId - Bulk job ID + * @param {string} jobId - Bulk job ID (optional, used for logging) * @returns {Promise} Number of deleted records */ - async clearUserData(userId, module, jobId) { + async clearUserData(userId, module, jobId = null) { try { 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({ where: { user_uuid: userId, - provider: 'zoho', - bulk_job_id: jobId + provider: 'zoho' + // Removed bulk_job_id filter to clear ALL data for the user and module } }); diff --git a/src/services/bulkReadService.js b/src/services/bulkReadService.js index add3e95..1e32dfd 100644 --- a/src/services/bulkReadService.js +++ b/src/services/bulkReadService.js @@ -234,6 +234,7 @@ class BulkReadService { 'leads': 2, 'accounts': 1, 'tasks': 3, + 'deals': 2, 'vendors': 1, 'invoices': 2, 'sales_orders': 2, @@ -288,6 +289,16 @@ class BulkReadService { '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', @@ -302,7 +313,7 @@ class BulkReadService { description: 'Invoice information', fields: [ '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', description: 'Sales order information', fields: [ - 'id', 'Sales_Order_Number', 'Subject', 'Status', 'Due_Date', - 'Total', 'Account_Name.Account_Name', 'Owner', 'Created_Time' + 'id', 'Subject', 'Status', 'Due_Date', 'Grand_Total', + 'Account_Name.Account_Name', 'Owner', 'Created_Time' ] }, { @@ -319,8 +330,8 @@ class BulkReadService { displayName: 'Purchase Orders', description: 'Purchase order information', fields: [ - 'id', 'Purchase_Order_Number', 'Subject', 'Vendor_Name.Vendor_Name', - 'Status', 'Due_Date', 'Total', 'Owner', 'Created_Time' + 'id', 'Subject', 'Vendor_Name.Vendor_Name', 'Status', + 'Due_Date', 'Grand_Total', 'Owner', 'Created_Time' ] } ]; diff --git a/src/services/csvService.js b/src/services/csvService.js index 76e5149..2da6736 100644 --- a/src/services/csvService.js +++ b/src/services/csvService.js @@ -261,7 +261,7 @@ class CsvService { invoice_date: this.parseDate(record.Invoice_Date), due_date: this.parseDate(record.Due_Date), status: record.Status, - total: this.parseDecimal(record.Total), + total: this.parseDecimal(record.Grand_Total), balance: this.parseDecimal(record.Balance), account_name: record['Account_Name.Account_Name'] || record.Account_Name, owner: record.Owner, @@ -277,7 +277,7 @@ class CsvService { subject: record.Subject, status: record.Status, 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, owner: record.Owner, created_time: this.parseDate(record.Created_Time) @@ -293,7 +293,7 @@ class CsvService { vendor_name: record['Vendor_Name.Vendor_Name'] || record.Vendor_Name, status: record.Status, due_date: this.parseDate(record.Due_Date), - total: this.parseDecimal(record.Total), + total: this.parseDecimal(record.Grand_Total), owner: record.Owner, created_time: this.parseDate(record.Created_Time) };