Appearance
Birthday & Points Emails
Flow ID: SY-12 | Module(s): job, eshop | Complexity: Medium
Business Overview
Two customer engagement email jobs drive loyalty retention:
- AdvSentMailToCustomerBirthday: Sends birthday greeting emails with optional bonus loyalty points. Targets customers whose birthday falls on the current date, with a configurable repeat interval to avoid sending too frequently.
- AdvSentMailToCustomerRemainingPoints: Reminds customers about their available loyalty points balance, targeting those with points above a minimum threshold who have recent order activity. Encourages point redemption and repeat purchases.
Both jobs track their sends via shop_customer_campaign to enforce configurable repeat intervals.
Architecture
AdvSentMailToCustomerBirthday AdvSentMailToCustomerRemainingPoints
(gated by EMAIL_FOR_BIRTHDAY) (gated by POINT_SYSTEM + EMAIL_FOR_TOTAL_POINTS)
| |
v v
customer_model customer_model
getCustomersForBirthday() getCustomersForRemainingPoints()
| |
v v
For each customer: For each customer:
adv_mailer::sentBirthdayWishes() adv_mailer::sentRemainingPoints()
+ optional loyalty::savePointsToCustomer() (includes point-to-cash conversion)
+ customer_campaign_model:: + customer_campaign_model::
saveCustomerCampaign(BIRTHDAY) saveCustomerCampaign(POINTS_REMAINING)Key Components
| Component | Path | Role |
|---|---|---|
AdvSentMailToCustomerBirthday | ecommercen/job/libraries/AdvSentMailToCustomerBirthday.php | Job: birthday email + optional points |
AdvSentMailToCustomerRemainingPoints | ecommercen/job/libraries/AdvSentMailToCustomerRemainingPoints.php | Job: points reminder email |
Adv_customer_model | ecommercen/eshop/models/Adv_customer_model.php | Model: birthday/points customer queries |
Adv_customer_campaign_model | ecommercen/eshop/models/Adv_customer_campaign_model.php | Model: campaign tracking (prevents duplicates) |
Adv_mailer | application/models/Adv_mailer.php | Mailer: birthday wishes and points reminder emails |
Adv_loyalty | ecommercen/libraries/Adv_loyalty.php | Library: point calculation and cash conversion |
| Client stubs | application/modules/job/libraries/SentMailToCustomerBirthday.php, SentMailToCustomerRemainingPoints.php | Empty extension points |
Code Flow
AdvSentMailToCustomerBirthday
- Feature gate: Checks
EMAIL_FOR_BIRTHDAY.IS_ENABLEDregistry value. Returns immediately if disabled. - Points calculation: If
POINT_SYSTEM.IS_ENABLEDis true, readsEMAIL_FOR_BIRTHDAY.POINTS_TO_ADDfor bonus birthday points. Otherwise,pointsToAdd = 0. - Customer query:
customer_model->getCustomersForBirthday($repeat):- Reads
EMAIL_FOR_BIRTHDAY.REPEATregistry value (default: 12 months). - Builds exclusion subquery: customers who already received a BIRTHDAY campaign within the repeat interval.
- Query conditions:
is_guest = false,is_sanitized = false. - Birthday match: Uses
DATE_ADD(birthdate, INTERVAL ... YEAR)to compute next birthday andHAVING next_birthday = TODAY.
- Reads
- Per-customer processing:
- Email:
adv_mailer->sentBirthdayWishes($customerId, $pointsToAdd):- Fetches full customer record.
- If
pointsToAdd > 0: Awards bonus points vialoyalty->savePointsToCustomer()and includes point/cash values in email data. - Sends email using
birthdayWishestemplate with personalized subject (EMAIL_SUBJECTS.BIRTHDAY_WISHESwith customer name).
- Tracking:
customer_campaign_model->saveCustomerCampaign($customerId, BIRTHDAY)inserts a record with current timestamp.
- Email:
AdvSentMailToCustomerRemainingPoints
- Feature gates (two checks):
POINT_SYSTEM.IS_ENABLEDmust be true.EMAIL_FOR_TOTAL_POINTS.IS_ENABLEDmust be true.
- Configuration: Reads from
EMAIL_FOR_TOTAL_POINTSregistry group:TOTAL_POINTS: Minimum points threshold (returns empty if <= 0).INTERVAL: Months of recent order activity required (0 = no filter).INTERVAL_REPEAT: Months before sending again to the same customer.
- Customer query:
customer_model->getCustomersForRemainingPoints($fromPoints, $interval, $intervalRepeat):- Conditions:
is_guest = false,is_sanitized = false,total_points > $fromPoints. - Excludes customers with orders within the interval period (targets inactive customers with points to spend).
- Excludes customers who received a POINTS_REMAINING campaign within the repeat interval.
- Conditions:
- Per-customer processing:
- Email:
adv_mailer->sentRemainingPoints($customerId):- Fetches customer record and calculates spendable points and cash equivalent.
- Sends email using
remainingPointstemplate with registry-configured subject (EMAIL_SUBJECTS.REMAINING_POINTS).
- Tracking:
customer_campaign_model->saveCustomerCampaign($customerId, POINTS_REMAINING).
- Email:
Data Model
Tables
| Table | Relevant Columns | Role |
|---|---|---|
shop_customer | id, birthdate, total_points, is_guest, is_sanitized | Customer records with birthday and point balance |
shop_customer_campaign | customer_id, campaign_type, entry_datetime | Campaign send tracking for repeat interval enforcement |
shop_order | customer_id, entry_datetime | Orders; used to filter active customers for points reminders |
Campaign Types (CUSTOMER_CAMPAIGN_TYPE constant)
| Type | ID | Used By |
|---|---|---|
POINTS_REMAINING | 1 | AdvSentMailToCustomerRemainingPoints |
BIRTHDAY | 2 | AdvSentMailToCustomerBirthday |
shop_customer_campaign Schema
| Column | Type | Description |
|---|---|---|
customer_id | int | FK to shop_customer |
campaign_type | int | Campaign type from CUSTOMER_CAMPAIGN_TYPE constant |
entry_datetime | datetime | Timestamp when the campaign email was sent |
Configuration
Registry Settings
Birthday Email
| Group | Key | Default | Description |
|---|---|---|---|
EMAIL_FOR_BIRTHDAY | IS_ENABLED | -- | Master toggle for birthday emails |
EMAIL_FOR_BIRTHDAY | POINTS_TO_ADD | -- | Bonus loyalty points awarded on birthday |
EMAIL_FOR_BIRTHDAY | REPEAT | 12 | Months between birthday emails for the same customer |
EMAIL_SUBJECTS | BIRTHDAY_WISHES | -- | Localized email subject (supports %s for customer name) |
Remaining Points Email
| Group | Key | Default | Description |
|---|---|---|---|
POINT_SYSTEM | IS_ENABLED | -- | Master toggle for loyalty points system |
EMAIL_FOR_TOTAL_POINTS | IS_ENABLED | -- | Toggle for remaining points emails |
EMAIL_FOR_TOTAL_POINTS | TOTAL_POINTS | -- | Minimum points threshold to qualify |
EMAIL_FOR_TOTAL_POINTS | INTERVAL | -- | Months of order inactivity required (0 = any) |
EMAIL_FOR_TOTAL_POINTS | INTERVAL_REPEAT | -- | Months before re-sending to same customer |
EMAIL_SUBJECTS | REMAINING_POINTS | -- | Localized email subject |
Email Templates
| Job | Template View | Mailer Configuration |
|---|---|---|
| Birthday | birthdayWishes | Sender: EMAIL_SHOP_MAILER, Config: REMAINING_POINTS |
| Remaining Points | remainingPoints | Sender: EMAIL_SHOP_MAILER, Config: REMAINING_POINTS |
Job Options
Both jobs accept no options. All behavior is controlled via registry settings.
Client Extension Points
- Job override: Create
SentMailToCustomerBirthdayorSentMailToCustomerRemainingPointsinapplication/modules/job/libraries/extending theAdv*base class. - Customer model override: Override
getCustomersForBirthday()orgetCustomersForRemainingPoints()to add custom eligibility criteria (e.g., membership tier, opt-in flag). - Mailer override: Override
sentBirthdayWishes()orsentRemainingPoints()inAdv_mailerto customize email content, add coupon codes, or integrate with third-party email services. - Loyalty override: Override
Adv_loyaltyto customize point-to-cash conversion rates or add birthday multipliers. - Campaign types: Extend
CUSTOMER_CAMPAIGN_TYPEconstant andAdv_customer_campaign_modelto add new campaign types.
Business Rules
Birthday Emails
- Feature gate: The job is a complete no-op when
EMAIL_FOR_BIRTHDAY.IS_ENABLEDis false. - Registered customers only: Guests (
is_guest=true) and sanitized accounts (is_sanitized=true) are excluded. - Birthday matching: Uses MySQL date arithmetic to find customers whose next birthday falls on today's date, handling year rollover correctly.
- Repeat interval: Default 12 months between birthday emails. Tracked via
shop_customer_campaignwith typeBIRTHDAY. Setting to 0 effectively disables the repeat prevention. - Conditional points: Birthday bonus points are only awarded when
POINT_SYSTEM.IS_ENABLEDis true ANDPOINTS_TO_ADD> 0. Points are persisted via the loyalty library before the email is sent. - Cash display: If points are awarded, the email includes both the point amount and the equivalent cash value (using the default currency).
- Personalized subject: The email subject line includes the customer's first name via
vsprintf().
Remaining Points Emails
- Double gate: Requires both
POINT_SYSTEM.IS_ENABLEDandEMAIL_FOR_TOTAL_POINTS.IS_ENABLEDto be true. - Minimum threshold: Only targets customers with
total_points > TOTAL_POINTSsetting. If threshold is 0 or negative, no customers are processed. - Inactivity targeting: When
INTERVAL> 0, excludes customers who have placed orders within that many months. This targets customers who may have forgotten about their points. - Repeat prevention:
INTERVAL_REPEATcontrols how many months must pass before the same customer receives another reminder, tracked viashop_customer_campaignwith typePOINTS_REMAINING. - Cash conversion: The email displays both the spendable points and their cash equivalent, calculated via
loyalty->calcPointsToSpend()andloyalty->pointsToCash().
Related Flows
- SY-01 Cron Framework -- Job registration and scheduling infrastructure
- SY-24 Email Dispatch System --
Adv_mailerinternals, SMTP routing, and template rendering used bysentBirthdayWishes()andsentRemainingPoints() - SY-10 Loyalty Points Jobs -- Automatic point awarding for fulfilled orders
- CF-32 Loyalty Points -- Full loyalty points system (earning, spending, display)
- SY-09 Customer Data Jobs -- Sanitization removes campaign tracking records
- AD-22 Audience & Campaigns -- Campaign configuration and audience management