Analytics
Overview
The Analytics service provides comprehensive revenue and ROI metrics for managed services, calculating profits, retention rates, time savings, and cost savings based on active subscriptions and product configurations.
Source Files:
- Service:
internal/api/v1/projects/services/analytics.service.js - Controller:
internal/api/v1/projects/controllers/analytics.controller.js - Utilities:
internal/api/v1/projects/utils/analytics.js
Key Capabilities:
- Calculate total and monthly profit from subscriptions
- Compute average retention months
- Calculate time saved based on product hours
- Calculate cost savings based on hourly rates
- Product-level breakdowns with month counts
- Current month vs all-time comparisons
Collections Used
_store.orders
Operations: Read (aggregation)
Model: shared/models/store-order.js
Usage: Primary source for active order analytics
Key Fields:
{
_id: ObjectId,
status: 'active',
seller_account: ObjectId,
buyer_account: ObjectId,
subscription: ObjectId,
metadata: {
product_type: String,
product_name: String,
price_name: String,
images: [String]
},
acc_price: {
monthly_cost: Number
},
created_at: Date
}
_store.subscriptions
Operations: Read (lookup)
Usage: Subscription details for profit and retention calculations
Key Fields:
{
_id: ObjectId,
plan: {
amount: Number,
nickname: String,
metadata: {
product_type: String
},
interval_count: Number
},
status: String, // 'active', 'trialing', 'past_due', 'canceled'
canceled_at: Number, // Unix timestamp (seconds)
created: Number, // Unix timestamp (seconds)
start_date: Number // Unix timestamp (seconds)
}
Business Logic & Functions
Service Layer
getAnalytics({ accountId })
Purpose: Calculates comprehensive analytics metrics for an agency account's managed services.
Parameters:
accountId(ObjectId) - Seller/agency account ID
Returns: Analytics object with all calculated metrics
{
total_profit: Number, // Lifetime profit (cents)
this_month_profit: Number, // Current month profit (cents)
averageRetentionMonths: Number, // Average months retained
total_time_saved: Number, // Total hours saved
this_month_time_saved: Number, // Current month hours saved
total_cost_saving: Number, // Total cost saved (cents)
this_month_cost_saving: Number // Current month cost saved (cents)
}
Aggregation Pipeline Logic:
Stage 1: Match Active Orders
{
$match: {
status: 'active',
seller_account: accountId,
buyer_account: { $ne: null },
'metadata.product_type': { $in: MANAGED_SUBSCRIPTIONS }
}
}
Stage 2: Lookup Subscription
{
$lookup: {
from: '_store.subscriptions',
let: { subscription_id: '$subscription' },
pipeline: [
{ $match: { $expr: { $eq: ['$_id', '$$subscription_id'] } }},
{ $project: {
'plan.amount': 1,
'plan.nickname': 1,
'plan.metadata.product_type': 1,
'plan.interval_count': 1,
canceled_at: 1,
created: 1,
start_date: 1,
status: 1
}}
]
}
}
Stage 3: Calculate Product Metrics
{
$project: {
subscription: 1,
product: {
name: '$metadata.product_name',
nickname: '$metadata.price_name',
month_active: {
$ceil: {
$divide: [
{
$subtract: [
// Use canceled_at if exists, otherwise current time
{ $ifNull: [
{ $multiply: ['$subscription.canceled_at', 1000] },
{ $toLong: new Date().getTime() }
]},
{ $multiply: ['$subscription.start_date', 1000] }
]
},
2592000000 // 30 days in milliseconds
]
}
}
},
created_at: 1,
// Profit Calculation
profit: {
$cond: {
if: { $in: ['$subscription.status', ['active', 'trialing', 'past_due']] },
then: {
$subtract: [
{
$ifNull: [
{ $multiply: [
'$acc_price.monthly_cost',
'$subscription.plan.interval_count'
]},
'$subscription.plan.amount'
]
},
'$subscription.plan.amount'
]
},
else: 0 // Canceled subscriptions have 0 profit
}
},
// Retention Calculation
retentionMonths: {
$ceil: {
$divide: [
{
$subtract: [
{
$cond: [
{ $eq: ['$subscription.canceled_at', null] },
{ $toLong: new Date().getTime() },
{ $multiply: ['$subscription.canceled_at', 1000] }
]
},
{ $multiply: ['$subscription.created', 1000] }
]
},
2592000000 // 30 days in milliseconds
]
}
}
}
}
Profit Formula:
IF subscription is active/trialing/past_due:
profit = (acc_price.monthly_cost × interval_count || plan.amount) - plan.amount
ELSE:
profit = 0
Stage 4: Faceted Aggregation
{
$facet: {
// Overall metrics
data: [
{
$group: {
_id: null,
total_profit: { $sum: '$profit' },
monthly_profit: {
$sum: {
$cond: [
{
$and: [
{ $gte: [{ $toDate: '$created_at' }, startOfMonth] },
{ $lt: [{ $toDate: '$created_at' }, startOfNextMonth] }
]
},
'$profit',
0
]
}
},
averageRetentionMonths: { $avg: '$retentionMonths' }
}
},
{
$project: {
_id: 0,
total_profit: { $cond: { if: { $lt: ['$total_profit', 0] }, then: 0, else: '$total_profit' }},
this_month_profit: { $cond: { if: { $lt: ['$monthly_profit', 0] }, then: 0, else: '$monthly_profit' }},
averageRetentionMonths: {
$round: {
$cond: { if: { $lt: ['$averageRetentionMonths', 0] }, then: 0, else: '$averageRetentionMonths' }
}
}
}
}
],
// Product breakdown (all time)
products: [
{
$group: {
_id: { name: '$product.name', nickname: '$product.nickname' },
productCount: { $sum: 1 },
monthCount: { $sum: { $ifNull: ['$product.month_active', 0] }}
}
},
{
$project: {
_id: 0,
productName: '$_id.name',
nickname: '$_id.nickname',
productCount: 1,
monthCount: 1
}
}
],
// Product breakdown (current month)
this_month_products: [
{
$match: {
$expr: {
$and: [
// Subscription started before end of current month
{ $lt: [
'$subscription.start_date',
{ $divide: [{ $toLong: endOfMonth }, 1000] }
]},
{
$or: [
// Still active (not canceled)
{ $eq: ['$subscription.canceled_at', null] },
// Canceled but not in current month
{ $gte: [
'$subscription.canceled_at',
{ $divide: [{ $toLong: startOfMonth }, 1000] }
]}
]
}
]
}
}
},
{
$group: {
_id: { name: '$product.name', nickname: '$product.nickname' },
productCount: { $sum: 1 }
}
},
{
$project: {
_id: 0,
productName: '$_id.name',
nickname: '$_id.nickname',
productCount: 1
}
}
]
}
}
Stage 5: Calculate Time & Cost Savings
const totalTimeSaved = calculateHourSpent(analyticsResult.products);
const thisMonthtotalTimeSaved = calculateHourSpent(analyticsResult.this_month_products);
const totalCostsaving = calculateCost(analyticsResult.products) * 100;
const thisMonthCostsaving = calculateCost(analyticsResult.this_month_products) * 100;
return {
...analyticsResult.data[0],
total_time_saved: Math.max(0, Math.round(totalTimeSaved)),
this_month_time_saved: Math.max(0, Math.round(thisMonthtotalTimeSaved)),
total_cost_saving: Math.max(0, totalCostsaving),
this_month_cost_saving: Math.max(0, thisMonthCostsaving),
};
Utility Functions
calculateHourSpent(products)
Purpose: Calculates total hours spent based on product configurations and active months.
Parameters:
products(Array) - Product breakdown with counts and months
Returns: Total hours (Number)
Logic:
products.reduce((totalHours, { productName, nickname, productCount, monthCount }) => {
const productTierDetails = getProductTierDetails(productName, nickname);
if (!productTierDetails) return totalHours;
const { setupHours, monthlyHours } = productTierDetails;
// Monthly hours for all active months
const totalMonthlyHours = monthlyHours * (monthCount || 1);
// Setup hours for each product
const totalSetupHours = productCount * setupHours;
return totalHours + totalMonthlyHours + totalSetupHours;
}, 0);
Example:
5 Meta Ads Pro products:
- Setup: 5 products × 10 hours = 50 hours
- Monthly: 240 hours (varies by active months per product)
- Total: 290 hours
calculateCost(products)
Purpose: Calculates total cost savings based on hourly rates and time spent.
Parameters:
products(Array) - Product breakdown with counts and months
Returns: Total cost (Number, in dollars)
Logic:
products.reduce((totalCost, { productName, nickname, productCount, monthCount }) => {
const productTierDetails = getProductTierDetails(productName, nickname);
if (!productTierDetails) return totalCost;
const { totalSetup, totalMonthly } = productTierDetails;
// Monthly cost for all active months
const totalMonthlyCost = totalMonthly * (monthCount || 1);
// Setup cost for each product
const totalSetupCost = productCount * totalSetup;
return totalCost + totalMonthlyCost + totalSetupCost;
}, 0);
Example:
5 Meta Ads Pro products:
- Setup: 5 × $400 = $2,000
- Monthly: $9,600 (varies by active months per product)
- Total: $11,600
getProductTierDetails(productName, tierName)
Purpose: Retrieves hourly rates and costs for specific product/tier combination.
Parameters:
productName(String) - Product name (e.g., "Meta Ads")tierName(String) - Tier name (e.g., "Pro", "Plus", "Platinum")
Returns: Tier details object or null
Product Configuration:
{
name: "Meta Ads",
tiers: {
"Pro": {
setupHours: 10,
monthlyHours: 15,
hourlyRate: 40,
totalSetup: 400,
totalMonthly: 600,
totalCombined: 1000
},
"Plus": { setupHours: 14, monthlyHours: 21, ... },
"Platinum": { setupHours: 18, monthlyHours: 27, ... }
}
}
Supported Products:
- Meta Ads (Pro, Plus, Platinum)
- Google Ads (Pro, Plus, Platinum)
- TikTok Ads (Pro, Plus, Platinum)
- SEO (Pro, Plus, Platinum)
- Backlinks (Pro, Plus, Platinum)
- GBP Ranker (Pro, Plus, Platinum)
- Content (Pro, Plus, Platinum)
- Social Posts (Pro, Plus, Platinum)
- Websites (Information, E-Commerce Pro/Plus/Platinum)
- Listings (Pro)
Controller Layer
getAnalytics(req, res)
Route: GET /api/projects/analytics
Authorization: Requires authentication
Response:
{
success: true,
message: 'SUCCESS',
data: {
total_profit: 150000,
this_month_profit: 12000,
averageRetentionMonths: 8,
total_time_saved: 1250,
this_month_time_saved: 105,
total_cost_saving: 5000000,
this_month_cost_saving: 420000
}
}
Data Flow Diagram
flowchart TD
A[GET /analytics] --> B[Match Active Orders]
B --> C[Lookup Subscriptions]
C --> D[Calculate Product Metrics]
D --> E[Calculate Profit]
E --> F[Calculate Retention Months]
F --> G[Faceted Aggregation]
G --> H[Group Overall Metrics]
G --> I[Group All-Time Products]
G --> J[Group Current Month Products]
H --> K[Ensure Non-Negative Values]
I --> L[Calculate Time Saved - All Time]
J --> M[Calculate Time Saved - Current Month]
L --> N[Calculate Cost Savings - All Time]
M --> O[Calculate Cost Savings - Current Month]
K --> P[Merge All Results]
N --> P
O --> P
P --> Q[Return Analytics Data]
Integration Points
Internal Dependencies
MANAGED_SUBSCRIPTIONS- Service type listcalculateHourSpentutility - Time calculationcalculateCostutility - Cost calculationgetProductTierDetailsutility - Product configuration lookupcatchAsync- Error handling
Product Configuration
HOURLY_RATE_AND_COST- Product/tier configuration arraySUBSCRIPTION_TIERS- Tier name constantsPRODUCTS- Product name constants
Key Metrics Explained
Profit Calculation
For active/trialing/past_due subscriptions:
profit = revenue - cost
revenue = acc_price.monthly_cost × interval_count (or plan.amount as fallback)
cost = subscription.plan.amount
For canceled subscriptions:
profit = 0
Retention Months
retentionMonths = ceil((end_date - created_date) / 30_days)
where end_date is:
- canceled_at (if subscription canceled)
- current date (if subscription active)
Month Active
month_active = ceil((end_date - start_date) / 30_days)
where end_date is:
- canceled_at (if subscription canceled)
- current date (if subscription active)
Time Saved
For each product:
setup_time = productCount × setupHours
monthly_time = monthCount × monthlyHours
total = setup_time + monthly_time
Cost Savings
For each product:
setup_cost = productCount × totalSetup
monthly_cost = monthCount × totalMonthly
total = setup_cost + monthly_cost
Note: Returned in cents (× 100)
Important Notes
- 💰 Currency: All monetary values in cents (divide by 100 for dollars)
- 📊 Active Only: Only counts orders with
status: 'active' - ⏰ 30-Day Months: Uses 2592000000ms (30 days) for month calculations
- 🔢 Ceiling Function: Months always rounded up to nearest integer
- ✅ Non-Negative: All final values ensured to be ≥ 0
- 📅 Current Month: Calculated using
new Date().getFullYear()andgetMonth() - 🔄 Faceted Aggregation: Parallel calculation of all metrics for performance
- 🏷️ Product Tiers: Matches
product_name+price_nameto tier configuration
Related Documentation
- Subscriptions - Related subscription management
- Dashboard - Dashboard metrics display
- Projects Module Overview - Parent module
Last Updated: 2025-10-08
Service Files:services/analytics.service.js,controllers/analytics.controller.js,utils/analytics.js
Primary Functions: 1 service function, 3 utility functions, 1 controller endpoint