Skip to main content

๐Ÿ” Account Active Status Check

๐Ÿ“– Overviewโ€‹

The Account Active Status Check job is a weekly maintenance task that identifies inactive software accounts and marks them for purging. It examines all active main accounts, checks their software subscription status, and flags accounts with canceled subscriptions (over 14 days) or no subscriptions for future cleanup. The job processes accounts in batches of 500 to optimize performance.

Complete Flow:

  1. Cron Initialization: queue-manager/crons/accounts/checkaccountactivestatus.js
  2. Service Processing: queue-manager/services/accounts/accountpurgecheck.js
  3. Queue Definition: None (direct database operations)

Execution Pattern: Cron-based (weekly on Sundays at midnight)

Queue Name: N/A (no Bull queue, service-only)

Environment Flag: QM_ACCOUNTS_PURGECHECK=true (in index.js)

๐Ÿ”„ Complete Processing Flowโ€‹

sequenceDiagram
participant CRON as Cron Schedule<br/>(Weekly Sunday)
participant SERVICE as Purge Check<br/>Service
participant ACCOUNT_DB as Accounts<br/>Collection
participant SUB_DB as Store<br/>Subscriptions
participant LOGGER as Logger

CRON->>SERVICE: Weekly status check
SERVICE->>LOGGER: Start purge check process

loop Process in batches of 500
SERVICE->>ACCOUNT_DB: Query active main accounts<br/>(without purge.lead flag)
ACCOUNT_DB-->>SERVICE: Return batch of accounts

alt Batch is empty
SERVICE->>LOGGER: No more accounts
SERVICE->>SERVICE: Exit loop
else Process batch
loop For each account
SERVICE->>SUB_DB: Get latest software subscription
SUB_DB-->>SERVICE: Return subscription

alt No subscription found
SERVICE->>ACCOUNT_DB: Set purge.lead = true
SERVICE->>LOGGER: Mark for purging<br/>(no subscription)
else Subscription canceled > 14 days
SERVICE->>ACCOUNT_DB: Set purge.lead = true
SERVICE->>LOGGER: Mark for purging<br/>(X days since cancel)
else Still active
SERVICE->>LOGGER: Keep active
end
end

SERVICE->>LOGGER: Batch summary<br/>(active vs. inactive)
end
end

SERVICE->>LOGGER: Final summary<br/>(total processed, counts)
SERVICE-->>CRON: Return results

๐Ÿ“ Source Filesโ€‹

1. Cron Initializationโ€‹

File: queue-manager/crons/accounts/checkaccountactivestatus.js

Purpose: Schedule weekly account purge checks

Cron Pattern: 0 0 * * 0 (every Sunday at midnight)

Initialization:

const { isSoftwareActive } = require('../../services/accounts/accountpurgecheck');
const cron = require('node-cron');
const logger = require('../../utilities/logger');

let inProgress = false;
exports.start = async () => {
try {
cron.schedule('0 0 * * 0', async () => {
if (!inProgress) {
inProgress = true;
await isSoftwareActive();
inProgress = false;
}
});
} catch (err) {
logger.error({ initiator: 'QM/accounts/purgecheck', error: err });
}
};

In-Progress Lock: Prevents overlapping executions if processing takes longer than 1 week.

2. Service Processing (THE CORE LOGIC)โ€‹

File: queue-manager/services/accounts/accountpurgecheck.js

Purpose: Check account subscription status and mark inactive accounts for purging

Key Functions:

  • Query active main accounts without purge flag
  • Batch processing (500 accounts per batch)
  • Check latest software subscription for each account
  • Calculate days since subscription cancellation
  • Mark inactive accounts with metadata.purge.lead = true
  • Comprehensive logging of results

Main Processing Function:

const isSoftwareActive = async () => {
try {
const batchSize = 500; // Process 500 accounts at a time
let skip = 0;
let allInactiveAccounts = [];
let allStillActiveAccounts = [];
let totalProcessed = 0;

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: 'Starting account purge check process...',
});

