What You Owe Your Members
A member buys a laptop at GS-KV-01 for RM 4,800 and earns 4,800 GSPTS. GadgetSphere has taken
RM 4,800 of cash and has also handed over RM 48 of future merchandise that it will have to supply
when the member comes back. The sale is not worth its face value: it is worth RM 4,800 less
whatever those points will cost to honour.
That is the whole idea, and it is why points are a promise to a customer rather than a marketing expense. A marketing cost is spent and gone. A point is an obligation that sits on your balance sheet until it is redeemed or it dies, and a share of the revenue on the original sale belongs to the period in which the member finally spends it. It is the same shape as a gift voucher you sold, and an auditor will look at it the same way.
Finance carries this, because the product does not
Nothing in the points chain posts a journal. ServerDocTypes declares
INTERNAL_MEMBERSHIP_POINT_REWARD(0,0) and INTERNAL_MEMBERSHIP_POINT_REDEEM(0,0) — no amount, no
quantity — and JournalPostingTypeHandler has no membership entry at all. Awarding 4,800 points
writes three membership tables and touches no GL account. There is no points liability account, no
deferred revenue, no accrual and no breakage income. Redeeming reaches the ledger only as part of
the bill it is spent on — a settlement line or a MEMBER_POINT_DISCOUNT discount line — and that
posting belongs to POS General, not to this module.
So the liability is a procedure a person runs, every period, outside the product. The rest of this page is that procedure.
The month-end procedure, and who runs it
This is not the accounts clerk’s task. It needs someone who can carry a judgement — the group finance manager, or whoever signs the management accounts — because half of it is an estimate.
Step 1 — take the outstanding balance, before the period locks. GET …/membership-points-txn/current-balance/query returns the stored total per member and point
currency; sum it. This is the same number the reports page points
you at, and it is a stored accumulator, not a live recomputation: one row per member and
currency in bl_crm_membership_points_current_balance, maintained by the queue processor that
recomputes it as the sum of every active transaction line for that member
(MembershipPointsBalanceHelper.calculatePoints). Take it on the same day of every month, because
it moves continuously.
Step 2 — prove the number before you use it. The sum of REWARD, REDEEM, EXPIRY and
adjustment lines for a member and point currency must equal that member’s stored balance. When
they disagree, the stored figure is the one that is wrong — it is a cache — and the recalculation
queue in Membership Admin rebuilds it from the lines. Run that check on the whole balance before
you book anything, because a balance the product has silently zeroed (see
where a balance goes wrong) understates
what you owe.
Step 3 — value it at what you will actually pay. Points × your redeem rate gives the gross
obligation: 12.4 million GSPTS at 100 points to RM 1 is RM 124,000. Value it at the redeem rate,
not at retail margin — you owe the member merchandise at the price you publish.
Step 4 — take off breakage. Apply the share you have established will never be redeemed (see
program design). If your evidence says 35%
of an award cohort is never spent, the accrual is RM 80,600, and the 35% is a number you can
defend from your own lines/query history rather than a rule of thumb. Write the assumption
down in the file with the journal. It will be the first thing asked about.
Step 5 — post the movement, not the balance. The accrual already on the books is last month’s figure; this month you post the difference. Debit revenue (or a contra-revenue line, if that is your house treatment) and credit the points liability with the increase; reverse it when the liability falls.
Step 6 — tie it out. The movement you posted must equal points earned, less points redeemed,
less points expired, over the same dates, valued and breakage-adjusted the same way.
…/membership-points-txn/lines/query gives you those three by txn_type. If the balance moved
and your sales did not, an earning rule changed — find out which, because nobody was told.
When points expire, you stop owing them
An expiry does not post either. The EXPIRY line reduces a membership balance and writes nothing
to the ledger, so the release of the liability is the same manual journal, in the opposite
direction, in the period the expiry actually ran — not the period the points were due to expire.
Those are two different dates, and the gap between them is entirely in your hands: the sweep is
MEMBERSHIP_EXPIRY_POINTS_ELIMINATION_PROCESSOR, it selects lots where date_end <= NOW(), and it
runs only when the Scheduler runs it. If nobody scheduled it, nothing has ever expired, your
liability has only ever gone up, and the release you were expecting to bank this year does not
exist. Whoever configures the tenant answers that question in one look at the Scheduler; finance
should ask them before relying on a breakage release, and should ask again if EXPIRY lines stop
appearing.
Two smaller facts that change the number:
- A lot with no end date never expires. Points from a POINT treatment with no Validity Period, an adjustment with no Valid Date To, or a CSV row without validity columns are a permanent obligation. Finding them is a query on the lots, and fixing them going forward is a change to the earning rule — the existing lots cannot be retrospectively dated without an adjustment that the member may notice.
- Which date the movement sits on. A points figure for a period can be read on the document date, the award date or the lot’s validity start, and they are not the same. Pick one, say which in the working paper, and use it every month.
Work out which company in the group owes it
A member’s balance has no company on it, and neither does the member.
bl_crm_membership_hdr has no company column and bl_crm_membership_points_current_balance has
none either — there is one balance per member and point currency for the whole tenant. The
transaction lines do carry company and branch.
So in a group like GadgetSphere a card issued by GS is live in GSO and GSD too: points are
earned per company and held per tenant. A member can earn in the retail company and spend in
the online one, and nothing in the product records that GSO has just honoured an obligation GS
created.
That is an intercompany settlement, and nobody has been told they need to run it. Group finance
builds it from the transaction lines rather than from the balance — lines/query carries the
company on every row, so the position is earnings by company less redemptions by company — and the
check that it was done right is that the three companies’ figures sum to the single tenant-wide
balance. If the group has never done this, the first sign is an auditor asking which entity’s
balance sheet the liability belongs on.
The same fact settles a reporting question that looks unanswerable: points outstanding by branch is not available from the balance and has to be built from the lines.
What you settle with your auditor, and not with us
- Which accounting standard applies and how your auditor wants it presented is a conversation with your auditor, not a setting. What is certain is that the obligation exists and the product does not carry it.
- Whether you show the liability gross or net of breakage on the face of the accounts is your house treatment. Either way the breakage assumption is disclosed in the working paper.
- Whether points expire at all under the law you trade in. The product will expire them the moment you schedule the processor; whether you are entitled to is a question for whoever approved your terms.
Related
- Reports — the queries this procedure is built on.
- Program design — pricing the promise, and estimating breakage.
- Points expiry — the lot model and the sweep.
- Point currencies and balances — line, lot and balance, and which of the three the number came from.