Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 29, 2026

Description

Implements automated business logic hooks for core CRM objects following @objectstack/spec v0.6.1 protocol. Covers 20+ automation scenarios including health scoring, hierarchy management, relationship strength tracking, ROI calculation, and recurrence handling.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • CI/CD update

Related Issues

Related to Week 2-3 Business Logic Implementation milestone

Changes Made

Account Hooks (packages/crm/src/hooks/account.hook.ts)

  • Health score calculation (0-100) based on customer status, contract value, activities, payment history, support cases
  • Parent/child hierarchy with cascading ownership and metric aggregation
  • Auto-upgrade Prospect → Active Customer on first contract
  • Contract value rollup from all active contracts
  • Renewal date tracking with automatic reminder tasks at 90/60/30 days

Contact Hooks (packages/crm/src/hooks/contact.hook.ts)

  • Last contact date updates on activity creation
  • Auto-set influence level based on job title/level (C-Level → High, VP → High, etc.)
  • Duplicate detection by email/name+account
  • Relationship strength auto-promotion/demotion based on activity frequency (90+ days no contact = demote)

Campaign Hooks (packages/crm/src/hooks/campaign.hook.ts)

  • ROI calculation: (ActualRevenue - ActualCost) / ActualCost * 100 with infinite ROI handling
  • Budget tracking with warnings at 80% and 100% utilization
  • Performance metrics: conversion rates, cost per lead/opportunity/customer
  • Campaign member bulk management with criteria-based filters

Activity Hooks (packages/crm/src/hooks/activity.hook.ts)

  • Auto-complete overdue tasks (batch job)
  • Related object date updates (Contact.LastContactDate, Account/Opportunity.LastActivityDate)
  • Recurrence management (Daily/Weekly/Monthly/Yearly) with proper month overflow handling
  • Overdue notifications to owners and managers

Technical Implementation

// All hooks use ObjectQL for type-safe data access
const accounts = await db.find('Account', {
  filters: [['CustomerStatus', '=', 'Active']],
  fields: ['Name', 'HealthScore'],
  sort: ['Name']
});

// Proper error handling without blocking saves
try {
  account.HealthScore = await calculateHealthScore(account, ctx);
} catch (error) {
  console.error('❌ Error in hook:', error);
  // Don't throw - allow save to complete
}

Edge Cases Addressed

  • Date validation for recurrence end dates with try-catch
  • Monthly recurrence overflow (Jan 31 + 1 month = Feb 28/29, not Mar 3)
  • getDaysSince returns negative for future dates (not abs value)
  • Infinite ROI when ActualCost = 0 and ActualRevenue > 0 (capped at 999.99%)
  • Null/undefined ActivityDate handling with fallback to current date

Documentation

  • HOOKS_IMPLEMENTATION.md - Technical implementation guide with architecture patterns
  • IMPLEMENTATION_SUMMARY.md - Executive summary with metrics and production checklist

Testing

  • Unit tests pass (npm test)
  • Linting passes (npm run lint)
  • Build succeeds (npm run build)
  • Manual testing completed
  • New tests added (if applicable)

Validation Results:

  • Protocol compliance: 8/8 objects compliant with @objectstack/spec v0.6.1
  • Security scan: 0 vulnerabilities (CodeQL)
  • TypeScript compilation: Success
  • Code review issues: 6/6 addressed

Screenshots

N/A - Backend business logic implementation

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Additional Notes

Current State: Database operations are commented out for safety. Hooks log intended actions but don't modify data. Uncomment db.doc.* and db.find() calls to enable.

Production Deployment:

  1. Enable database operations in hook files
  2. Add scheduled jobs for autoCompletePastDueActivities() and sendOverdueNotifications()
  3. Consider adding LastActivityDate fields to Account/Opportunity objects
  4. Convert Campaign.object.yml to TypeScript for consistency

Metrics: 1,090 LOC across 4 hook files, 10 triggers, 8 helper functions, 2 batch jobs

Original prompt

1.2 Business Logic Implementation (Week 2-3)