while (true) {
// Step 1: Get active main accounts without purge flag
const activeAccounts = await Account.find({
main: true,
active: true,
$or: [{ 'metadata.purge.lead': { $exists: false } }, { 'metadata.purge.lead': false }],
})
.sort({ _id: 1 }) // Consistent sorting for pagination
.skip(skip)
.limit(batchSize)
.lean();

// Exit when no more accounts
if (activeAccounts?.length === 0) {
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: 'No more accounts to process',
});
break;
}

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Processing batch: ${skip + 1} to ${skip + activeAccounts.length}`,
});

const accountIds = activeAccounts.map(account => account._id.toString());
let inactiveAccounts = [];
let stillActiveAccounts = [];

// Current timestamp and 14-day threshold
const currentTimestamp = Math.floor(Date.now() / 1000);
const fourteenDaysInSeconds = 14 * 24 * 60 * 60; // 14 days in seconds

// Step 2: Check each account's subscription status
for (const accountId of accountIds) {
// Get latest software subscription
const latestSubscription = await StoreSubscription.findOne({
$or: [
{ 'metadata.account_id': accountId },
{ 'metadata.account_id': new mongoose.Types.ObjectId(accountId) },
],
'plan.metadata.software': 'true',
'plan.metadata.product_type': 'software',
})
.sort({ created_at: -1 })
.lean(); // Get the latest subscription

if (latestSubscription) {
// Step 3: Check if subscription is canceled and over 14 days
if (
latestSubscription.status === 'canceled' &&
latestSubscription.canceled_at &&
currentTimestamp - latestSubscription.canceled_at > fourteenDaysInSeconds
) {
// Mark account as inactive
try {
await Account.updateOne(
{ _id: accountId },
{ $set: { 'metadata.purge.lead': true } },
);

inactiveAccounts.push({
accountId: accountId,
subscriptionStatus: latestSubscription.status,
subscriptionId: latestSubscription._id,
planName: latestSubscription.plan?.nickname,
canceledAt: latestSubscription.canceled_at,
daysSinceCancellation: Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
),
});

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - canceled ${Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
)} days ago`,
});
} catch (updateError) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId}`,
});
}
} else {
// Subscription still active or recently canceled
stillActiveAccounts.push({
accountId: accountId,
subscriptionStatus: latestSubscription.status,
subscriptionId: latestSubscription._id,
planName: latestSubscription.plan?.nickname,
});
}
} else {
// No software subscription found - mark as inactive
try {
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });

inactiveAccounts.push({
accountId: accountId,
subscriptionStatus: 'no_software_subscription',
subscriptionId: null,
planName: null,
canceledAt: null,
daysSinceCancellation: null,
});

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - no software subscription found`,
});
} catch (updateError) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId} (no subscription)`,
});
}
}
}

// Accumulate results from this batch
allInactiveAccounts = allInactiveAccounts.concat(inactiveAccounts);
allStillActiveAccounts = allStillActiveAccounts.concat(stillActiveAccounts);
totalProcessed += accountIds.length;

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Batch ${Math.floor(skip / batchSize) + 1} completed: ${
stillActiveAccounts.length
} still active, ${
inactiveAccounts.length
} marked for purging. Total processed: ${totalProcessed}`,
});

// Increment skip for next batch
skip += batchSize;
}

