Commit 68851585 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(subscriptions): mark dependents added after FY start as paid (not pending)

Dependents added after July 1st already pay their subscription via the
addition fee. Instead of skipping them entirely, create the subscription
row with status='paid' so they appear in the subscription list correctly.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 044de3a2
This diff is collapsed.
...@@ -101,9 +101,9 @@ final class SubscriptionGenerator ...@@ -101,9 +101,9 @@ final class SubscriptionGenerator
if (str_contains($e->getMessage(), 'Duplicate entry')) { $skipped++; continue; } else { throw $e; } if (str_contains($e->getMessage(), 'Duplicate entry')) { $skipped++; continue; } else { throw $e; }
} }
// Spouses — NO dev fee; skip if added on/after FY start (subscription included in addition fee) // Spouses — NO dev fee; if added on/after FY start, mark as paid (already included in addition fee)
if ($db->tableExists('spouses')) { if ($db->tableExists('spouses')) {
$spouses = $db->select("SELECT id, full_name_ar FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active' AND created_at < ?", [$memberId, $fyStartDate]); $spouses = $db->select("SELECT id, full_name_ar, created_at FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [$memberId]);
foreach ($spouses as $sp) { foreach ($spouses as $sp) {
$existingSp = $db->selectOne( $existingSp = $db->selectOne(
"SELECT id FROM subscriptions WHERE member_id = ? AND financial_year = ? AND person_type = 'spouse' AND person_id = ?", "SELECT id FROM subscriptions WHERE member_id = ? AND financial_year = ? AND person_type = 'spouse' AND person_id = ?",
...@@ -112,6 +112,7 @@ final class SubscriptionGenerator ...@@ -112,6 +112,7 @@ final class SubscriptionGenerator
if ($existingSp) { $skipped++; continue; } if ($existingSp) { $skipped++; continue; }
$spDiscount = $discountPct ? bcdiv(bcmul($spouseRate, $discountPct, 4), '100', 2) : '0.00'; $spDiscount = $discountPct ? bcdiv(bcmul($spouseRate, $discountPct, 4), '100', 2) : '0.00';
$spTotal = bcsub($spouseRate, $spDiscount, 2); $spTotal = bcsub($spouseRate, $spDiscount, 2);
$spStatus = ($sp['created_at'] >= $fyStartDate) ? 'paid' : 'pending';
try { try {
$db->insert('subscriptions', [ $db->insert('subscriptions', [
'member_id' => $memberId, 'member_id' => $memberId,
...@@ -123,7 +124,7 @@ final class SubscriptionGenerator ...@@ -123,7 +124,7 @@ final class SubscriptionGenerator
'development_fee' => '0.00', 'development_fee' => '0.00',
'discount_amount' => $spDiscount, 'discount_amount' => $spDiscount,
'total_amount' => $spTotal, 'total_amount' => $spTotal,
'status' => 'pending', 'status' => $spStatus,
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
'created_by' => $empId, 'created_by' => $empId,
...@@ -135,9 +136,9 @@ final class SubscriptionGenerator ...@@ -135,9 +136,9 @@ final class SubscriptionGenerator
} }
} }
// Children — NO dev fee; skip if added on/after FY start (subscription included in addition fee) // Children — NO dev fee; if added on/after FY start, mark as paid (already included in addition fee)
if ($db->tableExists('children')) { if ($db->tableExists('children')) {
$children = $db->select("SELECT id, full_name_ar FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active' AND created_at < ?", [$memberId, $fyStartDate]); $children = $db->select("SELECT id, full_name_ar, created_at FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [$memberId]);
foreach ($children as $ch) { foreach ($children as $ch) {
$existingCh = $db->selectOne( $existingCh = $db->selectOne(
"SELECT id FROM subscriptions WHERE member_id = ? AND financial_year = ? AND person_type = 'child' AND person_id = ?", "SELECT id FROM subscriptions WHERE member_id = ? AND financial_year = ? AND person_type = 'child' AND person_id = ?",
...@@ -146,6 +147,7 @@ final class SubscriptionGenerator ...@@ -146,6 +147,7 @@ final class SubscriptionGenerator
if ($existingCh) { $skipped++; continue; } if ($existingCh) { $skipped++; continue; }
$chDiscount = $discountPct ? bcdiv(bcmul($childRate, $discountPct, 4), '100', 2) : '0.00'; $chDiscount = $discountPct ? bcdiv(bcmul($childRate, $discountPct, 4), '100', 2) : '0.00';
$chTotal = bcsub($childRate, $chDiscount, 2); $chTotal = bcsub($childRate, $chDiscount, 2);
$chStatus = ($ch['created_at'] >= $fyStartDate) ? 'paid' : 'pending';
try { try {
$db->insert('subscriptions', [ $db->insert('subscriptions', [
'member_id' => $memberId, 'member_id' => $memberId,
...@@ -157,7 +159,7 @@ final class SubscriptionGenerator ...@@ -157,7 +159,7 @@ final class SubscriptionGenerator
'development_fee' => '0.00', 'development_fee' => '0.00',
'discount_amount' => $chDiscount, 'discount_amount' => $chDiscount,
'total_amount' => $chTotal, 'total_amount' => $chTotal,
'status' => 'pending', 'status' => $chStatus,
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
'created_by' => $empId, 'created_by' => $empId,
...@@ -169,9 +171,9 @@ final class SubscriptionGenerator ...@@ -169,9 +171,9 @@ final class SubscriptionGenerator
} }
} }
// Temporary members — NO dev fee; skip if added on/after FY start (subscription included in addition fee) // Temporary members — NO dev fee; if added on/after FY start, mark as paid (already included in addition fee)
if ($db->tableExists('temporary_members')) { if ($db->tableExists('temporary_members')) {
$temps = $db->select("SELECT id, full_name_ar FROM temporary_members WHERE member_id = ? AND is_archived = 0 AND status = 'active' AND created_at < ?", [$memberId, $fyStartDate]); $temps = $db->select("SELECT id, full_name_ar, created_at FROM temporary_members WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [$memberId]);
foreach ($temps as $t) { foreach ($temps as $t) {
$existingTmp = $db->selectOne( $existingTmp = $db->selectOne(
"SELECT id FROM subscriptions WHERE member_id = ? AND financial_year = ? AND person_type = 'temporary' AND person_id = ?", "SELECT id FROM subscriptions WHERE member_id = ? AND financial_year = ? AND person_type = 'temporary' AND person_id = ?",
...@@ -180,6 +182,7 @@ final class SubscriptionGenerator ...@@ -180,6 +182,7 @@ final class SubscriptionGenerator
if ($existingTmp) { $skipped++; continue; } if ($existingTmp) { $skipped++; continue; }
$tmpDiscount = $discountPct ? bcdiv(bcmul($tempRate, $discountPct, 4), '100', 2) : '0.00'; $tmpDiscount = $discountPct ? bcdiv(bcmul($tempRate, $discountPct, 4), '100', 2) : '0.00';
$tmpTotal = bcsub($tempRate, $tmpDiscount, 2); $tmpTotal = bcsub($tempRate, $tmpDiscount, 2);
$tmpStatus = ($t['created_at'] >= $fyStartDate) ? 'paid' : 'pending';
try { try {
$db->insert('subscriptions', [ $db->insert('subscriptions', [
'member_id' => $memberId, 'member_id' => $memberId,
...@@ -191,7 +194,7 @@ final class SubscriptionGenerator ...@@ -191,7 +194,7 @@ final class SubscriptionGenerator
'development_fee' => '0.00', 'development_fee' => '0.00',
'discount_amount' => $tmpDiscount, 'discount_amount' => $tmpDiscount,
'total_amount' => $tmpTotal, 'total_amount' => $tmpTotal,
'status' => 'pending', 'status' => $tmpStatus,
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
'created_by' => $empId, 'created_by' => $empId,
......
...@@ -191,6 +191,12 @@ final class SubscriptionSyncService ...@@ -191,6 +191,12 @@ final class SubscriptionSyncService
$empId = $employee ? (int) $employee->id : null; $empId = $employee ? (int) $employee->id : null;
$ts = date('Y-m-d H:i:s'); $ts = date('Y-m-d H:i:s');
// Dependents added on/after FY start already paid subscription via addition fee
$fyStartYear = (int) (explode('/', $fy)[0] ?? date('Y'));
$fyStartDate = $fyStartYear . '-07-01';
$depCreatedAt = $db->selectOne("SELECT created_at FROM {$table} WHERE id = ?", [$personId]);
$status = ($depCreatedAt && $depCreatedAt['created_at'] >= $fyStartDate) ? 'paid' : 'pending';
try { try {
$db->insert('subscriptions', [ $db->insert('subscriptions', [
'member_id' => $memberId, 'member_id' => $memberId,
...@@ -202,12 +208,12 @@ final class SubscriptionSyncService ...@@ -202,12 +208,12 @@ final class SubscriptionSyncService
'development_fee' => '0.00', 'development_fee' => '0.00',
'discount_amount' => $discount, 'discount_amount' => $discount,
'total_amount' => $total, 'total_amount' => $total,
'status' => 'pending', 'status' => $status,
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
'created_by' => $empId, 'created_by' => $empId,
]); ]);
Logger::info("SubscriptionSyncService: added {$personType} #{$personId} to FY {$fy} for member #{$memberId}"); Logger::info("SubscriptionSyncService: added {$personType} #{$personId} to FY {$fy} for member #{$memberId} (status={$status})");
} catch (\Throwable $insertErr) { } catch (\Throwable $insertErr) {
if (str_contains($insertErr->getMessage(), 'Duplicate entry')) { if (str_contains($insertErr->getMessage(), 'Duplicate entry')) {
Logger::info("SubscriptionSyncService: {$personType} #{$personId} FY {$fy} already exists (race guard)"); Logger::info("SubscriptionSyncService: {$personType} #{$personId} FY {$fy} already exists (race guard)");
......
...@@ -77,11 +77,11 @@ ...@@ -77,11 +77,11 @@
## ملخص المسارات ## ملخص المسارات
| العملية | المسار في السيستم | | العملية | المسار في السيستم |
|---------|------------------| | ------------------------------ | ----------------------------- |
| إدارة الخصومات الخاصة | التسعير ← الخصومات الخاصة | | إدارة الخصومات الخاصة | التسعير ← الخصومات الخاصة |
| تطبيق خصم على عضو | صفحة العضو ← قسم "خصم خاص" | | تطبيق خصم على عضو | صفحة العضو ← قسم "خصم خاص" |
| خصومات سنوية (rate adjustment) | التسعير ← لوحة التسعير | | خصومات سنوية (rate adjustment) | التسعير ← لوحة التسعير |
| عروض مجلس الإدارة | (تلقائية — لا يوجد UI حالياً) | | عروض مجلس الإدارة | (تلقائية — لا يوجد UI حالياً) |
| خصومات الأكاديميات | (تلقائية عند التسجيل) | | خصومات الأكاديميات | (تلقائية عند التسجيل) |
| خصومات الموسمي | (تلقائية عند الإنشاء) | | خصومات الموسمي | (تلقائية عند الإنشاء) |
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