Account Hooks (account.hook.ts)

// Key automation scenarios:

  1. Health Score Calculation

    • Based on: Activity frequency, payment history, support cases, contract value
    • Trigger: Daily batch + on significant events
    • Range: 0-100, with color coding
  2. Hierarchy Management

    • Auto-update parent account metrics
    • Cascade ownership changes
    • Aggregate child account values
  3. Customer Status Automation

    • Auto-upgrade Prospect → Customer on first contract
    • Auto-mark Churned when contracts expire
    • Flag at-risk based on health score
  4. Contract Value Rollup

    • Sum all active contract values
    • Update on contract changes
    • Include child accounts if parent
  5. Renewal Date Management

    • Find nearest renewal date from active contracts
    • Set reminder tasks 90/60/30 days before
      Contact Hooks (contact.hook.ts)

// Key automation scenarios:

  1. Last Contact Date Tracking

    • Update on any activity with this contact
    • Trigger: After activity creation/update
  2. Decision Chain Validation

    • Warn if account has no decision maker
    • Auto-set InfluenceLevel based on title/level
    • Track multiple decision makers per account
  3. Duplicate Detection

    • Check email uniqueness across accounts
    • Suggest merge when duplicates found
    • Flag potential duplicates by name + company
  4. Relationship Strength Auto-Update

    • Based on: Activity frequency, email sentiment, deal involvement
    • Auto-promote Weak → Medium → Strong
    • Auto-demote if no contact for 90+ days
      Campaign Hooks (campaign.hook.ts)

// Key automation scenarios:

  1. ROI Calculation

    • Formula: (Revenue from campaign - Cost) / Cost * 100
    • Track: Opportunities created, Leads generated, Revenue won
    • Auto-update on related opportunity/lead changes
  2. Budget Tracking

    • Warn when spent > 80% of budget
    • Block activities when budget exceeded
    • Track actual vs planned spend
  3. Campaign Member Management

    • Auto-add leads/contacts based on criteria
    • Track engagement: Sent, Opened, Clicked, Responded
    • Remove unsubscribed members
  4. Campaign Performance

    • Conversion rates: Lead → Opportunity → Won
    • Response rates by channel
    • Cost per lead/opportunity/customer
      Activity Hooks (activity.hook.ts)

// Key automation scenarios:

  1. Auto-Complete on Due Date

    • Set Status = Completed for past-due tasks
    • Send completion notifications
    • Log to timeline
  2. Related Object Updates

    • Update Account.LastActivityDate
    • Update Contact.LastContactDate
    • Update Opportunity.LastActivityDate
    • Increment activity counters
  3. Overdue Notifications

    • Daily job: Find overdue tasks
    • Notify owner and manager
    • Create follow-up tasks if needed
  4. Recurrence Management

    • Auto-create next occurrence on completion
    • Support: Daily, Weekly, Monthly patterns
    • End date or occurrence count limits

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link

vercel bot commented Jan 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
hotcrm Ready Ready Preview, Comment Jan 29, 2026 11:39am

Request Review

…tivity

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI and others added 2 commits January 29, 2026 11:02
…ases

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…klist

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add business logic for account hooks automation Implement business logic hooks for Account, Contact, Campaign, and Activity objects Jan 29, 2026
Copilot AI requested a review from hotlong January 29, 2026 11:05
@hotlong hotlong marked this pull request as ready for review January 29, 2026 11:19
Copilot AI review requested due to automatic review settings January 29, 2026 11:19
@github-actions github-actions bot added the documentation Improvements or additions to documentation label Jan 29, 2026
/**
* Create reminder tasks for upcoming renewal
*/
async function createRenewalReminderTasks(accountId: string, renewalDate: string, db: typeof import('@hotcrm/core').db): Promise<void> {

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused function createRenewalReminderTasks.

Copilot Autofix

AI about 12 hours ago

In general, unused functions should either be (1) removed when they are not needed, or (2) actually used (called) if they represent required behavior. Here, the implementation inside createRenewalReminderTasks is currently unreachable, and the only mention of it is in commented-out code inside updateAccountRenewalDate. To fix the issue without changing existing functionality, we should remove the unused helper function entirely, and also adjust the surrounding comments so they no longer reference a function that does not exist.

Concretely:

  • In packages/crm/src/hooks/account.hook.ts, delete the entire createRenewalReminderTasks function definition (lines 263–287 in the snippet).
  • Optionally, update the comment in updateAccountRenewalDate that mentions createRenewalReminderTasks so it no longer refers to a removed helper; we can rephrase it generically (“create reminder tasks at 90/60/30 days before renewal”) or drop the specific commented-out call line. This does not alter runtime behavior because that block is entirely commented out already.

No new imports, methods, or definitions are required.


Suggested changeset 1
packages/crm/src/hooks/account.hook.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/crm/src/hooks/account.hook.ts b/packages/crm/src/hooks/account.hook.ts
--- a/packages/crm/src/hooks/account.hook.ts
+++ b/packages/crm/src/hooks/account.hook.ts
@@ -254,38 +254,12 @@
   //   });
   //   
   //   // Create reminder tasks at 90/60/30 days before renewal
-  //   await createRenewalReminderTasks(accountId, nextRenewalDate, db);
+  //   // (implementation would go here in a real system)
   // }
   
   console.log(`✅ Renewal date updated for account ${accountId}`);
 }
 
-/**
- * Create reminder tasks for upcoming renewal
- */
-async function createRenewalReminderTasks(accountId: string, renewalDate: string, db: typeof import('@hotcrm/core').db): Promise<void> {
-  const reminderDays = [90, 60, 30];
-  
-  for (const days of reminderDays) {
-    const reminderDate = new Date(renewalDate);
-    reminderDate.setDate(reminderDate.getDate() - days);
-    
-    if (reminderDate > new Date()) {
-      // In real implementation, would create the task
-      console.log(`📅 Would create reminder task ${days} days before renewal`);
-      // await db.doc.create('Activity', {
-      //   Subject: `Renewal reminder: ${days} days until contract renewal`,
-      //   Type: 'Task',
-      //   Status: 'Planned',
-      //   Priority: days <= 30 ? 'High' : 'Medium',
-      //   WhatId: accountId,
-      //   ActivityDate: reminderDate.toISOString().split('T')[0],
-      //   Description: `Contract renewal is coming up in ${days} days. Please contact the customer to discuss renewal.`
-      // });
-    }
-  }
-}
-
 // Export all hooks
 export {
   AccountHealthScoreTrigger,
EOF
@@ -254,38 +254,12 @@
// });
//
// // Create reminder tasks at 90/60/30 days before renewal
// await createRenewalReminderTasks(accountId, nextRenewalDate, db);
// // (implementation would go here in a real system)
// }

console.log(`✅ Renewal date updated for account ${accountId}`);
}

/**
* Create reminder tasks for upcoming renewal
*/
async function createRenewalReminderTasks(accountId: string, renewalDate: string, db: typeof import('@hotcrm/core').db): Promise<void> {
const reminderDays = [90, 60, 30];

for (const days of reminderDays) {
const reminderDate = new Date(renewalDate);
reminderDate.setDate(reminderDate.getDate() - days);

if (reminderDate > new Date()) {
// In real implementation, would create the task
console.log(`📅 Would create reminder task ${days} days before renewal`);
// await db.doc.create('Activity', {
// Subject: `Renewal reminder: ${days} days until contract renewal`,
// Type: 'Task',
// Status: 'Planned',
// Priority: days <= 30 ? 'High' : 'Medium',
// WhatId: accountId,
// ActivityDate: reminderDate.toISOString().split('T')[0],
// Description: `Contract renewal is coming up in ${days} days. Please contact the customer to discuss renewal.`
// });
}
}
}

// Export all hooks
export {
AccountHealthScoreTrigger,
Copilot is powered by AI and may make mistakes. Always verify output.
}

