Points Expiry
A member of GadgetSphere earns 120 points on 3 March with a validity of 365 days. On 3 March next year those 120 points — or whatever is left of them — go. Not the member’s whole balance, not “points older than a year”: that lot. That is the entire expiry model, and it follows from how points are stored.
Each lot carries its own end date
Every earning creates a lot in bl_crm_membership_points_ctrl_acc with a date_start, a
date_end and a balance_amount. The dates are copied from the transaction line’s
date_valid_from / date_valid_to, which are set where the points were earned:
| Earned from | Where the validity comes from |
|---|---|
| A sales document line | The line’s point start and end dates, set by the Pricebook Point treatment that priced it |
| A Commission Scheme points run | Document date plus the treatment’s Validity Period (Days) (default 0) |
| A manual adjustment | Valid Date From / To on the Add Point Adjustment form |
| A CSV upload | The validity columns of the point-transaction template |
| A birthday gift | points_validity_in_months on the scheduled gift |
There is no expiry rule to configure in Membership Admin
and no per-currency or per-class expiry setting. If you want SILVER members’ points to last
longer than STANDARD members’, that is a different price set with a different validity, not an
expiry policy.
Redemptions draw lots down through contra rows (bl_crm_membership_points_ctrl_contra_details).
The Points Expiry tab on the member form lists the lots with their balance, dates and the
next_expiry_date_check, and Point Expiry Edit lets a permitted user change a lot’s dates —
which is how a customer-service extension is done.
Three scheduled processors
All three are jobs you schedule in the Scheduler; none runs unless it is scheduled.
| Processor | What it does |
|---|---|
MEMBERSHIP_EXPIRY_POINTS_ELIMINATION_PROCESSOR | Selects every active lot with balance_amount > 0 AND date_end <= NOW(), writes an EXPIRY transaction line and a contra row for the remaining balance, sets the lot to zero and marks it deleted. The running balance is updated from the line |
MEMBERSHIP_POINTS_EXPIRY_REMINDER_PROCESSOR | E-mails members whose points are expiring soon |
MEMBERSHIP_STATUS_UPDATE_ZERO_BALANCE_PROCESSOR | Sets the member status to inactive once the balance in the configured currency is zero |
The sweep is a whole-tenant batch, not a check at the moment of redemption: between the end date
and the next run, the running balance still includes the expired lot, while the valid balance
(…/balance/{card_no}, which refreshes the lot basket for expired points) does not. That is the
one place the two balances on the currencies page
legitimately differ.
What the accountant sees
Nothing. An expiry line is a membership record; it does not post a journal (ServerDocTypes
signum 0/0, no JournalPostingTypeHandler entry — see currencies).
“Breakage” is a number you compute from the EXPIRY lines in the transaction report, not something
the module books.
Related
- Points setup — schedule the processors.
- Membership Admin — Points Expiry tab.