CRMconnect Azuvio · Docs

Workflow: Loyalty Program — Customer Loyalty

Modules involved: Loyalty (Rules · Transactions · Redemption · Membership · Programs) · Invoices · OmniSales · Clients
Used by: Marketing · Sales · Customer Success
Typical duration: Continuous — points accumulate with each payment

Overview

The Loyalty module implements a complete loyalty system: automatic point accumulation upon invoice payment, three types of bonus awards (new account, birthday, manual), redemption in 4 different contexts (admin invoice, client portal, OmniSales, voucher), and a tier system with differentiated benefits per tier.

Required condition: The global toggle loyalty_setting must be set to 1 in Setup → Marketing → Loyalty. Without this, the after_payment_added hook executes no points logic regardless of configured rules.

There is no automatic point expiration. Points decrease only through explicit redemption.


Flow diagram

[SETUP — admin, one time]
  │
  ├── Global activation: loyalty_setting = 1
  ├── Loyalty Rules: earn type (card_total / product_category / product)
  │     + redemption tiers (point_weight per point band)
  ├── Membership Rules (Tiers): Bronze / Silver / Gold by point range
  ├── Membership Programs: discounts per tier (card/category/product)
  └── Loyalty Cards: visual template (optional)
  │
  ↓
[POINT ACCUMULATION — automatic on payment]
  │
  ├── Trigger: after_payment_added → invoice status = PAID (2)
  ├── System searches for active rules for the client (by client_id or client_group)
  │
  ├── rule_base = 'card_total':
  │     points = poin_awarded × floor(invoice_total / purchase_value)
  │     (e.g.: 10 points per 100 RON spent)
  │
  ├── rule_base = 'product_category':
  │     per invoice line: if product group = rel_id in rule_detail
  │     → points = loyalty_point × quantity
  │
  └── rule_base = 'product':
        per invoice line: if product = rel_id in rule_detail
        → points = loyalty_point × quantity
  │
  ├── Insert into loy_transation: type='debit', reference='order_debit'
  └── clients.loy_point += points earned
  │
  ↓
[BONUS AWARDS]
  │
  ├── New account (after_client_added hook):
  │     → rule with MAX(create_account_point) active today
  │     → type='credit', reference='manual_credit'
  │
  ├── Birthday (cron before_cron_run hook):
  │     → finds clients with custom field 'Birthday' = today (month+day)
  │     → once per year (checks loy_transation.note='bonus_points_for_customers_birthday')
  │     → sends email template 'loyalty-birthday-bonus-point'
  │
  └── Manual (admin): loyalty/transation_form → type='credit'
  │
  ↓
[TIER — calculated dynamically]
  │
  ├── client_rank() → finds loy_mbs_rule where loy_point ∈ [loyalty_point_from, loyalty_point_to]
  ├── Highest matching tier awarded
  └── client_next_rank() → shows how many points are needed for the next tier
  │
  ↓
[REDEMPTION — 4 contexts]
  │
  ├── [A] Admin on invoice: redeem field in the invoice form
  ├── [B] Client in portal: invoice page → redeem points
  ├── [C] OmniSales/POS: redeem field in the order cart
  └── [D] Voucher/Membership program: voucher code or automatic discount per tier
  │
  ↓
[DISCOUNT APPLIED]
  │
  ├── Points converted to currency via point_weight (e.g.: 1 pt = 0.05 RON)
  ├── redeemp_type='full': all points, value calculated automatically
  ├── redeemp_type='partial': client chooses how many points to redeem
  ├── Insert into loy_redeem_log: old_point, new_point, redeep_from, redeep_to
  └── clients.loy_point -= points redeemed

[LOYAL CLIENT ✓ · POINTS ACCUMULATED ✓]

Step by step

1. Activation and global configuration (admin)

Where: /admin/loyalty → Setup → General Settings

Required activation: loyalty_setting = 1

Without this, the after_payment_added hook is registered but returns immediately without any calculation.


2. Loyalty Rules — earn and redemption rules

Where: /admin/loyalty → Loyalty Rules → New Rule

A single rule defines both earning and redemption.

2a. Rule fields

Field Required Description
subject Yes Rule name
enable Yes 1 = active
start_date / end_date Yes Validity period
rule_base Yes card_total / product_category / product
client_group No Target client group (0 = all)
client No Specific client IDs (CSV)
minium_purchase No Minimum order value to earn points
max_amount_received No Maximum points cap earned per transaction

