Skip to main content

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 list
  • calculateHourSpent utility - Time calculation
  • calculateCost utility - Cost calculation
  • getProductTierDetails utility - Product configuration lookup
  • catchAsync - Error handling

Product Configuration

  • HOURLY_RATE_AND_COST - Product/tier configuration array
  • SUBSCRIPTION_TIERS - Tier name constants
  • PRODUCTS - 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() and getMonth()
  • 🔄 Faceted Aggregation: Parallel calculation of all metrics for performance
  • 🏷️ Product Tiers: Matches product_name + price_name to tier configuration

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

💬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM