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
'person_index' => (int) ($post["sub_person_index_{$i}"] ?? 0),
'person_name' => $post["sub_person_name_{$i}"] ?? $data['full_name_ar'],
'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',
'paid_amount' => $post["sub_paid_{$i}"] ?? '0.00',
'fine_amount' => $post["sub_fine_{$i}"] ?? '0.00',
......
......@@ -666,7 +666,7 @@ final class RetroactiveMembershipService
'person_id' => $sub['person_id'] ?? $memberId,
'person_name' => $sub['person_name'] ?? '',
'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'),
'total_amount' => (string) ($sub['total_amount'] ?? '0.00'),
'paid_amount' => $paidAmount,
......
......@@ -1011,7 +1011,7 @@ function generateSubscriptionYears() {
const fy = y + '/' + (y + 1);
const isPast = y < currentYear - 1;
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() {
const fy = y + '/' + (y + 1);
const isPast = y < currentYear - 1;
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() {
const fy = y + '/' + (y + 1);
const isPast = y < currentYear - 1;
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
}
// 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']]);
$qualificationCode = $qual['code'] ?? 'high';
$qualificationCode = $qual['code'] ?? null;
}
// Get new membership value at current date
$branchId = (int) $member['branch_id'];
$priceInfo = PricingEngine::getMembershipPrice($branchId, $qualificationCode ?? 'high');
$newMembershipValue = $priceInfo['price'] ?? '0.00';
// Get new membership value at current date from pricing_configs
$branchId = (int) ($member['branch_id'] ?? 1);
$mType = $member['membership_type'] ?? 'working';
$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
$acquisitionDate = $member['created_at'] ?? $member['form_date'] ?? date('Y-m-d');
......@@ -155,7 +179,25 @@ final class SeparationFeeCalculator
$breakdown = [];
$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';
$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
$extraSpouses = max(0, $targetSpousesCount - $sourceSpouses);
......
......@@ -58,7 +58,7 @@ class SubscriptionGeneratorJob
]);
$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']]);
foreach ($spouses as $sp) {
$this->db->insert('subscriptions', [
......@@ -68,8 +68,8 @@ class SubscriptionGeneratorJob
'person_id' => (int) $sp['id'],
'person_name' => $sp['full_name_ar'],
'base_amount' => $spouseRate,
'development_fee'=> $devFee,
'total_amount' => bcadd($spouseRate, $devFee, 2),
'development_fee'=> '0.00',
'total_amount' => $spouseRate,
'status' => 'pending',
'created_at' => $ts,
'updated_at' => $ts,
......@@ -77,7 +77,7 @@ class SubscriptionGeneratorJob
$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']]);
foreach ($children as $ch) {
$this->db->insert('subscriptions', [
......@@ -87,8 +87,8 @@ class SubscriptionGeneratorJob
'person_id' => (int) $ch['id'],
'person_name' => $ch['full_name_ar'],
'base_amount' => $childRate,
'development_fee'=> $devFee,
'total_amount' => bcadd($childRate, $devFee, 2),
'development_fee'=> '0.00',
'total_amount' => $childRate,
'status' => 'pending',
'created_at' => $ts,
'updated_at' => $ts,
......@@ -96,7 +96,7 @@ class SubscriptionGeneratorJob
$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']]);
foreach ($temps as $tmp) {
$this->db->insert('subscriptions', [
......@@ -106,8 +106,8 @@ class SubscriptionGeneratorJob
'person_id' => (int) $tmp['id'],
'person_name' => $tmp['full_name_ar'],
'base_amount' => $tempRate,
'development_fee'=> $devFee,
'total_amount' => bcadd($tempRate, $devFee, 2),
'development_fee'=> '0.00',
'total_amount' => $tempRate,
'status' => 'pending',
'created_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