Skip to content
Merged
7 changes: 3 additions & 4 deletions packages/crm/src/hooks/account.hook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Hook, HookContext } from '@objectstack/spec/data';
import { db } from '../db';



Expand All @@ -14,7 +13,7 @@ import { db } from '../db';
*/
const AccountHealthScoreTrigger: Hook = {
name: 'AccountHealthScoreTrigger',
object: 'Account',
object: 'account',
events: ['beforeInsert', 'beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -92,7 +91,7 @@ async function calculateHealthScore(account: Record<string, any>, ctx: any): Pro
*/
const AccountHierarchyTrigger: Hook = {
name: 'AccountHierarchyTrigger',
object: 'Account',
object: 'account',
events: ['afterInsert', 'afterUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -172,7 +171,7 @@ async function cascadeOwnershipChange(accountId: string, newOwnerId: string, ctx
*/
const AccountStatusAutomationTrigger: Hook = {
name: 'AccountStatusAutomationTrigger',
object: 'Account',
object: 'account',
events: ['beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down
11 changes: 5 additions & 6 deletions packages/crm/src/hooks/activity.hook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Hook, HookContext } from '@objectstack/spec/data';
import { db } from '../db';



Expand All @@ -9,7 +8,7 @@ import { db } from '../db';
* Automatically completes past-due activities
* This would typically run as a daily batch job
*/
export async function autoCompletePastDueActivities(db: any): Promise<void> {
export async function autoCompletePastDueActivities(ql: any): Promise<void> {
console.log('🔄 Running auto-complete for past-due activities...');

// In real implementation:
Expand Down Expand Up @@ -44,7 +43,7 @@ export async function autoCompletePastDueActivities(db: any): Promise<void> {
*/
const ActivityRelatedObjectUpdatesTrigger: Hook = {
name: 'ActivityRelatedObjectUpdatesTrigger',
object: 'Activity',
object: 'activity',
events: ['afterInsert', 'afterUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -127,7 +126,7 @@ async function updateWhatObjectLastActivityDate(whatId: string, activityDate: st
*/
const ActivityCompletionTrigger: Hook = {
name: 'ActivityCompletionTrigger',
object: 'Activity',
object: 'activity',
events: ['beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -241,7 +240,7 @@ async function createNextRecurrence(activity: Record<string, any>, ctx: any): Pr
*
* Daily job to find and notify about overdue activities
*/
export async function sendOverdueNotifications(db: any): Promise<void> {
export async function sendOverdueNotifications(ql: any): Promise<void> {
console.log('🔄 Finding overdue activities...');

// In real implementation:
Expand Down Expand Up @@ -286,7 +285,7 @@ export async function sendOverdueNotifications(db: any): Promise<void> {
*/
const ActivityTypeValidationTrigger: Hook = {
name: 'ActivityTypeValidationTrigger',
object: 'Activity',
object: 'activity',
events: ['beforeInsert', 'beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down
13 changes: 6 additions & 7 deletions packages/crm/src/hooks/contact.hook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Hook, HookContext } from '@objectstack/spec/data';
import { db } from '../db';



Expand All @@ -9,7 +8,7 @@ import { db } from '../db';
* Updates LastContactDate when activities are created/updated
* This hook is actually called from Activity hooks
*/
export async function updateContactLastContactDate(contactId: string, activityDate: string, db: any): Promise<void> {
export async function updateContactLastContactDate(contactId: string, activityDate: string, ql: any): Promise<void> {
console.log(`🔄 Updating last contact date for contact: ${contactId}`);

// Get current contact to check if update is needed
Expand All @@ -34,7 +33,7 @@ export async function updateContactLastContactDate(contactId: string, activityDa
*/
const ContactDecisionChainTrigger: Hook = {
name: 'ContactDecisionChainTrigger',
object: 'Contact',
object: 'contact',
events: ['beforeInsert', 'beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -85,7 +84,7 @@ const ContactDecisionChainTrigger: Hook = {
*/
const ContactDecisionMakerValidationTrigger: Hook = {
name: 'ContactDecisionMakerValidationTrigger',
object: 'Contact',
object: 'contact',
events: ['afterInsert', 'afterUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -126,7 +125,7 @@ const ContactDecisionMakerValidationTrigger: Hook = {
*/
const ContactDuplicateDetectionTrigger: Hook = {
name: 'ContactDuplicateDetectionTrigger',
object: 'Contact',
object: 'contact',
events: ['beforeInsert', 'beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -174,7 +173,7 @@ const ContactDuplicateDetectionTrigger: Hook = {
*/
const ContactRelationshipStrengthTrigger: Hook = {
name: 'ContactRelationshipStrengthTrigger',
object: 'Contact',
object: 'contact',
events: ['beforeUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -236,7 +235,7 @@ function getDaysSince(dateString: string): number {
* Update relationship strength based on activity analysis
* This is called periodically or after significant activity changes
*/
export async function analyzeAndUpdateRelationshipStrength(contactId: string, db: any): Promise<void> {
export async function analyzeAndUpdateRelationshipStrength(contactId: string, ql: any): Promise<void> {
console.log(`🔄 Analyzing relationship strength for contact: ${contactId}`);

// In real implementation:
Expand Down
24 changes: 12 additions & 12 deletions packages/crm/src/hooks/lead.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ async function calculateLeadScore(lead: Lead, ctx: HookContext): Promise<number>
*/
async function runAssignmentRules(lead: Lead, ctx: HookContext): Promise<void> {
try {
const rules: any[] = await (ctx.ql as any).find('assignment_rule', {
const rules: any[] = await ctx.ql.find('assignment_rule', {
filters: [
['object_name', '=', 'lead'],
['active', '=', true]
Expand Down Expand Up @@ -318,14 +318,14 @@ const LeadStatusChangeTrigger: Hook = {
// Log activity
try {
await (ctx.ql as any).doc.create('activity', {
Subject: `线索已转化: ${lead.FirstName} ${lead.LastName}`,
Subject: `Lead Converted: ${lead.FirstName} ${lead.LastName}`,
Type: 'Conversion',
Status: 'Completed',
Priority: 'high',
WhoId: lead.Id,
OwnerId: ctx.session?.userId,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `线索 "${lead.FirstName} ${lead.LastName}" 来自 "${lead.Company}" 已成功转化`
Description: `Lead "${lead.FirstName} ${lead.LastName}" from "${lead.Company}" successfully converted`
});
} catch (error) {
console.error('❌ Failed to log conversion activity:', error);
Comment on lines 318 to 331
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

These statements reference ctx but (in the current file) they appear to sit under a malformed JSDoc header rather than inside a real function declaration. This will break parsing/compilation—wrap this block in a properly declared function or remove the duplicate block.

Copilot uses AI. Check for mistakes.
Expand All @@ -342,15 +342,15 @@ async function handleLeadConversion(ctx: HookContext): Promise<void> {

// Log activity for conversion
try {
await (ctx.ql as any).doc.create('activity', {
Subject: `线索转换: ${lead.FirstName} ${lead.LastName}`,
await ctx.ql.doc.create('activity', {
Subject: `Lead Conversion: ${lead.FirstName} ${lead.LastName}`,
Type: 'Conversion',
Status: 'Completed',
Priority: 'high',
WhoId: lead.Id,
OwnerId: ctx.session?.userId,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `线索 "${lead.FirstName} ${lead.LastName}" 已转换为客户`
Description: `Lead "${lead.FirstName} ${lead.LastName}" converted to customer`
});
} catch (error) {
console.error('❌ Failed to log conversion activity:', error);
Expand All @@ -371,15 +371,15 @@ async function handleLeadUnqualification(ctx: HookContext): Promise<void> {

// Log activity
try {
await (ctx.ql as any).doc.create('activity', {
Subject: `线索不合格: ${lead.FirstName} ${lead.LastName}`,
await ctx.ql.doc.create('activity', {
Subject: `Lead Unqualified: ${lead.FirstName} ${lead.LastName}`,
Type: 'Disqualification',
Status: 'Completed',
Priority: 'Normal',
WhoId: lead.Id,
OwnerId: ctx.session?.userId,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `线索 "${lead.FirstName} ${lead.LastName}" 已标记为不合格`
Description: `Lead "${lead.FirstName} ${lead.LastName}" marked as unqualified`
});
} catch (error) {
console.error('❌ Failed to log unqualification activity:', error);
Expand All @@ -394,15 +394,15 @@ async function logStatusChange(ctx: HookContext): Promise<void> {
const lead = ctx.input;
const oldStatus = ctx.previous?.Status || 'Unknown';

await (ctx.ql as any).doc.create('activity', {
Subject: `线索状态变更: ${oldStatus} → ${ctx.input.Status}`,
await ctx.ql.doc.create('activity', {
Subject: `Lead Status Change: ${oldStatus} → ${ctx.input.Status}`,
Type: 'Status Change',
Status: 'Completed',
Priority: 'Normal',
WhoId: lead.Id,
OwnerId: ctx.session?.userId,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `线索状态从 "${oldStatus}" 变更为 "${ctx.input.Status}"`
Description: `Lead status changed from "${oldStatus}" to "${ctx.input.Status}"`
});
} catch (error) {
console.error('❌ Failed to log status change activity:', error);
Expand Down
29 changes: 14 additions & 15 deletions packages/crm/src/hooks/opportunity.hook.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Hook, HookContext } from '@objectstack/spec/data';
import { db } from '../db';



const OpportunityValidation: Hook = {
name: 'OpportunityValidation',
object: 'Opportunity',
object: 'opportunity',
events: ['beforeUpdate', 'beforeInsert'],
handler: async (ctx: HookContext) => {
const opp = ctx.input.doc as Record<string, any>;
Expand Down Expand Up @@ -39,7 +38,7 @@ const OpportunityValidation: Hook = {
*/
const OpportunityStageChange: Hook = {
name: 'OpportunityStageChange',
object: 'Opportunity',
object: 'opportunity',
events: ['afterUpdate'],
handler: async (ctx: HookContext) => {
try {
Expand Down Expand Up @@ -97,7 +96,7 @@ async function handleClosedWon(ctx: any): Promise<void> {
// 1. Create Contract
let contractId;
try {
const contract = await ctx.db.doc.create('Contract', {
const contract = await ctx.ql.doc.create('contract', {
AccountId: opportunity.AccountId,
OpportunityId: opportunity.Id,
Status: 'Draft',
Expand All @@ -119,7 +118,7 @@ async function handleClosedWon(ctx: any): Promise<void> {

// 2. Update Account Status
try {
await ctx.db.doc.update('Account', opportunity.AccountId, {
await ctx.ql.doc.update('account', opportunity.AccountId, {
CustomerStatus: 'Active Customer'
});
console.log('✅ Account status updated to Active Customer');
Expand All @@ -130,16 +129,16 @@ async function handleClosedWon(ctx: any): Promise<void> {

// 3. Log activity
try {
await ctx.db.doc.create('Activity', {
Subject: `商机成交: ${opportunity.Name}`,
await ctx.ql.doc.create('activity', {
Subject: `Deal Won: ${opportunity.Name}`,
Type: 'Milestone',
Status: 'Completed',
Priority: 'high',
AccountId: opportunity.AccountId,
WhatId: opportunity.Id,
OwnerId: ctx.user.id,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `商机 "${opportunity.Name}" 已成功成交,金额: ${opportunity.Amount?.toLocaleString() || 0}`
Description: `Opportunity "${opportunity.Name}" was successfully closed, amount: ${opportunity.Amount?.toLocaleString() || 0}`
});
console.log('✅ Activity logged for Closed Won');
} catch (error) {
Expand Down Expand Up @@ -171,16 +170,16 @@ async function handleClosedLost(ctx: any): Promise<void> {

// Log activity for lost opportunity
try {
await ctx.db.doc.create('Activity', {
Subject: `商机丢失: ${opportunity.Name}`,
await ctx.ql.doc.create('activity', {
Subject: `Deal Lost: ${opportunity.Name}`,
Type: 'Milestone',
Status: 'Completed',
Priority: 'Normal',
AccountId: opportunity.AccountId,
WhatId: opportunity.Id,
OwnerId: ctx.user.id,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `商机 "${opportunity.Name}" 已丢失,金额: ${opportunity.Amount?.toLocaleString() || 0}。原因待分析。`
Description: `Opportunity "${opportunity.Name}" was lost, amount: ${opportunity.Amount?.toLocaleString() || 0}. Reason pending analysis.`
});
console.log('✅ Activity logged for Closed Lost');
} catch (error) {
Expand All @@ -197,16 +196,16 @@ async function logStageChange(ctx: any): Promise<void> {
try {
const opportunity = ctx.result;
const oldStage = ctx.previous?.Stage || 'Unknown';
await ctx.db.doc.create('Activity', {
Subject: `商机阶段变更: ${oldStage} → ${ctx.result.Stage}`,
await ctx.ql.doc.create('activity', {
Subject: `Opportunity Stage Change: ${oldStage} → ${ctx.result.Stage}`,
Type: 'Stage Change',
Status: 'Completed',
Priority: 'Normal',
AccountId: opportunity.AccountId,
WhatId: opportunity.Id,
OwnerId: ctx.user.id,
ActivityDate: new Date().toISOString().split('T')[0],
Description: `商机阶段从 "${oldStage}" 变更为 "${ctx.result.Stage}"`
Description: `Opportunity stage changed from "${oldStage}" to "${ctx.result.Stage}"`
});
} catch (error) {
console.error('❌ Failed to log stage change activity:', error);
Expand Down Expand Up @@ -259,7 +258,7 @@ async function countRelatedQuotes(ctx: any, opportunityId: string): Promise<numb
// In a real monorepo with strict boundaries, we might use a decoupled service.
// Here we assume the broker can find 'quote' across packages.
// Mocking for now since we don't have the full runtime
const quotes = await ctx.db.find('quote', {
const quotes = await ctx.ql.find('quote', {
filters: [['opportunity', '=', opportunityId]]
});
return quotes.length;
Expand Down
Loading
Loading