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

Fix transfer fee calculation and remove dev fee from dependents

- SeparationFeeCalculator: handle NULL qualification_id with fallback
  pricing lookup, use actual membership_type instead of hardcoded 'working'
- SeparationFeeCalculator companion surcharge: use current pricing_configs
  instead of stale member.membership_value
- SubscriptionGeneratorJob: set development_fee=0.00 for spouse/child/temp
  (matching SubscriptionGenerator service that was already fixed)
- RetroactiveMembershipService: default dev fee to 0.00 for non-member types
- RetroactiveWizardController: same default logic for missing form fields
- Retroactive wizard JS: generate 0.00 dev fee for dependent subscriptions
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent c8353344
...@@ -262,7 +262,7 @@ class RetroactiveWizardController extends Controller ...@@ -262,7 +262,7 @@ class RetroactiveWizardController extends Controller
'person_index' => (int) ($post["sub_person_index_{$i}"] ?? 0), 'person_index' => (int) ($post["sub_person_index_{$i}"] ?? 0),
'person_name' => $post["sub_person_name_{$i}"] ?? $data['full_name_ar'], 'person_name' => $post["sub_person_name_{$i}"] ?? $data['full_name_ar'],
'base_amount' => $post["sub_base_{$i}"] ?? '492.00', 'base_amount' => $post["sub_base_{$i}"] ?? '492.00',
'development_fee' => $post["sub_dev_fee_{$i}"] ?? '35.00', 'development_fee' => $post["sub_dev_fee_{$i}"] ?? (($post["sub_person_type_{$i}"] ?? 'member') === 'member' ? '35.00' : '0.00'),
'total_amount' => $post["sub_total_{$i}"] ?? '527.00', 'total_amount' => $post["sub_total_{$i}"] ?? '527.00',
'paid_amount' => $post["sub_paid_{$i}"] ?? '0.00', 'paid_amount' => $post["sub_paid_{$i}"] ?? '0.00',
'fine_amount' => $post["sub_fine_{$i}"] ?? '0.00', 'fine_amount' => $post["sub_fine_{$i}"] ?? '0.00',
......
...@@ -666,7 +666,7 @@ final class RetroactiveMembershipService ...@@ -666,7 +666,7 @@ final class RetroactiveMembershipService
'person_id' => $sub['person_id'] ?? $memberId, 'person_id' => $sub['person_id'] ?? $memberId,
'person_name' => $sub['person_name'] ?? '', 'person_name' => $sub['person_name'] ?? '',
'base_amount' => (string) ($sub['base_amount'] ?? '0.00'), 'base_amount' => (string) ($sub['base_amount'] ?? '0.00'),
'development_fee' => (string) ($sub['development_fee'] ?? '35.00'), 'development_fee' => (string) ($sub['development_fee'] ?? (($sub['person_type'] ?? 'member') === 'member' ? '35.00' : '0.00')),
'discount_amount' => (string) ($sub['discount_amount'] ?? '0.00'), 'discount_amount' => (string) ($sub['discount_amount'] ?? '0.00'),
'total_amount' => (string) ($sub['total_amount'] ?? '0.00'), 'total_amount' => (string) ($sub['total_amount'] ?? '0.00'),
'paid_amount' => $paidAmount, 'paid_amount' => $paidAmount,
......
...@@ -1011,7 +1011,7 @@ function generateSubscriptionYears() { ...@@ -1011,7 +1011,7 @@ function generateSubscriptionYears() {
const fy = y + '/' + (y + 1); const fy = y + '/' + (y + 1);
const isPast = y < currentYear - 1; const isPast = y < currentYear - 1;
const status = isPast ? 'paid' : (y === currentYear - 1 ? 'overdue' : 'pending'); const status = isPast ? 'paid' : (y === currentYear - 1 ? 'overdue' : 'pending');
addSubscriptionRow(fy, status, '492.00', '35.00', 'spouse', s, spouseName); addSubscriptionRow(fy, status, '492.00', '0.00', 'spouse', s, spouseName);
} }
} }
...@@ -1027,7 +1027,7 @@ function generateSubscriptionYears() { ...@@ -1027,7 +1027,7 @@ function generateSubscriptionYears() {
const fy = y + '/' + (y + 1); const fy = y + '/' + (y + 1);
const isPast = y < currentYear - 1; const isPast = y < currentYear - 1;
const status = isPast ? 'paid' : (y === currentYear - 1 ? 'overdue' : 'pending'); const status = isPast ? 'paid' : (y === currentYear - 1 ? 'overdue' : 'pending');
addSubscriptionRow(fy, status, '222.00', '35.00', 'child', c, childName); addSubscriptionRow(fy, status, '222.00', '0.00', 'child', c, childName);
} }
} }
...@@ -1043,7 +1043,7 @@ function generateSubscriptionYears() { ...@@ -1043,7 +1043,7 @@ function generateSubscriptionYears() {
const fy = y + '/' + (y + 1); const fy = y + '/' + (y + 1);
const isPast = y < currentYear - 1; const isPast = y < currentYear - 1;
const status = isPast ? 'paid' : (y === currentYear - 1 ? 'overdue' : 'pending'); const status = isPast ? 'paid' : (y === currentYear - 1 ? 'overdue' : 'pending');
addSubscriptionRow(fy, status, '222.00', '35.00', 'temporary', t, tempName); addSubscriptionRow(fy, status, '222.00', '0.00', 'temporary', t, tempName);
} }
} }
} }
......
...@@ -22,15 +22,39 @@ final class SeparationFeeCalculator ...@@ -22,15 +22,39 @@ final class SeparationFeeCalculator
} }
// Determine qualification code // Determine qualification code
if ($qualificationCode === null && $member['qualification_id']) { if ($qualificationCode === null && !empty($member['qualification_id'])) {
$qual = $db->selectOne("SELECT code FROM qualifications WHERE id = ?", [(int) $member['qualification_id']]); $qual = $db->selectOne("SELECT code FROM qualifications WHERE id = ?", [(int) $member['qualification_id']]);
$qualificationCode = $qual['code'] ?? 'high'; $qualificationCode = $qual['code'] ?? null;
} }
// Get new membership value at current date // Get new membership value at current date from pricing_configs
$branchId = (int) $member['branch_id']; $branchId = (int) ($member['branch_id'] ?? 1);
$priceInfo = PricingEngine::getMembershipPrice($branchId, $qualificationCode ?? 'high'); $mType = $member['membership_type'] ?? 'working';
$newMembershipValue = $priceInfo['price'] ?? '0.00'; $newMembershipValue = '0.00';
if ($qualificationCode) {
$priceInfo = PricingEngine::getMembershipPrice($branchId, $qualificationCode);
$newMembershipValue = $priceInfo['price'] ?? '0.00';
}
// Fallback: query pricing_configs directly (handles NULL qualification or membership_type mismatch)
if (bccomp($newMembershipValue, '0.01', 2) < 0) {
$qualId = !empty($member['qualification_id']) ? (int) $member['qualification_id'] : null;
if ($qualId) {
$pricing = $db->selectOne(
"SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1",
[$branchId, $qualId, $mType]
);
} else {
$pricing = $db->selectOne(
"SELECT price FROM pricing_configs WHERE branch_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY price ASC LIMIT 1",
[$branchId, $mType]
);
}
if ($pricing && bccomp($pricing['price'], '0.01', 2) >= 0) {
$newMembershipValue = $pricing['price'];
}
}
// Calculate years since acquisition // Calculate years since acquisition
$acquisitionDate = $member['created_at'] ?? $member['form_date'] ?? date('Y-m-d'); $acquisitionDate = $member['created_at'] ?? $member['form_date'] ?? date('Y-m-d');
...@@ -155,7 +179,25 @@ final class SeparationFeeCalculator ...@@ -155,7 +179,25 @@ final class SeparationFeeCalculator
$breakdown = []; $breakdown = [];
$surcharge = '0.00'; $surcharge = '0.00';
// Use current pricing (same logic as calculate())
$branchId = (int) ($member['branch_id'] ?? 1);
$mType = $member['membership_type'] ?? 'working';
$membershipValue = $member['membership_value'] ?? '0.00'; $membershipValue = $member['membership_value'] ?? '0.00';
$qualId = !empty($member['qualification_id']) ? (int) $member['qualification_id'] : null;
if ($qualId) {
$currentPricing = $db->selectOne(
"SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1",
[$branchId, $qualId, $mType]
);
} else {
$currentPricing = $db->selectOne(
"SELECT price FROM pricing_configs WHERE branch_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY price ASC LIMIT 1",
[$branchId, $mType]
);
}
if ($currentPricing && bccomp($currentPricing['price'], '0.01', 2) >= 0) {
$membershipValue = $currentPricing['price'];
}
// Extra spouses: use tiered rules per ordinal position // Extra spouses: use tiered rules per ordinal position
$extraSpouses = max(0, $targetSpousesCount - $sourceSpouses); $extraSpouses = max(0, $targetSpousesCount - $sourceSpouses);
......
...@@ -58,7 +58,7 @@ class SubscriptionGeneratorJob ...@@ -58,7 +58,7 @@ class SubscriptionGeneratorJob
]); ]);
$processed++; $processed++;
// Generate for spouses // Generate for spouses (NO dev fee for dependents)
$spouses = $this->db->select("SELECT id, full_name_ar FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [(int) $m['id']]); $spouses = $this->db->select("SELECT id, full_name_ar FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [(int) $m['id']]);
foreach ($spouses as $sp) { foreach ($spouses as $sp) {
$this->db->insert('subscriptions', [ $this->db->insert('subscriptions', [
...@@ -68,8 +68,8 @@ class SubscriptionGeneratorJob ...@@ -68,8 +68,8 @@ class SubscriptionGeneratorJob
'person_id' => (int) $sp['id'], 'person_id' => (int) $sp['id'],
'person_name' => $sp['full_name_ar'], 'person_name' => $sp['full_name_ar'],
'base_amount' => $spouseRate, 'base_amount' => $spouseRate,
'development_fee'=> $devFee, 'development_fee'=> '0.00',
'total_amount' => bcadd($spouseRate, $devFee, 2), 'total_amount' => $spouseRate,
'status' => 'pending', 'status' => 'pending',
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
...@@ -77,7 +77,7 @@ class SubscriptionGeneratorJob ...@@ -77,7 +77,7 @@ class SubscriptionGeneratorJob
$processed++; $processed++;
} }
// Generate for children // Generate for children (NO dev fee for dependents)
$children = $this->db->select("SELECT id, full_name_ar FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [(int) $m['id']]); $children = $this->db->select("SELECT id, full_name_ar FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [(int) $m['id']]);
foreach ($children as $ch) { foreach ($children as $ch) {
$this->db->insert('subscriptions', [ $this->db->insert('subscriptions', [
...@@ -87,8 +87,8 @@ class SubscriptionGeneratorJob ...@@ -87,8 +87,8 @@ class SubscriptionGeneratorJob
'person_id' => (int) $ch['id'], 'person_id' => (int) $ch['id'],
'person_name' => $ch['full_name_ar'], 'person_name' => $ch['full_name_ar'],
'base_amount' => $childRate, 'base_amount' => $childRate,
'development_fee'=> $devFee, 'development_fee'=> '0.00',
'total_amount' => bcadd($childRate, $devFee, 2), 'total_amount' => $childRate,
'status' => 'pending', 'status' => 'pending',
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
...@@ -96,7 +96,7 @@ class SubscriptionGeneratorJob ...@@ -96,7 +96,7 @@ class SubscriptionGeneratorJob
$processed++; $processed++;
} }
// Generate for temporary members // Generate for temporary members (NO dev fee for dependents)
$temps = $this->db->select("SELECT id, full_name_ar FROM temporary_members WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [(int) $m['id']]); $temps = $this->db->select("SELECT id, full_name_ar FROM temporary_members WHERE member_id = ? AND is_archived = 0 AND status = 'active'", [(int) $m['id']]);
foreach ($temps as $tmp) { foreach ($temps as $tmp) {
$this->db->insert('subscriptions', [ $this->db->insert('subscriptions', [
...@@ -106,8 +106,8 @@ class SubscriptionGeneratorJob ...@@ -106,8 +106,8 @@ class SubscriptionGeneratorJob
'person_id' => (int) $tmp['id'], 'person_id' => (int) $tmp['id'],
'person_name' => $tmp['full_name_ar'], 'person_name' => $tmp['full_name_ar'],
'base_amount' => $tempRate, 'base_amount' => $tempRate,
'development_fee'=> $devFee, 'development_fee'=> '0.00',
'total_amount' => bcadd($tempRate, $devFee, 2), 'total_amount' => $tempRate,
'status' => 'pending', 'status' => 'pending',
'created_at' => $ts, 'created_at' => $ts,
'updated_at' => $ts, 'updated_at' => $ts,
......
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