// Final summary
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Final Summary: Total processed: ${totalProcessed}, Still active: ${allStillActiveAccounts.length}, Marked for purging: ${allInactiveAccounts.length}`,
});

// Log all inactive accounts
if (allInactiveAccounts.length > 0) {
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `All accounts marked for purging: ${allInactiveAccounts
.map(acc => `${acc.accountId} (${acc.daysSinceCancellation} days)`)
.join(', ')}`,
});
}

return {
inactiveAccounts: allInactiveAccounts,
activeAccounts: allStillActiveAccounts,
summary: {
total: totalProcessed,
active: allStillActiveAccounts.length,
inactive: allInactiveAccounts.length,
markedForPurging: allInactiveAccounts.length,
},
};
} catch (error) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: error,
});
return {
inactiveAccounts: [],
activeAccounts: [],
summary: { total: 0, active: 0, inactive: 0, markedForPurging: 0 },
};
}
};

module.exports = {
isSoftwareActive,
};

๐Ÿ—„๏ธ Collections Usedโ€‹

accountsโ€‹

  • Operations: Read, Update
  • Model: shared/models/account.js
  • Usage Context:
    • Query active main accounts
    • Mark inactive accounts with purge flag
    • Track purge status via metadata.purge.lead

Query Criteria:

{
main: true, // Main accounts only
active: true, // Active accounts
$or: [
{ 'metadata.purge.lead': { $exists: false } }, // Never checked
{ 'metadata.purge.lead': false }, // Previously active
],
}

Update Operation:

{
$set: { 'metadata.purge.lead': true } // Mark for purging
}

Key Fields:

  • main: Boolean - identifies main/parent accounts
  • active: Boolean - account active status
  • metadata.purge.lead: Boolean - purge flag for inactive accounts

store_subscriptionsโ€‹

  • Operations: Read
  • Model: shared/models/store-subscription.js
  • Usage Context: Check latest software subscription status for each account

Query Criteria:

{
$or: [
{ 'metadata.account_id': accountId }, // String format
{ 'metadata.account_id': ObjectId(accountId) }, // ObjectId format
],
'plan.metadata.software': 'true', // Software product
'plan.metadata.product_type': 'software', // Product type
}

Sort: { created_at: -1 } (latest subscription first)

Key Fields:

  • status: 'active' | 'canceled' | 'past_due' | etc.
  • canceled_at: Unix timestamp (seconds) when subscription canceled
  • plan.nickname: Subscription plan name
  • plan.metadata.software: 'true' for software subscriptions
  • plan.metadata.product_type: 'software'
  • metadata.account_id: Associated account ID

๐Ÿ”ง Job Configurationโ€‹

Cron Scheduleโ€‹

'0 0 * * 0'; // Every Sunday at midnight (00:00)

Frequency Rationale: Weekly checks provide timely identification of inactive accounts while minimizing database load.

Batch Processingโ€‹

const batchSize = 500; // Process 500 accounts per batch

Performance Optimization:

  • Prevents memory overflow from large result sets
  • Enables progress tracking via logging
  • Allows graceful handling of connection issues

Inactivity Thresholdโ€‹

const fourteenDaysInSeconds = 14 * 24 * 60 * 60; // 14 days

Grace Period: Accounts with subscriptions canceled less than 14 days ago are NOT marked for purging.

๐Ÿ“‹ Processing Logic - Detailed Flowโ€‹

Account Selection Criteriaโ€‹

Main Accounts Only:

{
main: true;
}
  • Excludes sub-accounts (which inherit parent status)
  • Focuses on parent/master accounts

Active Accounts Only:

{
active: true;
}
  • Skips already-deactivated accounts
  • Only checks currently active accounts

Without Purge Flag:

{
$or: [
{ 'metadata.purge.lead': { $exists: false } },
{ 'metadata.purge.lead': false },
],
}
  • Avoids re-processing already-marked accounts
  • Includes accounts never checked before

Subscription Status Logicโ€‹

Case 1: No Software Subscription

if (!latestSubscription) {
// Mark for purging immediately
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
}

Reason: Account without software subscription should not exist as active.

Case 2: Subscription Canceled > 14 Days

if (
latestSubscription.status === 'canceled' &&
latestSubscription.canceled_at &&
currentTimestamp - latestSubscription.canceled_at > fourteenDaysInSeconds
) {
// Mark for purging
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
}

Reason: Grace period expired, account should be purged.

Case 3: Still Active or Recently Canceled

else {
stillActiveAccounts.push({
accountId: accountId,
subscriptionStatus: latestSubscription.status,
subscriptionId: latestSubscription._id,
planName: latestSubscription.plan?.nickname,
});
}

Reason: Subscription active, trialing, past_due, or canceled within 14 days.

Batch Processing Flowโ€‹

  1. Query Batch of Accounts

    const activeAccounts = await Account.find({...})
    .sort({ _id: 1 })
    .skip(skip)
    .limit(batchSize)
    .lean();
    • Consistent _id sorting ensures no duplicates
    • lean() improves performance (plain objects)
  2. Check Each Account's Subscription

    const latestSubscription = await StoreSubscription.findOne({...})
    .sort({ created_at: -1 })
    .lean();
    • Only fetches latest subscription
    • Checks both string and ObjectId formats
  3. Calculate Days Since Cancellation

    const daysSinceCancellation = Math.floor(
    (currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
    );
    • Converts seconds to days
    • Used for logging and reporting
  4. Update Account if Inactive

    await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
    • Atomic update operation
    • Sets purge flag for future cleanup jobs
  5. Log Batch Results

    logger.log({
    initiator: 'QM/accounts/accountpurgecheck',
    message: `Batch ${Math.floor(skip / batchSize) + 1} completed: ${
    stillActiveAccounts.length
    } still active, ${
    inactiveAccounts.length
    } marked for purging. Total processed: ${totalProcessed}`,
    });
  6. Increment Skip for Next Batch

    skip += batchSize; // Move to next 500 accounts

Return Value Structureโ€‹

{
inactiveAccounts: [
{
accountId: '507f1f77bcf86cd799439011',
subscriptionStatus: 'canceled',
subscriptionId: '507f1f77bcf86cd799439012',
planName: 'Pro Plan',
canceledAt: 1735689600, // Unix timestamp
daysSinceCancellation: 20
},
{
accountId: '507f1f77bcf86cd799439013',
subscriptionStatus: 'no_software_subscription',
subscriptionId: null,
planName: null,
canceledAt: null,
daysSinceCancellation: null
}
],
activeAccounts: [
{
accountId: '507f1f77bcf86cd799439014',
subscriptionStatus: 'active',
subscriptionId: '507f1f77bcf86cd799439015',
planName: 'Enterprise Plan'
}
],
summary: {
total: 1500, // Total accounts processed
active: 1475, // Still active accounts
inactive: 25, // Marked for purging
markedForPurging: 25
}
}

๐Ÿšจ Error Handlingโ€‹

Common Error Scenariosโ€‹

Account Update Failureโ€‹

try {
await Account.updateOne({ _id: accountId }, { $set: { 'metadata.purge.lead': true } });
} catch (updateError) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId}`,
});
}

