Commit 5b4e277d authored by Mahmoud Aglan's avatar Mahmoud Aglan

dryjkftykft

parent 5ff91171
......@@ -72,8 +72,11 @@ final class ChildFeeCalculator
$breakdown[] = '👶 الترتيب: #' . $childOrder . ' — السن: ' . $childAge . ' سنة';
$breakdown[] = '📌 التصنيف: ' . ($classLabels[$classification] ?? $classification);
if ($classification === 'included') {
if ($classification === 'included' && bccomp($formFee, '0', 2) <= 0) {
$breakdown[] = '✅ مشمول في قيمة العضوية — بدون رسوم إضافية';
} elseif ($classification === 'included' && bccomp($formFee, '0', 2) > 0) {
$breakdown[] = '✅ مشمول (بدون رسوم عضوية)';
$breakdown[] = '📝 رسوم استمارة إضافة: ' . money($formFee);
} else {
if (bccomp($childFee, '0', 2) > 0) {
$breakdown[] = '📊 نسبة ' . $percentage . '% × ' . money($membershipValue) . ' = ' . money($childFee);
......
......@@ -10,26 +10,31 @@ use App\Modules\ServiceCatalog\Models\ServicePrice;
/**
* Determines whether a dependant addition needs a form fee (570 EGP).
*
* Business rule:
* - During initial membership creation (member status = potential / under_review),
* all additions are on the same initial form — no additional form fee.
* - After activation (member has membership number), any addition/modification/removal
* requires a 570 EGP form fee PER PERSON. No session sharing.
* Business rule — the boundary is the MEMBERSHIP NUMBER:
* - Before the member gets a membership number (hasn't paid membership yet),
* all additions are on the initial 505 EGP form — no extra form fee.
* This covers all pre-activation statuses: potential, under_review,
* interview_scheduled, accepted, payment_pending.
* - After activation (member has a membership number), every addition costs
* an additional 570 EGP form fee PER PERSON on top of the dependant's own fees.
* Free dependants (1st spouse, first 2 children under 18) remain free — only
* the 570 form fee applies, and free slots are still free (no 570 either).
*/
final class FormFeeService
{
/**
* Check if the member is still on the initial membership form.
* True = no membership number yet = pre-activation = all on same 505 form.
*/
public static function isOnInitialForm(array $member): bool
{
return in_array($member['status'] ?? '', ['potential', 'under_review'], true);
return empty($member['membership_number']);
}
/**
* Get the form fee that should be charged for an addition.
* Returns '0.00' if on initial form.
* Returns 570 EGP (or configured amount) per person after activation.
* Get the form fee for adding a dependant.
* Returns '0.00' if still on initial form (no membership number).
* Returns 570 EGP per person after activation.
*/
public static function getFormFee(int $memberId, array $member): string
{
......@@ -42,31 +47,43 @@ final class FormFeeService
}
/**
* Check if this is a "free" spouse slot on the initial form.
* Rule: first N spouses are free (default N=1) during initial creation.
* Check if this spouse is in a "free" slot (zero dependant fee).
* Free slots apply ALWAYS — on initial form AND after activation.
* The only difference: after activation the 570 form fee is added.
* On initial form: free slot = no fee at all.
* After activation: free slot = 0 dependant fee + 570 form fee.
*/
public static function isSpouseFreeOnInitialForm(int $memberId, int $spouseOrder, array $member): bool
public static function isSpouseFreeSlot(int $memberId, int $spouseOrder): bool
{
if (!self::isOnInitialForm($member)) {
return false;
}
$maxFree = (int) (RuleEngine::getValue('INITIAL_FREE_SPOUSES_COUNT', 'value') ?? 1);
return $spouseOrder <= $maxFree;
}
/**
* Check if this child is in a "free" slot on the initial form.
* Rule: first N children under max age are free (default N=2, age<18).
* Uses INITIAL_FREE_CHILDREN_COUNT for the initial form specifically,
* and CHILD_INCLUDED_MAX_COUNT / CHILD_INCLUDED_MAX_AGE for general classification.
* @deprecated Use isSpouseFreeSlot() instead
*/
public static function isChildFreeOnInitialForm(int $memberId, int $childOrder, int $childAge, array $member): bool
public static function isSpouseFreeOnInitialForm(int $memberId, int $spouseOrder, array $member): bool
{
return self::isSpouseFreeSlot($memberId, $spouseOrder);
}
/**
* Check if this child is in a "free" slot (zero dependant fee).
* Free slots apply ALWAYS. After activation: 0 dependant fee + 570 form fee.
* On initial form: no fee at all.
*/
public static function isChildFreeSlot(int $memberId, int $childOrder, int $childAge): bool
{
if (!self::isOnInitialForm($member)) {
return false;
}
$maxFreeCount = (int) (RuleEngine::getValue('INITIAL_FREE_CHILDREN_COUNT', 'value') ?? 2);
$maxFreeAge = (int) (RuleEngine::getValue('CHILD_INCLUDED_MAX_AGE', 'value') ?? 18);
return $childOrder <= $maxFreeCount && $childAge < $maxFreeAge;
}
/**
* @deprecated Use isChildFreeSlot() instead
*/
public static function isChildFreeOnInitialForm(int $memberId, int $childOrder, int $childAge, array $member): bool
{
return self::isChildFreeSlot($memberId, $childOrder, $childAge);
}
}
......@@ -42,7 +42,7 @@ final class SpouseFeeCalculator
$spouseOrder = $existingCount + 1;
$isInitialCreation = FormFeeService::isOnInitialForm($member);
$isFirstFreeSlot = FormFeeService::isSpouseFreeOnInitialForm($memberId, $spouseOrder, $member);
$isFreeSlot = FormFeeService::isSpouseFreeSlot($memberId, $spouseOrder);
$formFee = FormFeeService::getFormFee($memberId, $member);
......@@ -63,9 +63,13 @@ final class SpouseFeeCalculator
switch (true) {
case ($spouseOrder === 1):
if ($isFirstFreeSlot && !$isForeign) {
if ($isFreeSlot && !$isForeign) {
$percentage = '0.00';
$ruleApplied = 'الزوجة الأولى — مشمولة في قيمة العضوية الأساسية (بدون رسوم إضافية)';
if ($isInitialCreation) {
$ruleApplied = 'الزوجة الأولى — مشمولة في الاستمارة الأولى (بدون رسوم)';
} else {
$ruleApplied = 'الزوجة الأولى — مشمولة (رسوم الاستمارة فقط ' . money($formFee) . ')';
}
} elseif ($isForeign) {
$data = RuleEngine::get('SPOUSE_FOREIGN_FEE');
$percentage = $data['percentage'] ?? '15.00';
......@@ -143,7 +147,7 @@ final class SpouseFeeCalculator
'breakdown' => self::buildBreakdown(
$spouseOrder, $membershipValue, $percentage, $percentageFee,
$annualPerYear, $yearCount, $yearlyTotal,
$formFee, $totalFee, $ruleApplied, $isFirstFreeSlot && !$isForeign
$formFee, $totalFee, $ruleApplied, $isFreeSlot && !$isForeign
),
];
}
......@@ -215,8 +219,11 @@ final class SpouseFeeCalculator
$lines[] = '📋 القاعدة المطبقة: ' . $rule;
$lines[] = '💰 قيمة العضوية: ' . money($membershipValue);
if ($isFirstFree) {
$lines[] = '✅ الزوجة الأولى مشمولة في قيمة العضوية — بدون رسوم إضافية';
if ($isFirstFree && bccomp($formFee, '0', 2) <= 0) {
$lines[] = '✅ الزوجة الأولى مشمولة في الاستمارة — بدون رسوم إضافية';
} elseif ($isFirstFree && bccomp($formFee, '0', 2) > 0) {
$lines[] = '✅ الزوجة الأولى مشمولة (بدون رسوم عضوية)';
$lines[] = '📝 رسوم استمارة إضافة: ' . money($formFee);
} else {
if (bccomp($pctFee, '0', 2) > 0) {
$lines[] = "📊 نسبة {$pct}% × " . money($membershipValue) . ' = ' . money($pctFee);
......
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