Skip to content

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 fromWhere the validity comes from
A sales document lineThe line’s point start and end dates, set by the Pricebook Point treatment that priced it
A Commission Scheme points runDocument date plus the treatment’s Validity Period (Days) (default 0)
A manual adjustmentValid Date From / To on the Add Point Adjustment form
A CSV uploadThe validity columns of the point-transaction template
A birthday giftpoints_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.

ProcessorWhat it does
MEMBERSHIP_EXPIRY_POINTS_ELIMINATION_PROCESSORSelects 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_PROCESSORE-mails members whose points are expiring soon
MEMBERSHIP_STATUS_UPDATE_ZERO_BALANCE_PROCESSORSets 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