Skip to main content

Golf Cart Notifications

The golf cart management system integrates with the messaging service to provide automated notifications for operational events, maintenance alerts, incidents, and player communications.

Architecture

┌─────────────────────────┐     ┌─────────────────────────┐
│ GolfCartService │────▶│ GolfCartNotification │
│ (Domain Logic) │ │ Service │
└─────────────────────────┘ └───────────┬─────────────┘


┌─────────────────────────┐ ┌─────────────────────────┐
│ GolfCartNotification │────▶│ NotificationEmitter │
│ Processor (@Cron) │ │ (BullMQ Jobs) │
└─────────────────────────┘ └───────────┬─────────────┘


┌─────────────────────────┐
│ ContactPreferenceService│
│ (Channel Filtering) │
└─────────────────────────┘

Notification Types (13 total)

Operational Alerts

TypeDescriptionChannels
golf_cart_low_batteryCart battery below 20% thresholdEMAIL, SMS, PUSH, WHATSAPP
golf_cart_out_of_serviceCart marked as out of serviceEMAIL
golf_cart_not_returnedAssignment overdue (configurable hours)EMAIL
golf_cart_zone_anomalyCart detected outside allowed zonesSMS

Maintenance Notifications

TypeDescriptionChannels
golf_cart_maintenance_dueCart approaching service date/odometerEMAIL
golf_cart_maintenance_scheduledMaintenance scheduled for cartEMAIL
golf_cart_service_completedService completed on cartEMAIL
golf_cart_predictive_alertUsage patterns indicate service neededEMAIL

Incident Notifications

TypeDescriptionChannels
golf_cart_incident_reportedNew incident logged for cartEMAIL, SMS, WHATSAPP
golf_cart_incident_resolvedIncident resolvedEMAIL

Player-Facing Notifications

TypeDescriptionChannels
golf_cart_assignedCart assigned to bookingEMAIL, PUSH, WHATSAPP
golf_cart_return_reminderReminder to return cartSMS, PUSH, WHATSAPP
golf_cart_return_confirmedCart return acknowledgedEMAIL, PUSH

Service Integration

GolfCartNotificationService

Located at: libs/facilities/facilities-service/src/lib/golf-cart-notification.service.ts

@Injectable()
export class GolfCartNotificationService {
constructor(
@Optional() @Inject(NotificationEmitter) private readonly emitter?: NotificationEmitter,
@Optional() @Inject(ContactPreferenceService) private readonly preferences?: ContactPreferenceService,
) {}

// 11 notification methods
async notifyLowBattery(cart, tenantId, recipients): Promise<void>
async notifyCartAssigned(cart, assignment, tenantId, recipients): Promise<void>
async notifyIncidentReported(cart, incident, tenantId, recipients): Promise<void>
async notifyMaintenanceScheduled(cart, log, tenantId, recipients): Promise<void>
async notifyMaintenanceDue(cart, tenantId, recipients): Promise<void>
async notifyServiceCompleted(cart, tenantId, recipients): Promise<void>
async notifyCartNotReturned(cart, assignment, tenantId, recipients): Promise<void>
async notifyReturnReminder(cart, assignment, tenantId, recipients): Promise<void>
async notifyIncidentResolved(cart, incident, tenantId, recipients): Promise<void>
async notifyOutOfService(cart, tenantId, recipients): Promise<void>
async notifyReturnConfirmed(cart, tenantId, recipients): Promise<void>
}

Scheduled Jobs (Processor)

Located at: libs/facilities/facilities-service/src/lib/golf-cart-notification.processor.ts

@Injectable()
export class GolfCartNotificationProcessor {
@Cron(CronExpression.EVERY_10_MINUTES)
async checkOverdueAssignments(): Promise<void>

@Cron(CronExpression.EVERY_10_MINUTES)
async checkReturnReminders(): Promise<void>

@Cron(CronExpression.EVERY_DAY_AT_1AM)
async checkPredictiveMaintenance(): Promise<void>
}

Configuration

Environment Variables

VariableDescriptionDefault
GOLF_CART_NOTIFICATION_TENANTSComma-separated tenant IDs to process(empty = disabled)
GOLF_CART_OVERDUE_HOURSHours before cart considered overdue6
GOLF_CART_REMINDER_HOURSHours before return reminder sent5.5

Example Configuration

GOLF_CART_NOTIFICATION_TENANTS=1,2,5
GOLF_CART_OVERDUE_HOURS=6
GOLF_CART_REMINDER_HOURS=5.5

Template Seeding

Templates are seeded via NestJS seeder script:

MESSAGING_DATABASE_URL=postgres://... pnpm -s dlx tsx \
tools/scripts/messaging/seeds/seed-golf-cart-notifications.ts

Optional Environment Variables

VariableDescriptionDefault
SEED_TENANTTenant ID for templatesglobal
SEED_LOCALELocale for translationsen-US

Template Count

ChannelTemplates
EMAIL11
SMS4
PUSH4
WHATSAPP4
Total21

Trigger Points (GolfCartService)

The following service methods trigger notifications:

MethodNotification
updateBattery()notifyLowBattery (if < 20%)
updateCondition()notifyOutOfService (if OUT_OF_SERVICE)
assignCart()notifyCartAssigned
returnCart()notifyReturnConfirmed
reportIncident()notifyIncidentReported
resolveIncident()notifyIncidentResolved
scheduleMaintenance()notifyMaintenanceScheduled
markServiced()notifyServiceCompleted

Preference Filtering

All notifications respect user channel preferences via ContactPreferenceService.isAllowed():

const mapped = CHANNEL_MAP[channel]; // EMAIL → 'email', SMS → 'sms', etc.
if (!(await this.preferences.isAllowed(userId, mapped, notificationType))) {
// Skip sending to this channel
}

Graceful Degradation

If NotificationEmitter or ContactPreferenceService are not configured, notifications are silently skipped:

private shouldSend(): boolean {
if (!this.emitter || !this.preferences) {
this.logger.debug('Golf cart notifications disabled: emitter or preferences not configured.');
return false;
}
return true;
}

Testing

Unit tests: libs/facilities/facilities-service/src/lib/__tests__/unit/golf-cart-notification.service.spec.ts

pnpm nx run facilities-service:test:unit --testPathPattern=golf-cart-notification