Gym Module Documentation
Overview
The Gym Module manages the subscription lifecycle for fitness center members. It generates recurring revenue through time-based "Plans" (Monthly, Yearly) and one-off "Day Passes". It is fully integrated with Accounting (Accrual Revenue) and Tax (VAT Calculation).
Architecture
Domain Layer (app/Domain/Gym)
Models (4 Models)
GymMember (GymMember.php)
- Table:
gym_members - Description: The profile. Links to
Party. - Key Fields:
status: ACTIVE | INACTIVE | BANNED.health_conditions: Medical notes.emergency_contact.
GymPlan (GymPlan.php)
- Table:
gym_plans - Description: The product definition.
- Key Fields:
name: e.g., "Gold Monthly".duration_days: 30.price: Base price (excluding tax).revenue_account_id: GL Account to credit (e.g. 413).
GymMembership (GymMembership.php)
- Table:
gym_memberships - Description: The contract.
- Key Fields:
start_date,end_date: Access window.amount_paid: Total.renewed_from_id: Link to previous membership (Traceability).
GymCheckIn (GymCheckIn.php)
- Table:
gym_check_ins - Description: Audit log of access.
Services
GymMembershipService (GymMembershipService.php)
Purpose: Sales and Lifecycle management.
Key Methods:
sellMembership(member, plan, startDate, payments)
Creates a net-new contract.
- Logic:
- Calculates
endDate(startDate + plan.duration). - Calculates Tax (
TaxEngine::calculate('GYM', price)). - Accounting: Credits Revenue (413), Credits Tax (204), Debits Cash (101).
- Updates Member Status -> ACTIVE.
- Calculates
renewMembership(oldMembership, plan)
Extends access.
- Logic:
- Smart Date: If
oldMembershipis still active, new start date =old_end_date + 1 day. If expired, startstoday. - Chain: Sets
renewed_from_idto build a history chain. - Expire: Sets
oldMembership.status= EXPIRED.
- Smart Date: If
postMembershipRevenue()
Internal helper for GL posting.
- Checks: Verifies "Sales Discount" (450) and "Tax Payable" (204) accounts exist. Throws exception if missing (Safety).
Audit Findings & Improvements
Strengths
- Renewal Logic: The "Smart Date" logic ensures members aren't penalized for renewing early (they don't lose the remaining days of their current pass).
- Traceability: The
renewed_from_idlinked list allows accurate calculation of "Customer Lifetime Value" and retention rates.
Issues Identified
Major
- Hardcoded Account Requirements: The service throws an exception if Account 450 (Discounts) is missing, even if no discount is applied. This creates a fragility where a Chart of Accounts change can break the Gym POS.
- Fix: Make the check conditional on
discount_amount > 0.
- Fix: Make the check conditional on
Minor
- Constraint Error: Previous debugging session revealed
gym_plan_idintegrity issues if the Plan is soft-deleted. - Date Rigidness: Day passes require
valid_date == today. You cannot sell a "Tomorrow Pass".
Module Version
Version: 1.0 Status: Stable