Commit 7e05dc96 authored by Mahmoud Aglan's avatar Mahmoud Aglan

docs(subscriptions): update architecture map with getRate fix and cron setup

Documents the PHP truthiness bug (0.00 ?: fallback), the fix applied,
production data corrections, and new Docker cron infrastructure.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent c647eaa8
# Subscriptions Module — Architecture Map # Subscriptions Module — Architecture Map
> **Last updated:** 2026-06-13 (dev fee included in paid_amount for all payment paths) > **Last updated:** 2026-07-03 (getRate() truthiness bug fixed, cron daemon added to Docker)
> **Status:** Living document — incrementally updated as new information is discovered > **Status:** Living document — incrementally updated as new information is discovered
--- ---
...@@ -49,7 +49,7 @@ app/Modules/Subscriptions/ ...@@ -49,7 +49,7 @@ app/Modules/Subscriptions/
## 3. Database Schema (Production — Source of Truth) ## 3. Database Schema (Production — Source of Truth)
### 3.1 `subscriptions` Table (254 rows as of 2026-06-10) ### 3.1 `subscriptions` Table (221 rows for 2026/2027 FY as of 2026-07-03)
| Column | Type | Nullable | Key | Default | Notes | | Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------| |--------|------|----------|-----|---------|-------|
...@@ -75,10 +75,11 @@ app/Modules/Subscriptions/ ...@@ -75,10 +75,11 @@ app/Modules/Subscriptions/
| created_by | bigint unsigned | YES | | | FK to employees | | created_by | bigint unsigned | YES | | | FK to employees |
| updated_by | bigint unsigned | YES | | | FK to employees | | updated_by | bigint unsigned | YES | | | FK to employees |
**Current data distribution (status):** **Current data distribution (2026/2027 FY, 221 rows):**
- pending: 201 - 66 member @ 492.00
- paid: 39 - 54 spouse @ 492.00
- overdue: 14 - 90 child @ 222.00
- 11 temporary @ 222.00
**Financial years with data:** 2020/2021 through 2026/2027 (7 years) **Financial years with data:** 2020/2021 through 2026/2027 (7 years)
...@@ -126,6 +127,13 @@ app/Modules/Subscriptions/ ...@@ -126,6 +127,13 @@ app/Modules/Subscriptions/
guarded). Re-running batch generation on the same FY would create duplicate subscription rows for dependents. guarded). Re-running batch generation on the same FY would create duplicate subscription rows for dependents.
Each dependent now checks for an existing row before inserting. Each dependent now checks for an existing row before inserting.
**Fix applied 2026-07-03 (getRate truthiness bug):** `getRate()` returned `'0.00'` for missing/zero
service_catalog rows. PHP's `?:` operator treats `'0.00'` as truthy, preventing fallback to generic
service codes. All 204 initially generated subscriptions had `base_amount=0.00`. Fixed by:
1. `getRate()` now returns `''` for missing/zero rows (falsy, triggers fallback)
2. Hard-coded fallback rates as last resort: member/spouse=492, child/temporary=222
3. All 204 bad rows manually corrected via UPDATE on production DB.
### 5.2 Subscription Payment (with FIFO enforcement) ### 5.2 Subscription Payment (with FIFO enforcement)
``` ```
...@@ -277,6 +285,13 @@ the correct values idempotently via ON DUPLICATE KEY UPDATE. ...@@ -277,6 +285,13 @@ the correct values idempotently via ON DUPLICATE KEY UPDATE.
**Note:** `OverdueFineJob` now delegates entirely to `OverdueFineApplicator::run()` for fine/drop logic. The cron job only handles marking subscriptions as overdue; all calculation and distribution is in the service class. No duplicate logic exists. **Note:** `OverdueFineJob` now delegates entirely to `OverdueFineApplicator::run()` for fine/drop logic. The cron job only handles marking subscriptions as overdue; all calculation and distribution is in the service class. No duplicate logic exists.
**Cron Infrastructure (added 2026-07-03):**
- Docker container now installs `cron` package via Dockerfile
- `docker/entrypoint.sh` installs crontab and starts cron daemon before Apache
- Crontab: `0 * * * * cd /var/www/html && php cli.php cron >> storage/logs/cron.log 2>&1`
- `SubscriptionGeneratorJob` delegates to `SubscriptionGenerator::generateForYear()` — the same service used by manual batch-generate
- Job checks if ANY rows exist for the FY before generating (skips entirely if count > 0)
--- ---
## 11. Configuration Dependencies ## 11. Configuration Dependencies
...@@ -360,6 +375,8 @@ Financial year runs July to June: ...@@ -360,6 +375,8 @@ Financial year runs July to June:
6. **Menu registered via Members module**: Subscription menu items appear under the Members sidebar group 6. **Menu registered via Members module**: Subscription menu items appear under the Members sidebar group
7. **No API endpoints**: All interaction is web-only (no REST API for external systems) 7. **No API endpoints**: All interaction is web-only (no REST API for external systems)
8. **Exempt status is terminal**: Once exempted, a subscription cannot be un-exempted without direct DB intervention 8. **Exempt status is terminal**: Once exempted, a subscription cannot be un-exempted without direct DB intervention
9. **PHP truthiness hazard in getRate()**: `'0.00' ?: fallback` never triggers because PHP treats non-empty strings as truthy. Fixed: `getRate()` returns `''` for missing/zero rows, plus hard fallback defaults.
10. **service_catalog date filtering**: `getRate()` filters by `effective_from <= today AND (effective_to IS NULL OR effective_to >= today)`. Year-specific codes (e.g. `SVC_ANNUAL_TEMP_2026`) may not exist — the generic code is the reliable fallback.
--- ---
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment