Commit 0959adae authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(subscriptions): remove subs for inactive dependents, fix Waiver boot crash

1. Added phase to SubscriptionDataMigration that removes unpaid
   subscriptions for dependents who are archived or no longer active
   (e.g., temporary members who became inactive, frozen children).

2. Fixed WaiverProcessor referencing non-existent 'reason' column in
   fines table — correct column is 'notes'.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 5ff54e5b
......@@ -32,6 +32,7 @@ final class SubscriptionDataMigration
private array $stats = [
'members_processed' => 0,
'pre_2023_deleted' => 0,
'inactive_removed' => 0,
'subscriptions_created' => 0,
'orphaned_ids_fixed' => 0,
'duplicates_removed' => 0,
......@@ -54,6 +55,9 @@ final class SubscriptionDataMigration
// Phase 0: Remove subscriptions before the system start (2023/2024)
$this->removePreSystemSubscriptions($dryRun);
// Phase 0.5: Remove subscriptions for inactive/archived dependents
$this->removeInactiveDependentSubscriptions($dryRun);
// Phase 1: Fix orphaned person_id references
$this->fixOrphanedPersonIds($dryRun);
......@@ -103,6 +107,33 @@ final class SubscriptionDataMigration
}
}
private function removeInactiveDependentSubscriptions(bool $dryRun): void
{
$types = [
'spouse' => 'spouses',
'child' => 'children',
'temporary' => 'temporary_members',
];
foreach ($types as $personType => $table) {
// Find unpaid subscriptions where the dependent is no longer active
$stale = $this->db->select(
"SELECT s.id FROM subscriptions s
JOIN {$table} t ON t.id = s.person_id AND t.member_id = s.member_id
WHERE s.person_type = ? AND s.status != 'paid'
AND (t.is_archived = 1 OR t.status != 'active')",
[$personType]
);
foreach ($stale as $row) {
if (!$dryRun) {
$this->db->query("DELETE FROM subscriptions WHERE id = ?", [(int)$row['id']]);
}
$this->stats['inactive_removed']++;
}
}
}
private function fixOrphanedPersonIds(bool $dryRun): void
{
$types = [
......
......@@ -196,7 +196,7 @@ final class WaiverProcessor
// 2. Fines (member-level — system stores all fines under member_id)
$fines = $db->select(
"SELECT id, reason, (amount - paid_amount) as due, created_at FROM fines WHERE member_id = ? AND status IN ('imposed','pending','appeal_upheld') AND (amount - paid_amount) > 0",
"SELECT id, notes, (amount - paid_amount) as due, created_at FROM fines WHERE member_id = ? AND status IN ('imposed','pending','appeal_upheld') AND (amount - paid_amount) > 0",
[$memberId]
);
foreach ($fines as $fine) {
......@@ -210,7 +210,7 @@ final class WaiverProcessor
'debt_type_code' => 'fine',
'period' => substr($fine['created_at'] ?? '', 0, 10),
'amount' => $fine['due'],
'description' => 'غرامة' . (!empty($fine['reason']) ? ' — ' . $fine['reason'] : '') . ' — العضو الأساسي',
'description' => 'غرامة' . (!empty($fine['notes']) ? ' — ' . $fine['notes'] : '') . ' — العضو الأساسي',
'payment_url' => '/payments/process/' . $memberId,
];
}
......
......@@ -159,10 +159,13 @@ switch ($command) {
$result = $migration->run($dryRun);
echo " Members processed: {$result['members_processed']}\n";
echo " Pre-2023 deleted: {$result['pre_2023_deleted']}\n";
echo " Inactive deps removed: {$result['inactive_removed']}\n";
echo " Subscriptions created: {$result['subscriptions_created']}\n";
echo " Orphaned IDs fixed: {$result['orphaned_ids_fixed']}\n";
echo " Duplicates removed: {$result['duplicates_removed']}\n";
echo " Amount corrections: {$result['amount_corrections']}\n";
echo " Discount corrections: {$result['discount_corrections']}\n";
echo " Fines applied: {$result['fines_applied']}\n";
if (!empty($result['errors'])) {
echo "\n ⚠️ Errors:\n";
......
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