// Create next occurrence
const nextActivity = {

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused variable nextActivity.

Copilot Autofix

AI about 12 hours ago

In general, unused local variables should either be removed or put to use. Here, the surrounding comment // Create next occurrence strongly suggests the intent is to create and persist the next recurring activity. The safest and most meaningful fix is to call the database API with nextActivity, rather than deleting it.

Detailed fix:

  • After constructing nextActivity, immediately use it in a db.doc.create (or similar) call to actually create the next occurrence in the database.
  • Because we are inside a trigger/hook (and we already have access to db in this file), we can assume the same db instance is in scope within this function. To avoid guessing API names, we should follow the pattern used elsewhere in the same file: other snippets use db.doc.update('Activity', ...). Here we’ll use db.doc.create('Activity', nextActivity) as the minimal, consistent way to “create” a new record.
  • Await this call (the function around this code is already async per the overall trigger design), and optionally log the creation for traceability.

Concretely, within packages/crm/src/hooks/activity.hook.ts, in the block starting at line 222 where nextActivity is declared, add an await db.doc.create('Activity', nextActivity); call and a log line right after the object definition. No new imports or types are required.

Suggested changeset 1
packages/crm/src/hooks/activity.hook.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/crm/src/hooks/activity.hook.ts b/packages/crm/src/hooks/activity.hook.ts
--- a/packages/crm/src/hooks/activity.hook.ts
+++ b/packages/crm/src/hooks/activity.hook.ts
@@ -237,6 +237,10 @@
     RecurrenceEndDate: activity.RecurrenceEndDate,
     RecurrenceInstanceId: activity.RecurrenceInstanceId || activity.Id
   };
+
+  // Persist the next recurring activity occurrence
+  await db.doc.create('Activity', nextActivity);
+  console.log(`🔁 Created next recurring activity instance for ${activity.Subject} on ${nextActivity.DueDate}`);
   
   // await ctx.db.doc.create('Activity', nextActivity);
   console.log(`✅ Created next recurrence for ${nextDate.toISOString().split('T')[0]}`);
EOF
@@ -237,6 +237,10 @@
RecurrenceEndDate: activity.RecurrenceEndDate,
RecurrenceInstanceId: activity.RecurrenceInstanceId || activity.Id
};

// Persist the next recurring activity occurrence
await db.doc.create('Activity', nextActivity);
console.log(`🔁 Created next recurring activity instance for ${activity.Subject} on ${nextActivity.DueDate}`);

// await ctx.db.doc.create('Activity', nextActivity);
console.log(`✅ Created next recurrence for ${nextDate.toISOString().split('T')[0]}`);
Copilot is powered by AI and may make mistakes. Always verify output.
Copilot AI and others added 2 commits January 29, 2026 11:25
…case block

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements comprehensive business logic hooks for the four core CRM objects (Account, Contact, Campaign, and Activity) following the @objectstack/spec v0.6.1 protocol. The implementation provides 20+ automation scenarios including health scoring, hierarchy management, ROI calculation, relationship tracking, and activity recurrence handling.

Changes:

  • Added TypeScript hook files for Account, Contact, Campaign, and Activity objects with automated business logic
  • Implemented helper functions for cross-object updates and batch processing jobs
  • Created comprehensive documentation in HOOKS_IMPLEMENTATION.md and IMPLEMENTATION_SUMMARY.md

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/crm/src/hooks/account.hook.ts Account health scoring, hierarchy management, status automation, contract value rollup, and renewal date tracking
packages/crm/src/hooks/contact.hook.ts Contact date tracking, decision chain validation, duplicate detection, and relationship strength management
packages/crm/src/hooks/campaign.hook.ts Campaign ROI calculation, budget tracking, member management, and performance metrics
packages/crm/src/hooks/activity.hook.ts Activity auto-completion, related object updates, overdue notifications, and recurrence management
IMPLEMENTATION_SUMMARY.md Executive summary with implementation metrics, quality checklist, and production deployment guide
HOOKS_IMPLEMENTATION.md Technical implementation guide with architecture patterns, data access examples, and integration points

}

