BMC membership journey: the rules
This documents the rules of BMC membership — when you can join, the renew window, when a membership lapses, and the early-bird discount — together with where each rule is configured and how it's decided in the code. The rules were revised to give members a longer renew window and to restrict the early-bird discount to continuing members.
The rules at a glance
When can I join?
- BMC membership covers a calendar year (1 Jan – 31 Dec).
- A new member joins and their membership runs to the end of the current membership year — unless they join on or after 1 September, in which case it runs to the end of the following year (and starts on the day they join).
When can I renew?
- You can renew when your last BMC membership is still within the renew window — that is, it has not lapsed (see below) and you're renewing inside the ordinary renewal window in the weeks before the year ends (45 days before 31 Dec).
- A renewal inside the window extends the membership to the next year end; outside it, to the current year end.
When does a membership lapse?
- A membership lapses when it has slipped out of the three-membership-year renew window. In practice: if your last BMC membership covered, say, 2025, you can still renew through 2026 and 2027, and only have to re-join (rather than renew) when you would renew for 2028.
- Once lapsed you can't renew online; you go through the join process as a new member instead.
Early-bird discount
- Renewals only (never a new join), by 31 March, and only if you held a BMC membership in the immediately previous membership year (renewing for 2027 needs a 2026 membership). New joiners, and renewers whose last membership was more than one season ago, do not get the early-bird price — they pay the standard £40 rate.
Prices
- Standard — £40 for one annual (calendar-year) membership.
- Early-bird — £35 — renew and confirm payment by 31 March and, as a continuing member, you pay £35 instead of £40.
- New members — £45 for the first year, which includes a BMC vest (can take 4+ weeks to arrive). A join is never given the early-bird discount.
- Joining later in the year — a new join from 1 September covers the following calendar year, and the standard £40 yearly rate applies.
- Overseas — £50.
The rule changes
Two rules were changed to tighten/relax these:
- Longer renew window (renew vs join). A member's right to renew (rather than re-join) now stays open for three membership years after their last membership, not two. A member who expired 31 Dec 2025 is still treated as renewable (not lapsed) all through 2026 and 2027, and only lapses when they would renew for 2028.
- Early-bird discount is for continuing renewals only. It now requires all of: (a) a renewal, not a new join; (b) the early-bird window open (renewing by 31 Mar); and (c) a membership in the immediately previous season. A new joiner, or a renewer whose last membership was more than one season ago, pays full price.
Where the rules live
Config on the BMC federation and organisation (fixtures in
project/federation/fixtures/bmc/bmc.json, and in the admin):
- On the
Organisation:membership_year_end_day/month= 12 / 31 (calendar-year membership);renewal_window_days= 45 (the renew window). - On the
Federation:membership_joining_window_days= 122 (the new-member joining window — makes 1 September the first day of the following-year window: 1 Sep → 121 days to 31 Dec, 31 Aug → 122);membership_discount_day/month= 31 / 3 andmembership_discount_amount=-5.00(the early-bird cutoff and amount).
How the code decides
- Not lapsed / renew-vs-join:
Person.membership_has_lapsed(org_code, start, end)(reference/models.py) is not-lapsed iffstart <= date_to <= end, where the window comes fromFederation.membership_not_lapsed_window(today)— spanning three consecutive season ends ([2 season-ends ago … current season end]). All four BMC call sites infederation/views.pywere updated to use it; other federations keep the old two-season window. - New-member joining window:
Organisation.new_member_dates(when)returns the(date_from, date_to)for a join;make_member(..., is_join=True)uses it. The path decides (a lapsed-old-member is still "joining"), and the join URL quotes the same end date in the Stripe description. - Early-bird:
Federation.early_bird_discount(...)delegates to a per-federation pricing mixin (project/federation/pricing.py, registered in_MIXINS_BY_CODE). The BMC mixin returns0unless it's a renewal and the person held aMEMBER/REGISTEREDrole on the BMC org whosedate_toyear is the season before the one being bought. BMC's £45 categories carryrenew_fee_modifier = -5.00, so a renewal costs £40 (the standard rate) — that modifier is NOT window-gated and applies to every renewal. When the early-bird window is open and the member qualifies,org_renew_payadds the federationmembership_discount_amount = -5.00on top of that modifier, giving the £35 early-bird price. So the modifier is the always-on standard rate and the early-bird is a window-gated extra; neither applies to a new join. Overseas categories are £50.
Emails
After a successful payment the member can get a confirmation email; this is
gated on a non-empty athlete_membership_email on the Org (see
notify_athlete in federation/utils.py). Not part of the membership rules.
Tests
project/federation/tests/test_bmc.py:
BMCEarlyBirdDiscountTests— renew + previous-season membership → discount; renew without it → none; join → none; window closed → none; generic federation → discounts joins and renewals;membership_not_lapsed_window(today=2027-06-01)→[2025-12-31, 2027-12-31].BMCOverseasLogicTest— join dates (from 1 September → following year, before September → this year end, long-lapsed re-join → still following year), an end-to-end join payment throughorg-join-pay, and a category change keeping the season dates.- Renewal prices —
test_renewal_after_the_early_bird_date_pays_the_full_feeandtest_renewal_price_stacks_category_and_early_bird_discountsfetchorg-renew-payin offline-payment mode and read the quoted amount, plus the page-level January/prior-season cases inBMCEarlyBirdRenewPageTests.
The _FederationStub in that file models the federation surface; the DB-backed
cases use the bmc/bmc.json fixture and real Person/Role rows.
Adding another federation's early-bird rule
- Subclass
EarlyBirdPricingMixin, overrideearly_bird_discount(...). - Register the class in
_MIXINS_BY_CODEkeyed by the federation'scode. get_federation_pricingpicks it up automatically; the model method and the two price blocks need no change.