Result: Error logged, processing continues with next account.

Subscription Query Failureโ€‹

const latestSubscription = await StoreSubscription.findOne({...})
.sort({ created_at: -1 })
.lean();

if (!latestSubscription) {
// Treat as no subscription found
await Account.updateOne(
{ _id: accountId },
{ $set: { 'metadata.purge.lead': true } },
);
}

Result: Account marked for purging if no subscription exists.

Database Connection Errorโ€‹

catch (error) {
logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: error,
});
return {
inactiveAccounts: [],
activeAccounts: [],
summary: { total: 0, active: 0, inactive: 0, markedForPurging: 0 },
};
}

Result: Returns empty results, will retry next week.

Error Recoveryโ€‹

  • Batch-Level: If a batch fails, already-processed batches are saved (no rollback)
  • Account-Level: Individual account errors don't stop batch processing
  • Service-Level: Top-level errors return empty results, job retries next week

๐Ÿ“Š Monitoring & Loggingโ€‹

Start Loggingโ€‹

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: 'Starting account purge check process...',
});

Batch Loggingโ€‹

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Processing batch: ${skip + 1} to ${skip + activeAccounts.length}`,
});

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Batch ${Math.floor(skip / batchSize) + 1} completed: ${
stillActiveAccounts.length
} still active, ${
inactiveAccounts.length
} marked for purging. Total processed: ${totalProcessed}`,
});

Account-Level Loggingโ€‹

