Saltar al contenido principal

Communication Recipient Targeting - Changes Summary

What Changes?

Current Structure (Before)

business_communication_config
├── Controls: WHAT and HOW (type + channel + timing)
└── Recipients: NONE (hardcoded in handler code)

Problem: To change recipients, you need to modify code and redeploy!

New Structure (After)

business_communication_config
├── Controls: WHAT and HOW (type + channel + timing)
└── Extended by: business_communication_recipient_rule
└── Controls: WHO (roles + groups + ad-hoc)

Solution: Business admins configure recipients via admin UI - no code changes needed!

New Tables Overview

1. communication_recipient_group

Purpose: Create custom groups of recipients

ColumnDescriptionExample
nameGroup name"Finance Team", "Purchasing Dept"
descriptionPurpose"Team handling all financial reports"
is_activeEnable/disabletrue/false

Use Case: Create a "Purchasing Team" group containing 3 staff members + 1 supplier email


2. communication_group_member

Purpose: Define who's in each group

ColumnTypeExample
member_typeenumbusiness_user, email, phone, whatsapp
business_user_iduuidLink to existing user
email_addressvarcharexternal@supplier.com
phone_numbervarchar+1234567890

Use Case: Add internal users + external contacts to the same group


3. business_communication_recipient_rule

Purpose: Define WHO receives each communication type/channel

Targeting TypeExampleWhat It Does
role"inventory_manager"Send to ALL users with this role
group"Purchasing Team"Send to ALL members in this group
ad_hoc_email"supplier@vendor.com"Send to this specific email
ad_hoc_phone"+1234567890"Send to this specific phone

Use Case: Configure low_stock_alert to go to: inventory_manager role + Purchasing Team group + owner's phone


4. communication_recipient_log

Purpose: Audit trail of recipient selection

Use Case: See who was selected as recipients and why (for compliance/debugging)


Configuration Examples

Scenario 1: Low Stock Alerts

Current: Hardcoded to send to ALL business users ❌

New: Configure via rules ✅

-- Rule 1: Send to inventory managers
INSERT INTO business_communication_recipient_rule
VALUES (..., 'low_stock_alert', 'email', 'role', 'inventory_manager');

-- Rule 2: Send to purchasing team
INSERT INTO business_communication_recipient_rule
VALUES (..., 'low_stock_alert', 'email', 'group', 'purchasing-team-uuid');

-- Rule 3: SMS to owner's mobile
INSERT INTO business_communication_recipient_rule
VALUES (..., 'low_stock_alert', 'sms', 'ad_hoc_phone', '+1234567890');

Result:

  • Email → All inventory managers + purchasing team members
  • SMS → Owner's phone
  • Configurable via admin UI (no code deploy needed!)

Scenario 2: Daily Reports

Current: Would need custom code for each business ❌

New: Each business configures their own ✅

Business A:

-- Send to finance group only
INSERT INTO business_communication_recipient_rule
VALUES ('business-A', 'daily_report', 'email', 'group', 'finance-group');

Business B:

-- Send to owner + admin + external accountant
INSERT INTO business_communication_recipient_rule
VALUES ('business-B', 'daily_report', 'email', 'role', 'owner'),
('business-B', 'daily_report', 'email', 'role', 'admin'),
('business-B', 'daily_report', 'email', 'ad_hoc_email', 'cpa@firm.com');

How It Works

Before (Current Handler)

// ❌ Hardcoded: Send to ALL business users
const businessUsers = await this.businessUsersService.findByBusinessId(businessId);

for (const user of businessUsers) {
await this.communicationsService.send({ recipientContact: user.email });
}

After (With RecipientResolverService)

// ✅ Dynamic: Resolve recipients based on business rules
const recipients = await this.recipientResolverService.resolveRecipients(
businessId,
'low_stock_alert',
'email'
);

for (const recipient of recipients) {
await this.communicationsService.send({
recipientContact: recipient.contact,
// Audit: Track why this person was selected
metadata: { selectionSource: recipient.source }
});
}

RecipientResolverService logic:

  1. Query all active rules for this business + type + channel
  2. For each rule, resolve to actual recipients:
    • Role rule → Query all users with that role
    • Group rule → Query all group members
    • Ad-hoc rule → Use the email/phone directly
  3. Deduplicate (same person via multiple rules)
  4. Return final list of recipients

Migration Strategy

Phase 1: Database (No Breaking Changes)

✅ Run migration to create new tables ✅ Existing handlers continue to work (no changes yet)

Phase 2: Create Services

  • RecipientGroupRepository
  • RecipientRuleRepository
  • RecipientResolverService

Phase 3: Create APIs

  • Group management endpoints
  • Recipient rule management endpoints
  • Preview endpoint (see who will receive before saving)

Phase 4: Update Handlers

Update event handlers one at a time:

  1. Low stock alerts (migrate first)
  2. Daily reports
  3. Invoices
  4. Collection notices ... etc

Phase 5: Admin UI

  • Group management page
  • Recipient rule configuration page
  • Integration with communication settings

Key Benefits

BenefitDescription
FlexibilityMix roles, groups, and ad-hoc recipients
Self-ServiceBusiness admins configure without developer help
Audit TrailKnow exactly who received what and why
ScalabilityAdd new communication types without code changes
Multi-TenantEach business has different recipient rules
No Code DeployChange recipients via admin UI, not code

Comparison Table

FeatureBeforeAfter
Recipient ConfigurationHardcoded in handlersDatabase-driven rules
Change RecipientsCode change + deployAdmin UI (instant)
Send to Roles❌ No✅ Yes
Send to Groups❌ No✅ Yes
Send to External Contacts❌ No✅ Yes
Mix Multiple Types❌ No✅ Yes (roles + groups + ad-hoc)
Per-Business Config❌ No (all businesses same)✅ Yes (each configures their own)
Audit Trail❌ No✅ Yes (recipient_log table)
Preview Recipients❌ No✅ Yes (via API)

Example API Calls

Create a Group

POST /api/recipient-groups
{
"businessId": "business-123",
"name": "Finance Team",
"description": "Team handling financial reports"
}

Add Members to Group

POST /api/recipient-groups/{groupId}/members
{
"members": [
{ "type": "business_user", "userId": "user-1" },
{ "type": "business_user", "userId": "user-2" },
{ "type": "email", "email": "cpa@external.com", "name": "External CPA" }
]
}

Create Recipient Rules

POST /api/communication-recipient-rules
{
"businessId": "business-123",
"communicationType": "low_stock_alert",
"channel": "email",
"rules": [
{ "targetingType": "role", "roleName": "inventory_manager" },
{ "targetingType": "group", "groupId": "group-456" },
{ "targetingType": "ad_hoc_phone", "phone": "+1234567890", "name": "Owner Mobile" }
]
}

Preview Recipients (Before Saving)

POST /api/communication-recipient-rules/preview
{
"businessId": "business-123",
"communicationType": "low_stock_alert",
"channel": "email",
"rules": [...]
}

// Response:
{
"recipients": [
{ "name": "John Doe", "contact": "john@company.com", "source": "role:inventory_manager" },
{ "name": "Jane Smith", "contact": "jane@company.com", "source": "group:Purchasing Team" },
{ "name": "External Supplier", "contact": "supplier@vendor.com", "source": "group:Purchasing Team" },
{ "name": "Owner Mobile", "contact": "+1234567890", "source": "ad_hoc" }
],
"totalCount": 4
}

Decision Matrix: When to Use What

ScenarioUse This
Send to everyone with a specific job functionRole (e.g., all managers)
Send to a specific team across locationsGroup (e.g., Finance Team)
Send to external partyAd-hoc email/phone
Send to mix of internal + externalGroup with mixed members
Send to specific person's backup contactAd-hoc (personal phone/email)
Different recipients per businessRecipient rules (per business)
Different recipients per channelMultiple rules (one per channel)

Summary

The recipient targeting system extends your existing business_communication_config with flexible recipient rules. Instead of hardcoding recipients in event handlers, businesses now configure their own rules via admin UI.

The key insight:

  • business_communication_config = WHAT to send and HOW to send it
  • business_communication_recipient_rule = WHO should receive it

This separation allows maximum flexibility while maintaining backward compatibility and audit trails.