For card_total:

Field Description
poin_awarded Points awarded per bracket
purchase_value Value of one bracket (e.g.: 100 RON = 10 points)

For product_category and product: add lines in Rule Details — each line specifies rel_type, rel_id (group or product ID) and loyalty_point per unit.

Special bonuses per rule:

Field Description
create_account_point Bonus points when a new client account is created
birthday_point Birthday bonus points (requires the custom field 'Birthday' on the client)

2b. Redemption Tiers

Add at least one tier in the Redemption Details section of the rule:

Field Description
point_from / point_to Point range for this tier
point_weight Conversion rate: points → currency (e.g.: 0.05 = 1 pt = 0.05 RON)
status 'enable' = active tier
redeem_portal 1 = can be used in the client portal
redeem_pos 1 = can be used in OmniSales/POS
min_poin_to_redeem Minimum points required to activate redemption
redeemp_type 'full' = full redemption · 'partial' = partial redemption

3. Membership Rules — tiers

Where: /admin/loyalty → Membership → New Membership Rule

Defines tiers (e.g.: Bronze, Silver, Gold) based on accumulated points:

Field Description
name Tier name (e.g.: "Gold")
loyalty_point_from / loyalty_point_to Point range for this tier
client_group Eligible client group (0 = all)
card FK → loy_card (optional visual card template)

4. Membership Programs — tier benefits

Where: /admin/loyalty → Membership Programs → New Program

A program links one or more tiers to a discount benefit:

Field Description
program_name Program name
membership Target tier IDs (CSV from loy_mbs_rule)
discount Discount type: card_total / product_category / product
discount_percent Discount percentage
voucher_code Optional voucher code (for manual redemption)
voucher_value Fixed voucher value
minium_purchase Minimum order for discount activation
start_date / end_date Program validity

Note: When a membership program with a discount is applied to an invoice, the system automatically generates a credit note (add_credit_mbs_program()) and applies it to the invoice. This credit note appears in the Credit Notes module.


5. The "Birthday" custom field (for birthday bonus)

Where: /admin/clients → client record → custom field Birthday (date_picker)

The field is created automatically by the module with slug customers_birthday. The birthday bonus is triggered by the cron (before_cron_run) once per year per client.


6. Points redemption

6a. On invoice (admin)

When creating/editing an invoice, the Redeem Points section appears if:

  • The client has sufficient points (≥ min_poin_to_redeem)
  • An active rule with a valid redemption tier exists

Staff selects the points to redeem → discount calculated automatically → written to loy_redeem_log.

6b. Client portal

The logged-in client sees the Redeem Points button on the invoice page (if redeem_portal=1 on the rule and the invoice is not already fully paid).

The client chooses the points → the system directly modifies invoices.total (reduces the total) and logs to loy_voucher_inv_log.

6c. OmniSales / POS

The redemption field appears in the OmniSales cart (if redeem_pos=1). Applied at order creation.

6d. Membership voucher

When entering a voucher code (apply_other_voucher filter), the system checks whether the client belongs to a tier with an active program that has that code. Returns voucher_value and minium_purchase.


7. Viewing client point balance

Where: /admin/clients/{id} → column loy_point on the client record

The current balance is stored directly in tblclients.loy_point (DECIMAL). The full transaction history is visible in Loyalty → Transactions filtered by client.


Required permissions

Action Permission
View Loyalty module loyalty → view
Configure rules, cards, programs is_admin() exclusively (require_loyalty_admin)
Client portal (redemption) is_client_logged_in()

Gotchas

Problem Cause Solution
Points not added after payment loyalty_setting ≠ 1 or rule expired Activate global toggle + check rule start_date/end_date
Client not matched by accumulation rule client or client_group mismatch Verify the rule includes that client or their group
Birthday bonus not awarded Birthday field not filled on client or cron inactive Fill in the field + check cron job
Redemption not appearing on invoice min_poin_to_redeem not met or rule inactive Check point balance vs min_poin_to_redeem
Membership discount generates unexpected credit note Correct behavior — add_credit_mbs_program() The credit note is normal; view it in Credit Notes
Points do not expire No expiration mechanism exists Correct behavior — points are permanent; limit via end_date on the rule

Module references