// Marked for purging (canceled)
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - canceled ${Math.floor(
(currentTimestamp - latestSubscription.canceled_at) / (24 * 60 * 60),
)} days ago`,
});

// Marked for purging (no subscription)
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Marked account ${accountId} for purging - no software subscription found`,
});

Final Summary Loggingโ€‹

logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `Final Summary: Total processed: ${totalProcessed}, Still active: ${allStillActiveAccounts.length}, Marked for purging: ${allInactiveAccounts.length}`,
});

// List all inactive accounts
if (allInactiveAccounts.length > 0) {
logger.log({
initiator: 'QM/accounts/accountpurgecheck',
message: `All accounts marked for purging: ${allInactiveAccounts
.map(acc => `${acc.accountId} (${acc.daysSinceCancellation} days)`)
.join(', ')}`,
});
}

Error Loggingโ€‹

logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: updateError,
message: `Error updating account ${accountId}`,
});

logger.error({
initiator: 'QM/accounts/accountpurgecheck',
error: error,
});

Performance Metricsโ€‹

  • Average Processing Time: 5-15 minutes (depends on account count)
  • Batch Size: 500 accounts per batch
  • Typical Volume: 1,000-10,000 accounts per week
  • Mark Rate: ~5-10% of accounts marked for purging

๐Ÿ”— Integration Pointsโ€‹

Triggers This Jobโ€‹

  • Cron Schedule: Every Sunday at midnight automatically
  • Manual Trigger: Via API endpoint (if QM_HOOKS=true)

Data Dependenciesโ€‹

  • Active Main Accounts: Must have main: true and active: true
  • Store Subscriptions: Software subscription records with cancellation dates
  • 14-Day Grace Period: Hardcoded threshold for purge eligibility

Jobs That Depend On Thisโ€‹

  • Account Purge Job: Uses metadata.purge.lead flag to identify accounts for deletion
  • Cleanup Jobs: Additional cleanup tasks may reference purge flag
  • Reporting Jobs: Analytics on subscription churn and account lifecycle

โš ๏ธ Important Notesโ€‹

Side Effectsโ€‹

  • โš ๏ธ Account Flagging: Sets metadata.purge.lead = true on inactive accounts
  • โš ๏ธ Database Writes: Updates account records (1 write per inactive account)
  • โš ๏ธ No Deletion: This job only FLAGS accounts, does NOT delete them
  • โš ๏ธ Irreversible: Once flagged, accounts won't be re-checked (flag remains until purge or manual reset)

Performance Considerationsโ€‹

  • Weekly Schedule: Minimizes database load while catching inactive accounts
  • Batch Processing: 500 accounts per batch prevents memory overflow
  • Lean Queries: Uses .lean() for improved performance (plain objects)
  • Sequential Processing: Accounts processed one-by-one within batch (avoids race conditions)
  • Indexing: Ensure indexes on accounts.main, accounts.active, accounts.metadata.purge.lead

Maintenance Notesโ€‹

  • Purge Flag Persistence: metadata.purge.lead remains until account purged or manually reset
  • Grace Period: 14-day threshold hardcoded - requires code change to modify
  • Subscription Types: Only checks plan.metadata.product_type = 'software'
  • Main Accounts Only: Sub-accounts not checked (inherit parent status)
  • Timestamp Format: Uses Unix timestamps (seconds), not milliseconds

Business Logicโ€‹

Why 14 Days?

  • Gives users time to renew subscriptions
  • Prevents accidental purging of temporarily canceled accounts
  • Industry standard grace period for SaaS

Why Mark Instead of Delete?

  • Allows human review before permanent deletion
  • Enables recovery if flagged incorrectly
  • Separate purge job can handle actual deletion with additional safeguards

Why Weekly?

  • Daily checks unnecessary (14-day grace period)
  • Weekly schedule reduces database load
  • Sunday midnight minimizes impact on business operations

๐Ÿงช Testingโ€‹

Manual Triggerโ€‹

# Via API (if QM_HOOKS=true)
POST http://localhost:6002/api/trigger/accounts/purgecheck

Create Test Scenariosโ€‹