// Auto-set IsDecisionMaker for C-Level
if (contact.Level === 'C-Level' && !contact.IsDecisionMaker) {
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for checking if IsDecisionMaker should be set has a potential issue. When contact.IsDecisionMaker is false (explicitly set by user), this condition will still override it to true. The condition should check for undefined/null specifically, not just falsy values.

Consider using: if (contact.Level === 'C-Level' && contact.IsDecisionMaker !== true && contact.IsDecisionMaker !== false) or similar logic to avoid overriding explicit user choices.

Suggested change
if (contact.Level === 'C-Level' && !contact.IsDecisionMaker) {
if (
contact.Level === 'C-Level' &&
contact.IsDecisionMaker !== true &&
contact.IsDecisionMaker !== false
) {

Copilot uses AI. Check for mistakes.
const date = new Date(dateString);
const now = new Date();
const diffTime = now.getTime() - date.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Math.ceil for date difference calculation can produce incorrect results. When the time difference is exactly midnight (0 hours), Math.ceil will round up to 1 day when it should be 0 days. For "days since" calculations, Math.floor would be more appropriate to get the complete days elapsed. Alternatively, consider using Math.round if you want half-day precision.

Suggested change
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +84
//
// const convertedLeads = leads.filter(l => l.Status === 'Converted');

// 2. Count opportunities from this campaign
// const opportunities = await db.find('Opportunity', {
// filters: [['CampaignId', '=', campaignId]]
// });
//
// const wonOpportunities = opportunities.filter(o => o.Stage === 'Closed Won');

// 3. Calculate actual revenue from won opportunities
// const actualRevenue = wonOpportunities.reduce((sum, opp) => sum + (opp.Amount || 0), 0);

Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented implementation uses JavaScript array.filter() to count converted leads and won opportunities, which is inefficient for large datasets. Instead, use ObjectQL filters to perform server-side filtering.

For example, replace:
const convertedLeads = leads.filter(l => l.Status === 'Converted');

With:
const convertedLeads = await db.find('Lead', { filters: [['CampaignId', '=', campaignId], ['Status', '=', 'Converted']] });

This applies to both the lead and opportunity queries on lines 73 and 80.

Suggested change
//
// const convertedLeads = leads.filter(l => l.Status === 'Converted');
// 2. Count opportunities from this campaign
// const opportunities = await db.find('Opportunity', {
// filters: [['CampaignId', '=', campaignId]]
// });
//
// const wonOpportunities = opportunities.filter(o => o.Stage === 'Closed Won');
// 3. Calculate actual revenue from won opportunities
// const actualRevenue = wonOpportunities.reduce((sum, opp) => sum + (opp.Amount || 0), 0);
//
// const convertedLeads = await db.find('Lead', {
// filters: [
// ['CampaignId', '=', campaignId],
// ['Status', '=', 'Converted'],
// ],
// });
//
// 2. Count opportunities from this campaign
// const opportunities = await db.find('Opportunity', {
// filters: [['CampaignId', '=', campaignId]]
// });
//
// const wonOpportunities = await db.find('Opportunity', {
// filters: [
// ['CampaignId', '=', campaignId],
// ['Stage', '=', 'Closed Won'],
// ],
// });
//
// 3. Calculate actual revenue from won opportunities
// const actualRevenue = wonOpportunities.reduce((sum, opp) => sum + (opp.Amount || 0), 0);
//

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +10
// Types for Context
interface TriggerContext {
old?: Record<string, any>;
new: Record<string, any>;
db: typeof db;
user: { id: string; name: string; email: string; };
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TriggerContext interface is duplicated across multiple hook files (account, contact, campaign, activity). This creates maintenance burden and potential inconsistencies. The interface should be defined once in a shared location (e.g., packages/core/src/types.ts or packages/crm/src/types.ts) and imported by all hook files.

Looking at opportunity.hook.ts, it exports TriggerContext, while other files define it as a non-exported interface. This inconsistency should be resolved by centralizing the definition.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +11

// Types for Context
interface TriggerContext {
old?: Record<string, any>;
new: Record<string, any>;
db: typeof db;
user: { id: string; name: string; email: string; };
}

Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TriggerContext interface is duplicated across multiple hook files. This creates maintenance burden and potential inconsistencies. The interface should be defined once in a shared location and imported by all hook files to ensure consistency.

Suggested change
// Types for Context
interface TriggerContext {
old?: Record<string, any>;
new: Record<string, any>;
db: typeof db;
user: { id: string; name: string; email: string; };
}
import type { TriggerContext } from '@hotcrm/core';

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +10
// Types for Context
interface TriggerContext {
old?: Record<string, any>;
new: Record<string, any>;
db: typeof db;
user: { id: string; name: string; email: string; };
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TriggerContext interface is duplicated across multiple hook files. This creates maintenance burden and potential inconsistencies. The interface should be defined once in a shared location and imported by all hook files to ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +10
// Types for Context
interface TriggerContext {
old?: Record<string, any>;
new: Record<string, any>;
db: typeof db;
user: { id: string; name: string; email: string; };
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TriggerContext interface is duplicated across multiple hook files. This creates maintenance burden and potential inconsistencies. The interface should be defined once in a shared location and imported by all hook files to ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +257 to +288
// await createRenewalReminderTasks(accountId, nextRenewalDate, db);
// }

console.log(`✅ Renewal date updated for account ${accountId}`);
}

/**
* Create reminder tasks for upcoming renewal
*/
async function createRenewalReminderTasks(accountId: string, renewalDate: string, db: typeof import('@hotcrm/core').db): Promise<void> {
const reminderDays = [90, 60, 30];

for (const days of reminderDays) {
const reminderDate = new Date(renewalDate);
reminderDate.setDate(reminderDate.getDate() - days);

if (reminderDate > new Date()) {
// In real implementation, would create the task
console.log(`📅 Would create reminder task ${days} days before renewal`);
// await db.doc.create('Activity', {
// Subject: `Renewal reminder: ${days} days until contract renewal`,
// Type: 'Task',
// Status: 'Planned',
// Priority: days <= 30 ? 'High' : 'Medium',
// WhatId: accountId,
// ActivityDate: reminderDate.toISOString().split('T')[0],
// Description: `Contract renewal is coming up in ${days} days. Please contact the customer to discuss renewal.`
// });
}
}
}

Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused function createRenewalReminderTasks.

Suggested change
// await createRenewalReminderTasks(accountId, nextRenewalDate, db);
// }
console.log(`✅ Renewal date updated for account ${accountId}`);
}
/**
* Create reminder tasks for upcoming renewal
*/
async function createRenewalReminderTasks(accountId: string, renewalDate: string, db: typeof import('@hotcrm/core').db): Promise<void> {
const reminderDays = [90, 60, 30];
for (const days of reminderDays) {
const reminderDate = new Date(renewalDate);
reminderDate.setDate(reminderDate.getDate() - days);
if (reminderDate > new Date()) {
// In real implementation, would create the task
console.log(`📅 Would create reminder task ${days} days before renewal`);
// await db.doc.create('Activity', {
// Subject: `Renewal reminder: ${days} days until contract renewal`,
// Type: 'Task',
// Status: 'Planned',
// Priority: days <= 30 ? 'High' : 'Medium',
// WhatId: accountId,
// ActivityDate: reminderDate.toISOString().split('T')[0],
// Description: `Contract renewal is coming up in ${days} days. Please contact the customer to discuss renewal.`
// });
}
}
}
// // This logic can be implemented here in the future if needed.
// }
console.log(`✅ Renewal date updated for account ${accountId}`);
}

Copilot uses AI. Check for mistakes.
}

// Create next occurrence
const nextActivity = {
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable nextActivity.

Copilot uses AI. Check for mistakes.
@hotlong hotlong merged commit 2f9c3cb into main Jan 29, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants