WP Manifestindependent plugin directory
manifest / users / team-oversight-migration

MURVC Club Manager

MURVC custom WordPress plugin: trial applications, fee calculation, team oversight dashboard, RevSport/Xero CSV sync

by MURVC · github.com/jjdunlop/team-oversight-migration

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/jjdunlop/team-oversight-migration/archive/refs/heads/main.zip

Readme

                                          .--""--.
                            *    .       /  .--.  \
                     o/        *        ;  /    \  ;
                    /|      .    *      :  \    /  :
                    / \          .       \  '--'  /
                   /   \      *           '--..--'
                                              \\
     __________________________________________\\________________
    |    |    |    |    |    |    |    |    |   \\   |    |    |
    |____|____|____|____|____|____|____|____|____\\__|____|____|
    |    |    |    |    |    |    |    |    |     \\ |    |    |
    |____|____|____|____|____|____|____|____|______vv|____|____|
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        B u m p .   S e t .   S p i k e .   A d m i n i s t r a t e .

MURVC Club Manager

MURVC's club management system for WordPress, integrating Ultimate Member (accounts/profiles) and WooCommerce (payments via Square). The admin splits into three areas:

  • Club Membership — club-wide member list, time-bound membership tiers, membership history reporting, stats and logs
  • VVL Oversight — competition machinery: teams, trials, coach selections, team assignments, fees and payments, player readiness
  • Club Programs — attendance for casual programs (Mixed Development and anything else sold as a product with date variations)

The plugin folder is still team-oversight-migration and the constant is TEAM_OVERSIGHT_VERSION; the display name changed in 1.33.0. Renaming the folder/slug is a deliberate cutover-day job (it deactivates the plugin and changes the deploy path).

Requirements

  • WordPress 5.0+, PHP 7.4+
  • Ultimate Member (profiles, registration, login)
  • WooCommerce with legacy (CPT) order storage — HPOS is not currently supported by the kit-purchase detection

Club Membership

Membership tiers

Memberships are time-bound grants stored in team_memberships: tier + start/end date + source (purchase / manual / import). A member's current status is their highest unexpired grant. Three tiers:

Tier WP role How granted Expiry
Life Member life-member Manual only Never (2099 sentinel, shown as "no expiry")
Full Member full-member Purchase, manual, or automatic on team assignment Term from purchase date; assignment grants run to 31 Dec of the season year
Associate Member associate-member Purchase or manual Term from purchase date
  • From purchases: set "Membership tier granted" + "Membership term (months)" on a WooCommerce product (Product → Edit → General), or configure product-category rules on the Members page. Only explicitly configured products grant anything. Terms run from the purchase date; overlapping grants simply overlap (highest active tier wins).
  • From team assignments: anyone with an active team assignment (any role — player, training only, coach, assistant coach, team manager) for the current season or later is automatically granted Full Membership running to 31 Dec of the season year (source = assignment). Synced immediately after plugin admin changes and by the daily cron; deduped per person per season, and removing someone from a team never revokes the grant.
  • Role sync: the full-member/associate-member/life-member WP roles are kept in sync on every grant and by a daily cron (team_oversight_membership_sync), including automatic demotion when the last grant expires. Users with roles but no ledger rows are never touched (protects pre-ledger role assignments until seeding runs).
  • Seeding: Members page → "Seed memberships from this year's purchases" converts the year's qualifying purchases into dated grants (dry-run available, idempotent).
  • Order re-scan: Members page → "Re-scan this year's paid orders" replays every paid order through the grant logic with the current product/category configuration — run it after adding membership attributes to a product, since orders paid before the attributes were set granted nothing. Idempotent; backfilled grants are dated from the order's paid date. (Grants require the order to reach Processing/Completed and be linked to a WP account — guest orders never grant.)
  • The [murvc_member_role] shortcode (registered by the plugin) shows the profile owner's tier badge on Ultimate Member profiles.

Admin dashboard

The WP dashboard is replaced (for admins) with a fast MURVC Club Overview widget: current members, fees outstanding, overdue people/amount, trial applications awaiting action, people on teams — each tile linking to its admin page — plus the latest activity-log entries and quick links. The slow stock widgets (WordPress news, WooCommerce status/report queries, quick draft, activity, Solid Security panel) are removed; the widget itself renders from the plugin's indexed tables in a few milliseconds.

Members page (Club Membership menu)

One row per person: membership status + expiry, age, gender, MUS category, VVL teams for the season, fees owing, VA accreditation, profile-confirmation status. Filter/search/sort, CSV export, manual grant (with email autocomplete) and revoke.

Member Lookup

Club Membership → Member Lookup: search any account on the site — not just people on the membership ledger — and get everything the club holds about that one person on a single screen. Emergency contacts sit at the top (both of them, phone numbers repaired and tap-to-call), followed by profile details, membership grant history, every team assignment across all seasons, fees with the payment ledger and computed overdue amount, trial applications, recent WooCommerce orders and recent activity-log entries.

Search matches display name, email, username, first/last name, mobile, student ID and emergency contacts — so an unknown number that rings the club can be traced back to whose contact it is. When a result matched on someone else's details the row says so ("emergency contact: Jane Smith — 0411 222 333") rather than looking unexplained.

Phone matching compares digits to digits, so formatting never gets in the way: 0411 222 333, 0411222333, 411222333 and +61 411 222 333 all find each other. That covers both the imports that dropped the leading zero and the profiles saved with spaces or a country code.

Deliberately search-first: with thousands of accounts the page never lists them all. Nothing is queried until you type at least two characters, results are capped at 25, and the detail view costs one person's worth of queries (a search runs in ~20ms against ~3,800 accounts). The Profile button on the Members page opens this view.

Stats

Club Membership → Stats — statistics about current members (unexpired ledger grants), in tabs designed to grow over time:

  • Data Quality: profile completeness — gender, DOB, mobile, MUS category, this-year confirmation — as percentages with colour bars and 30-day deltas. A snapshot is recorded once a day (membership cron, page-view fallback, manual button) into a non-autoloaded option capped at ~3 years, so sparkline trends accrue from first deploy.
  • Locations: postcode distribution (UM postal_code, falling back to the WooCommerce billing postcode; messy values like "VIC 3000" are normalised), plus a saved postcode watchlist — enter any list of postcodes and see how many members live in them. Defaults to current members; a date-range picker widens the population to anyone who held a membership during the period.

Membership History

Club Membership → Membership History: pick a date range and see everyone whose membership overlapped it — highest tier held, every membership period with dates and source, age/gender/MUS, current status. CSV export. History exists from the moment grants exist (run seeding to backfill).

The MUS Matrix tab on the same page produces the periodic MUS return: active members (anyone holding a membership grant in the date range) as a matrix of MUS eligibility category × gender (Male / Female / Non-Binary / Unknown) with totals, CSV export. Profile category variants fold into the MUS row labels (other-university student/alumni variants merge; junior/high-school count as Other; missing data counts as Unknown; unrecognised categories keep their own row rather than being lumped).

The VV Report tab produces the VV return: age band (10-year bands, age at the end of the period; missing DOB → "Unknown DOB") × gender (Male / Female / Other / Unknown gender) with totals, CSV export. Defaults to Associate members only, with tier checkboxes to change what's included; each person counts once under the highest tier they held in the period, so tier selections never double-count.


VVL Oversight

Teams (Configuration page)

Teams are configured with a code (stable internal ID — never shown to players), name (what players see), gender (men's/women's/mixed), age eligibility rule and playing-shirt count:

  • Age rules follow the VVL By-Laws and compute their DOB cutoff from the season year automatically (nothing to update each season): U19 = no 19th birthday during the season year; U17/U15 (YSL) = 16/14 or younger as of 31 August.
  • Shirts: how many playing shirts a player must pay for on this team (Premier 2, YSL 0 — supplied, default 1).
  • "Load default club team list" resets to the current club teams.
  • Create season (button beside the Configuration season selector): registers the next season (highest known + 1, e.g. 2028) so it appears in every season selector before any data exists — ready for season dates, fee matrix, and trial opening.

Trials

Front-end form via [team_trial_form] (login required; prompts to log in / create an account with an explanation):

  • Prefilled, read-only account details (name, email, phone, DOB, gender, institution) — edited via the profile, never the form. Submission requires only what trials genuinely need: name, contact number, DOB and gender (age rules + competition). MUS/degree fields are not required to apply — they're a fee-class matter, collected by the annual profile wall and resolved at invoicing.
  • Competition (men's/women's) derives from profile gender; the question is only asked when the profile can't answer (unset/non-binary).
  • Questionnaire mirrors the club's VVL trials form: VVL history (with conditional returning-player and club-transfer sections — the previous club is a dropdown of the VVL clubs plus Other Victorian / Interstate / International options that reveal a club-name field, stored as e.g. "Interstate: Sydney Uni"), international player details, team selection (real teams, grouped by gender, with ineligible teams greyed out live by competition and DOB cutoff — enforced server-side too), positions, venue availability, trial-date availability, experience. Answers stored as JSON (form_data).
  • Per-season opening: "Accepting trial applications for: [year checkboxes]" (Trial Applications → settings). Only ticked seasons appear in the trial form's season selector, and submissions for unticked seasons are rejected server-side; with nothing ticked the form shows a friendly closed notice (existing applicants always keep seeing their trial number and status). The settings summary shows the open seasons, or APPLICATIONS CLOSED in red.
  • Trial fee rules (Configuration → Trial application rules): who pays the trial fee is an if-then rule set — by default new-to-VVL and transferring players pay, returning Renegades players are free, and anyone under 18 is free regardless (threshold configurable, 0 disables). Waived applications submit directly (no checkout) and record the waiver reason in the application ("Waived — under 18" / "Waived — returning Renegades player"). The form's fee notice adapts: juniors see "trials are free for players under X", adults see who the fee applies to. The transfer club list is editable in the same section (one club per line; empty = built-in VVL default list).
  • Trial fee: configure a product in the Trial Applications settings box. Submissions save as awaiting_payment, go to checkout, and become reviewable (pending) when the order is paid. Unpaid applications expire after 7 days; "Mark as Paid" covers offline payments. No fee product = direct submission.
  • Trial numbers: per-season sequential number assigned at submission, shown persistently to the player (status panel on the form page) and throughout admin/coach views — players can write it on themselves at busy trials.
  • Optional training-info page URL linked at the top of the form so players pick teams by training venue/day.

Read the full README on GitHub →