// Scenario 1: Account with canceled subscription (20 days ago)
const testAccount1 = await Account.create({
main: true,
active: true,
email: 'test1@example.com',
// No purge flag
});

const testSubscription1 = await StoreSubscription.create({
metadata: {
account_id: testAccount1._id.toString(),
},
plan: {
metadata: {
software: 'true',
product_type: 'software',
},
nickname: 'Pro Plan',
},
status: 'canceled',
canceled_at: Math.floor(Date.now() / 1000) - 20 * 24 * 60 * 60, // 20 days ago
created_at: Math.floor(Date.now() / 1000) - 60 * 24 * 60 * 60, // Created 60 days ago
});

// Scenario 2: Account with no subscription
const testAccount2 = await Account.create({
main: true,
active: true,
email: 'test2@example.com',
});

// Scenario 3: Account with recently canceled subscription (5 days ago)
const testAccount3 = await Account.create({
main: true,
active: true,
email: 'test3@example.com',
});

const testSubscription3 = await StoreSubscription.create({
metadata: {
account_id: testAccount3._id.toString(),
},
plan: {
metadata: {
software: 'true',
product_type: 'software',
},
nickname: 'Enterprise Plan',
},
status: 'canceled',
canceled_at: Math.floor(Date.now() / 1000) - 5 * 24 * 60 * 60, // 5 days ago
created_at: Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60,
});

// Scenario 4: Account with active subscription
const testAccount4 = await Account.create({
main: true,
active: true,
email: 'test4@example.com',
});

const testSubscription4 = await StoreSubscription.create({
metadata: {
account_id: testAccount4._id.toString(),
},
plan: {
metadata: {
software: 'true',
product_type: 'software',
},
nickname: 'Starter Plan',
},
status: 'active',
created_at: Math.floor(Date.now() / 1000) - 10 * 24 * 60 * 60,
});

// Run purge check
const result = await isSoftwareActive();

// Verify results
console.log('Summary:', result.summary);
// { total: 4, active: 2, inactive: 2, markedForPurging: 2 }

// Check flagged accounts
const flagged1 = await Account.findById(testAccount1._id);
console.log('Account 1 purge flag:', flagged1.metadata.purge.lead); // true (20 days)

const flagged2 = await Account.findById(testAccount2._id);
console.log('Account 2 purge flag:', flagged2.metadata.purge.lead); // true (no subscription)

const flagged3 = await Account.findById(testAccount3._id);
console.log('Account 3 purge flag:', flagged3.metadata?.purge?.lead); // undefined/false (only 5 days)

const flagged4 = await Account.findById(testAccount4._id);
console.log('Account 4 purge flag:', flagged4.metadata?.purge?.lead); // undefined/false (active)

Monitor Executionโ€‹

// Count accounts pending purge check
const pendingCheck = await Account.countDocuments({
main: true,
active: true,
$or: [{ 'metadata.purge.lead': { $exists: false } }, { 'metadata.purge.lead': false }],
});
console.log('Accounts pending purge check:', pendingCheck);

// Count accounts marked for purging
const markedForPurge = await Account.countDocuments({
main: true,
'metadata.purge.lead': true,
});
console.log('Accounts marked for purging:', markedForPurge);

// Find accounts marked this week
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const recentlyMarked = await Account.find({
'metadata.purge.lead': true,
updatedAt: { $gte: oneWeekAgo },
});
console.log('Recently marked accounts:', recentlyMarked.length);

// Check subscription distribution
const subscriptionStats = await StoreSubscription.aggregate([
{
$match: {
'plan.metadata.software': 'true',
'plan.metadata.product_type': 'software',
},
},
{
$group: {
_id: '$status',
count: { $sum: 1 },
},
},
]);
console.log('Subscription status distribution:', subscriptionStats);
// [ { _id: 'active', count: 500 }, { _id: 'canceled', count: 50 }, ... ]

Job Type: Scheduled
Execution Frequency: Weekly (Sundays at midnight)
Average Duration: 5-15 minutes
Status: Active

๐Ÿ’ฌ

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