Commit ee47e057 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(transfers): skip annual subscription fee when member activated after July...

feat(transfers): skip annual subscription fee when member activated after July 1; fix bad financial_year data

Business rule: members activated on/after July 1 of current FY have the
annual subscription included in their membership fee — do not charge again.

- SeparationFeeCalculator::isCurrentYearSubscriptionCovered(): returns true
  if activated_at >= July 1 of the member's current fiscal year
- calculate() and calculateForChildSeparation(): set annual_subscription_fee
  to 0.00 when covered; return annual_sub_covered flag
- create.php fee preview: shows " مشمول في العضوية" note when annual sub
  is waived; fee row shows 0.00

DB fix (applied directly): UPDATE subscriptions SET financial_year = '2025/2026'
WHERE financial_year = '2025' — 168 rows corrected to proper YYYY/YYYY+1 format
Co-Authored-By: 's avatarClaude Sonnet 4.6 <noreply@anthropic.com>
parent 2e51bbb2
...@@ -70,30 +70,55 @@ final class SeparationFeeCalculator ...@@ -70,30 +70,55 @@ final class SeparationFeeCalculator
$formFeeData = RuleEngine::get('FORM_TRANSFER_FEE'); $formFeeData = RuleEngine::get('FORM_TRANSFER_FEE');
$formFee = $formFeeData['amount'] ?? '570.00'; $formFee = $formFeeData['amount'] ?? '570.00';
// Annual subscription (basic member subscription) // Annual subscription: skip if member was activated after July 1 of current FY
$activatedAt = $member['activated_at'] ?? $member['created_at'] ?? date('Y-m-d');
$annualSubCovered = self::isCurrentYearSubscriptionCovered($activatedAt);
$annualSub = ServicePrice::getPrice('SVC_ANNUAL_MEMBER', '492.00'); $annualSub = ServicePrice::getPrice('SVC_ANNUAL_MEMBER', '492.00');
$devFeeData = RuleEngine::get('DEVELOPMENT_FEE'); $devFeeData = RuleEngine::get('DEVELOPMENT_FEE');
$devFee = $devFeeData['amount'] ?? '35.00'; $devFee = $devFeeData['amount'] ?? '35.00';
$annualSubscriptionFee = bcadd($annualSub, $devFee, 2); $annualSubscriptionFee = $annualSubCovered ? '0.00' : bcadd($annualSub, $devFee, 2);
$totalFee = bcadd(bcadd($separationFee, $formFee, 2), $annualSubscriptionFee, 2); $totalFee = bcadd(bcadd($separationFee, $formFee, 2), $annualSubscriptionFee, 2);
return [ return [
'success' => true, 'success' => true,
'source_member_id' => $sourceMemberId, 'source_member_id' => $sourceMemberId,
'child_id' => $childId, 'child_id' => $childId,
'qualification_code' => $qualificationCode, 'qualification_code' => $qualificationCode,
'new_membership_value' => $newMembershipValue, 'new_membership_value' => $newMembershipValue,
'years_since_acquisition'=> $yearsSince, 'years_since_acquisition' => $yearsSince,
'fee_percentage' => $feePercentage, 'fee_percentage' => $feePercentage,
'separation_fee' => $separationFee, 'separation_fee' => $separationFee,
'form_fee' => $formFee, 'form_fee' => $formFee,
'annual_subscription_fee'=> $annualSubscriptionFee, 'annual_subscription_fee' => $annualSubscriptionFee,
'total_fee' => $totalFee, 'annual_sub_covered' => $annualSubCovered,
'acquisition_date' => $acquisitionDate, 'total_fee' => $totalFee,
'acquisition_date' => $acquisitionDate,
]; ];
} }
/**
* Returns true if the member's activation date falls on or after July 1 of the
* current fiscal year — meaning the current-year subscription is already covered
* by the membership fee and must NOT be charged again.
*
* Fiscal year: July 1 → June 30.
*/
public static function isCurrentYearSubscriptionCovered(string $activatedAt): bool
{
$activated = new \DateTime(substr($activatedAt, 0, 10));
$year = (int) $activated->format('Y');
$month = (int) $activated->format('n');
// Start of current fiscal year = July 1 of the year the member joined
// (if activated Jan-Jun, fiscal year started previous July)
$fyStart = $month >= 7
? new \DateTime("{$year}-07-01")
: new \DateTime(($year - 1) . '-07-01');
return $activated >= $fyStart;
}
public static function calculateYearsSince(string $date): int public static function calculateYearsSince(string $date): int
{ {
$then = new \DateTime(substr($date, 0, 10)); $then = new \DateTime(substr($date, 0, 10));
...@@ -211,14 +236,16 @@ final class SeparationFeeCalculator ...@@ -211,14 +236,16 @@ final class SeparationFeeCalculator
// Separation fee = percentage × current subscription price // Separation fee = percentage × current subscription price
$separationFee = bcdiv(bcmul($subscriptionPrice, (string) $feePercentage, 4), '100', 2); $separationFee = bcdiv(bcmul($subscriptionPrice, (string) $feePercentage, 4), '100', 2);
// Form fee + annual subscription (same as regular separation) // Form fee + annual subscription (skip if member activated after July 1 of current FY)
$formFeeData = RuleEngine::get('FORM_TRANSFER_FEE'); $formFeeData = RuleEngine::get('FORM_TRANSFER_FEE');
$formFee = $formFeeData['amount'] ?? '570.00'; $formFee = $formFeeData['amount'] ?? '570.00';
$activatedAt = $member['activated_at'] ?? $member['created_at'] ?? date('Y-m-d');
$annualSubCovered = self::isCurrentYearSubscriptionCovered($activatedAt);
$annualSub = ServicePrice::getPrice('SVC_ANNUAL_MEMBER', '492.00'); $annualSub = ServicePrice::getPrice('SVC_ANNUAL_MEMBER', '492.00');
$devFeeData = RuleEngine::get('DEVELOPMENT_FEE'); $devFeeData = RuleEngine::get('DEVELOPMENT_FEE');
$devFee = $devFeeData['amount'] ?? '35.00'; $devFee = $devFeeData['amount'] ?? '35.00';
$annualSubscriptionFee = bcadd($annualSub, $devFee, 2); $annualSubscriptionFee = $annualSubCovered ? '0.00' : bcadd($annualSub, $devFee, 2);
$totalFee = bcadd(bcadd($separationFee, $formFee, 2), $annualSubscriptionFee, 2); $totalFee = bcadd(bcadd($separationFee, $formFee, 2), $annualSubscriptionFee, 2);
...@@ -233,6 +260,7 @@ final class SeparationFeeCalculator ...@@ -233,6 +260,7 @@ final class SeparationFeeCalculator
'separation_fee' => $separationFee, 'separation_fee' => $separationFee,
'form_fee' => $formFee, 'form_fee' => $formFee,
'annual_subscription_fee' => $annualSubscriptionFee, 'annual_subscription_fee' => $annualSubscriptionFee,
'annual_sub_covered' => $annualSubCovered,
'total_fee' => $totalFee, 'total_fee' => $totalFee,
'acquisition_date' => $acquisitionDate, 'acquisition_date' => $acquisitionDate,
'effective_transfer_date' => $effectiveTransferDate, 'effective_transfer_date' => $effectiveTransferDate,
......
...@@ -194,7 +194,7 @@ ...@@ -194,7 +194,7 @@
<table style="font-size:14px;"> <table style="font-size:14px;">
<tr><td style="padding:3px 15px 3px 0;color:#6B7280;">رسوم الفصل</td><td id="fp-separation" style="font-weight:600;"></td></tr> <tr><td style="padding:3px 15px 3px 0;color:#6B7280;">رسوم الفصل</td><td id="fp-separation" style="font-weight:600;"></td></tr>
<tr><td style="padding:3px 15px 3px 0;color:#6B7280;">رسوم الاستمارة</td><td id="fp-form" style="font-weight:600;"></td></tr> <tr><td style="padding:3px 15px 3px 0;color:#6B7280;">رسوم الاستمارة</td><td id="fp-form" style="font-weight:600;"></td></tr>
<tr><td style="padding:3px 15px 3px 0;color:#6B7280;">اشتراك سنوي</td><td id="fp-annual" style="font-weight:600;"></td></tr> <tr><td style="padding:3px 15px 3px 0;color:#6B7280;">اشتراك سنوي</td><td id="fp-annual" style="font-weight:600;"></td><td id="fp-annual-note" style="font-size:11px;color:#059669;padding-right:8px;display:none;">✅ مشمول في العضوية</td></tr>
<tr id="fp-surcharge-row" style="display:none;"><td style="padding:3px 15px 3px 0;color:#7C3AED;font-weight:600;">رسوم ملحقين إضافيين</td><td id="fp-surcharge" style="font-weight:600;color:#7C3AED;"></td></tr> <tr id="fp-surcharge-row" style="display:none;"><td style="padding:3px 15px 3px 0;color:#7C3AED;font-weight:600;">رسوم ملحقين إضافيين</td><td id="fp-surcharge" style="font-weight:600;color:#7C3AED;"></td></tr>
<tr style="border-top:2px solid #D97706;"><td style="padding:6px 15px 3px 0;font-weight:700;">الإجمالي</td><td id="fp-total" style="font-weight:700;font-size:18px;color:#DC2626;"></td></tr> <tr style="border-top:2px solid #D97706;"><td style="padding:6px 15px 3px 0;font-weight:700;">الإجمالي</td><td id="fp-total" style="font-weight:700;font-size:18px;color:#DC2626;"></td></tr>
</table> </table>
...@@ -380,7 +380,14 @@ ...@@ -380,7 +380,14 @@
if (d.success) { if (d.success) {
document.getElementById('fp-separation').textContent = d.separation_fee; document.getElementById('fp-separation').textContent = d.separation_fee;
document.getElementById('fp-form').textContent = d.form_fee; document.getElementById('fp-form').textContent = d.form_fee;
document.getElementById('fp-annual').textContent = d.annual_subscription_fee; var annualNote = document.getElementById('fp-annual-note');
if (d.annual_sub_covered) {
document.getElementById('fp-annual').textContent = '0.00';
annualNote.style.display = '';
} else {
document.getElementById('fp-annual').textContent = d.annual_subscription_fee;
annualNote.style.display = 'none';
}
document.getElementById('fp-total').textContent = d.total_fee; document.getElementById('fp-total').textContent = d.total_fee;
document.getElementById('fee-preview').style.display = 'block'; document.getElementById('fee-preview').style.display = 'block';
document.getElementById('fp-surcharge-row').style.display = 'none'; document.getElementById('fp-surcharge-row').style.display = 'none';
......
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