Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
Clubphp
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Clubphp
Commits
2b105af3
Commit
2b105af3
authored
May 10, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
koool
parent
1c230fd3
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
2050 additions
and
5 deletions
+2050
-5
AutoFreezeService.php
app/Modules/Members/Services/AutoFreezeService.php
+163
-0
MembershipRulesService.php
app/Modules/Members/Services/MembershipRulesService.php
+1191
-0
WaiverService.php
app/Modules/Members/Services/WaiverService.php
+165
-0
SubscriptionGenerator.php
app/Modules/Subscriptions/Services/SubscriptionGenerator.php
+18
-5
SeparationFeeCalculator.php
app/Modules/Transfers/Services/SeparationFeeCalculator.php
+25
-0
TransferEligibility.php
app/Modules/Transfers/Services/TransferEligibility.php
+180
-0
Phase_46_001_seed_subscription_rates_and_prices.php
...seeds/Phase_46_001_seed_subscription_rates_and_prices.php
+308
-0
No files found.
app/Modules/Members/Services/AutoFreezeService.php
0 → 100644
View file @
2b105af3
<?php
declare
(
strict_types
=
1
);
namespace
App\Modules\Members\Services
;
use
App\Core\App
;
/**
* Handles automatic freezing of male children who reach age 25,
* subscription blocking checks, and carnet printing eligibility.
*/
final
class
AutoFreezeService
{
/**
* Process automatic freeze for male children aged 25+.
* Sets is_frozen = 1 with reason indicating they must convert to independent membership.
*
* @return array{frozen_count: int, processed: int}
*/
public
static
function
processAutoFreeze
()
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$children
=
$db
->
select
(
"SELECT id, date_of_birth FROM children
WHERE gender = 'male'
AND is_frozen = 0
AND is_archived = 0
AND status = 'active'"
);
$processed
=
count
(
$children
);
$frozenCount
=
0
;
$now
=
date
(
'Y-m-d H:i:s'
);
$today
=
new
\DateTimeImmutable
(
'today'
);
foreach
(
$children
as
$child
)
{
$dob
=
new
\DateTimeImmutable
(
$child
[
'date_of_birth'
]);
$age
=
(
int
)
$dob
->
diff
(
$today
)
->
y
;
if
(
$age
>=
25
)
{
$db
->
update
(
'children'
,
[
'is_frozen'
=>
1
,
'frozen_at'
=>
$now
,
'frozen_reason'
=>
'بلوغ سن 25 عام - يجب التحويل لعضوية مستقلة'
,
],
'id = ?'
,
[
$child
[
'id'
]]
);
$frozenCount
++
;
}
}
return
[
'frozen_count'
=>
$frozenCount
,
'processed'
=>
$processed
,
];
}
/**
* Check if a member has unpaid subscriptions for the current financial year.
* Blocks certain operations if annual subscription is not paid.
*
* @return array{blocked: bool, reason?: string, unpaid_amount?: string}
*/
public
static
function
checkSubscriptionBlock
(
int
$memberId
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$financialYear
=
self
::
getCurrentFinancialYear
();
$unpaid
=
$db
->
select
(
"SELECT total_amount, paid_amount FROM subscriptions
WHERE member_id = ?
AND financial_year = ?
AND status IN ('pending', 'overdue')"
,
[
$memberId
,
$financialYear
]
);
if
(
!
empty
(
$unpaid
))
{
$unpaidAmount
=
'0.00'
;
foreach
(
$unpaid
as
$row
)
{
$remaining
=
bcsub
(
$row
[
'total_amount'
],
$row
[
'paid_amount'
],
2
);
$unpaidAmount
=
bcadd
(
$unpaidAmount
,
$remaining
,
2
);
}
return
[
'blocked'
=>
true
,
'reason'
=>
'لم يتم سداد الاشتراك السنوي'
,
'unpaid_amount'
=>
$unpaidAmount
,
];
}
return
[
'blocked'
=>
false
];
}
/**
* Check if a member is eligible to print their carnet (membership card).
* Blocked if subscription unpaid, member is suspended, or member is frozen.
*
* @return array{allowed: bool, reason?: string}
*/
public
static
function
canPrintCarnet
(
int
$memberId
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
// Check member status
$member
=
$db
->
selectOne
(
"SELECT status FROM members WHERE id = ? AND is_archived = 0"
,
[
$memberId
]
);
if
(
!
$member
)
{
return
[
'allowed'
=>
false
,
'reason'
=>
'العضو غير موجود'
,
];
}
if
(
$member
[
'status'
]
===
'suspended'
)
{
return
[
'allowed'
=>
false
,
'reason'
=>
'العضوية موقوفة'
,
];
}
if
(
$member
[
'status'
]
===
'frozen'
)
{
return
[
'allowed'
=>
false
,
'reason'
=>
'العضوية مجمدة'
,
];
}
// Check subscription block
$subscriptionCheck
=
self
::
checkSubscriptionBlock
(
$memberId
);
if
(
$subscriptionCheck
[
'blocked'
])
{
return
[
'allowed'
=>
false
,
'reason'
=>
$subscriptionCheck
[
'reason'
],
];
}
return
[
'allowed'
=>
true
];
}
/**
* Get current financial year string.
* FY runs July to June: if month >= 7, FY is "thisYear/thisYear+1", else "lastYear/thisYear".
*/
private
static
function
getCurrentFinancialYear
()
:
string
{
$month
=
(
int
)
date
(
'n'
);
$year
=
(
int
)
date
(
'Y'
);
if
(
$month
>=
7
)
{
return
$year
.
'/'
.
(
$year
+
1
);
}
return
(
$year
-
1
)
.
'/'
.
$year
;
}
}
app/Modules/Members/Services/MembershipRulesService.php
0 → 100644
View file @
2b105af3
This diff is collapsed.
Click to expand it.
app/Modules/Members/Services/WaiverService.php
0 → 100644
View file @
2b105af3
<?php
declare
(
strict_types
=
1
);
namespace
App\Modules\Members\Services
;
use
App\Core\App
;
use
App\Core\Logger
;
/**
* Handles membership waiver (التنازل عن العضوية).
* A waiver transfers the membership from one person to another with board approval.
*/
final
class
WaiverService
{
private
const
WAIVER_FEE_PERCENTAGE
=
'30.00'
;
/**
* Calculate the waiver fee for a member based on their current membership value.
* Fee = 30% of current membership value.
*
* @return array{membership_value: string, percentage: string, waiver_fee: string, annual_subscription_required: bool}
*/
public
static
function
calculateWaiverFee
(
int
$memberId
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$member
=
$db
->
selectOne
(
"SELECT id, membership_value, qualification_id FROM members WHERE id = ? AND is_archived = 0"
,
[
$memberId
]
);
if
(
!
$member
)
{
return
[
'membership_value'
=>
'0.00'
,
'percentage'
=>
self
::
WAIVER_FEE_PERCENTAGE
,
'waiver_fee'
=>
'0.00'
,
'annual_subscription_required'
=>
true
,
];
}
$membershipValue
=
$member
[
'membership_value'
]
??
'0.00'
;
// If membership_value is zero or not set, try service_catalog based on qualification
if
(
bccomp
(
$membershipValue
,
'0'
,
2
)
<=
0
&&
!
empty
(
$member
[
'qualification_id'
]))
{
$qual
=
$db
->
selectOne
(
"SELECT code FROM qualifications WHERE id = ?"
,
[(
int
)
$member
[
'qualification_id'
]]
);
if
(
$qual
&&
!
empty
(
$qual
[
'code'
]))
{
$serviceCode
=
'SVC_MEMBERSHIP_'
.
strtoupper
(
$qual
[
'code'
]);
$catalogValue
=
\App\Modules\ServiceCatalog\Models\ServicePrice
::
getPrice
(
$serviceCode
);
if
(
$catalogValue
!==
null
)
{
$membershipValue
=
$catalogValue
;
}
}
}
$waiverFee
=
bcdiv
(
bcmul
(
$membershipValue
,
self
::
WAIVER_FEE_PERCENTAGE
,
4
),
'100'
,
2
);
return
[
'membership_value'
=>
$membershipValue
,
'percentage'
=>
self
::
WAIVER_FEE_PERCENTAGE
,
'waiver_fee'
=>
$waiverFee
,
'annual_subscription_required'
=>
true
,
];
}
/**
* Get the list of conditions required for a membership waiver.
*
* @return array<int, string>
*/
public
static
function
getWaiverConditions
()
:
array
{
return
[
'موافقة مجلس الإدارة مطلوبة'
,
'لا يجوز أن يتجاوز عدد المرافقين للمتنازل إليه عدد مرافقي المتنازل'
,
'يحتفظ بنفس رقم العضوية'
,
'يجب سداد الاشتراك السنوي قبل التنازل'
,
'يتم أرشفة بيانات العضو القديم (لا تُحذف)'
,
];
}
/**
* Process a waiver request: validate source member, count dependants,
* and create a transfer_request record.
*
* @param int $sourceMemberId ID of the member transferring their membership
* @param array $newMemberData Data for the new member receiving the membership
*
* @return array{transfer_request_id: int, fee: string, conditions: array<int, string>}
*
* @throws \RuntimeException If source member is not active or not found
*/
public
static
function
processWaiver
(
int
$sourceMemberId
,
array
$newMemberData
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
// Validate source member exists and is active
$sourceMember
=
$db
->
selectOne
(
"SELECT id, status, membership_number, membership_value FROM members WHERE id = ? AND is_archived = 0"
,
[
$sourceMemberId
]
);
if
(
!
$sourceMember
)
{
throw
new
\RuntimeException
(
'العضو المصدر غير موجود'
);
}
if
(
$sourceMember
[
'status'
]
!==
'active'
)
{
throw
new
\RuntimeException
(
'العضو المصدر غير نشط - لا يمكن التنازل'
);
}
// Count dependants (spouses + children) on source
$spouseCount
=
(
int
)
(
$db
->
selectOne
(
"SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0"
,
[
$sourceMemberId
]
)[
'cnt'
]
??
0
);
$childCount
=
(
int
)
(
$db
->
selectOne
(
"SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0"
,
[
$sourceMemberId
]
)[
'cnt'
]
??
0
);
$totalDependants
=
$spouseCount
+
$childCount
;
// Calculate fee
$feeData
=
self
::
calculateWaiverFee
(
$sourceMemberId
);
// Create transfer_request record
$now
=
date
(
'Y-m-d H:i:s'
);
$db
->
insert
(
'transfer_requests'
,
[
'source_member_id'
=>
$sourceMemberId
,
'transfer_type'
=>
'waiver'
,
'source_membership_number'
=>
$sourceMember
[
'membership_number'
],
'new_membership_value'
=>
$sourceMember
[
'membership_value'
]
??
'0.00'
,
'fee_percentage'
=>
self
::
WAIVER_FEE_PERCENTAGE
,
'total_fee'
=>
$feeData
[
'waiver_fee'
],
'status'
=>
'requested'
,
'notes'
=>
json_encode
([
'new_member_data'
=>
$newMemberData
,
'source_dependants'
=>
$totalDependants
,
'spouse_count'
=>
$spouseCount
,
'child_count'
=>
$childCount
,
],
JSON_UNESCAPED_UNICODE
),
'created_at'
=>
$now
,
'updated_at'
=>
$now
,
]);
$transferRequestId
=
(
int
)
$db
->
selectOne
(
"SELECT LAST_INSERT_ID() as id"
)[
'id'
];
Logger
::
info
(
'Waiver request created'
,
[
'transfer_request_id'
=>
$transferRequestId
,
'source_member_id'
=>
$sourceMemberId
,
'fee'
=>
$feeData
[
'waiver_fee'
],
'dependants'
=>
$totalDependants
,
]);
return
[
'transfer_request_id'
=>
$transferRequestId
,
'fee'
=>
$feeData
[
'waiver_fee'
],
'conditions'
=>
self
::
getWaiverConditions
(),
];
}
}
app/Modules/Subscriptions/Services/SubscriptionGenerator.php
View file @
2b105af3
...
@@ -16,14 +16,27 @@ final class SubscriptionGenerator
...
@@ -16,14 +16,27 @@ final class SubscriptionGenerator
$ts
=
date
(
'Y-m-d H:i:s'
);
$ts
=
date
(
'Y-m-d H:i:s'
);
$empId
=
$employee
?
(
int
)
$employee
->
id
:
null
;
$empId
=
$employee
?
(
int
)
$employee
->
id
:
null
;
// Get rates from service catalog
// Get year-specific rates (e.g. 2024/2025 -> suffix 2024)
$memberRate
=
self
::
getRate
(
'SVC_ANNUAL_MEMBER'
);
$yearParts
=
explode
(
'/'
,
$financialYear
);
$spouseRate
=
self
::
getRate
(
'SVC_ANNUAL_SPOUSE'
);
$yearSuffix
=
$yearParts
[
0
]
??
''
;
$childRate
=
self
::
getRate
(
'SVC_ANNUAL_CHILD'
);
$memberRate
=
self
::
getRate
(
'SVC_ANNUAL_MEMBER_'
.
$yearSuffix
)
?:
self
::
getRate
(
'SVC_ANNUAL_MEMBER'
);
$tempRate
=
self
::
getRate
(
'SVC_ANNUAL_TEMP'
);
$spouseRate
=
self
::
getRate
(
'SVC_ANNUAL_SPOUSE_'
.
$yearSuffix
)
?:
self
::
getRate
(
'SVC_ANNUAL_SPOUSE'
);
$childRate
=
self
::
getRate
(
'SVC_ANNUAL_CHILD_'
.
$yearSuffix
)
?:
self
::
getRate
(
'SVC_ANNUAL_CHILD'
);
$tempRate
=
self
::
getRate
(
'SVC_ANNUAL_TEMP_'
.
$yearSuffix
)
?:
self
::
getRate
(
'SVC_ANNUAL_TEMP'
);
$devFeeData
=
RuleEngine
::
get
(
'DEVELOPMENT_FEE'
);
$devFeeData
=
RuleEngine
::
get
(
'DEVELOPMENT_FEE'
);
$devFee
=
$devFeeData
[
'amount'
]
??
'35.00'
;
$devFee
=
$devFeeData
[
'amount'
]
??
'35.00'
;
// Apply year-specific discount/increase from rules
$yearAdjustment
=
RuleEngine
::
get
(
'SUBSCRIPTION_YEAR_ADJUSTMENT_'
.
$yearSuffix
);
if
(
$yearAdjustment
&&
isset
(
$yearAdjustment
[
'discount_percentage'
]))
{
$discPct
=
$yearAdjustment
[
'discount_percentage'
];
$multiplier
=
bcsub
(
'1.00'
,
bcdiv
(
$discPct
,
'100'
,
4
),
4
);
$memberRate
=
bcmul
(
$memberRate
,
$multiplier
,
2
);
$spouseRate
=
bcmul
(
$spouseRate
,
$multiplier
,
2
);
$childRate
=
bcmul
(
$childRate
,
$multiplier
,
2
);
$tempRate
=
bcmul
(
$tempRate
,
$multiplier
,
2
);
}
// Get active members
// Get active members
$memberWhere
=
"m.status = 'active' AND m.is_archived = 0 AND m.membership_type NOT IN ('honorary')"
;
$memberWhere
=
"m.status = 'active' AND m.is_archived = 0 AND m.membership_type NOT IN ('honorary')"
;
$memberParams
=
[];
$memberParams
=
[];
...
...
app/Modules/Transfers/Services/SeparationFeeCalculator.php
View file @
2b105af3
...
@@ -78,6 +78,31 @@ final class SeparationFeeCalculator
...
@@ -78,6 +78,31 @@ final class SeparationFeeCalculator
return
max
(
1
,
$diff
->
y
+
(
$diff
->
m
>
0
||
$diff
->
d
>
0
?
1
:
0
));
// partial year rounds up
return
max
(
1
,
$diff
->
y
+
(
$diff
->
m
>
0
||
$diff
->
d
>
0
?
1
:
0
));
// partial year rounds up
}
}
public
static
function
calculateAcquiredMemberChildFee
(
int
$childAge
,
string
$membershipValue
)
:
array
{
if
(
$childAge
<
12
)
{
$ruleCode
=
'DIVORCE_CHILD_UNDER_12'
;
}
elseif
(
$childAge
<
16
)
{
$ruleCode
=
'DIVORCE_CHILD_12_TO_16'
;
}
elseif
(
$childAge
<
18
)
{
$ruleCode
=
'DIVORCE_CHILD_16_TO_18'
;
}
else
{
$ruleCode
=
'DIVORCE_CHILD_OVER_18'
;
}
$data
=
RuleEngine
::
get
(
$ruleCode
);
$percentage
=
$data
[
'percentage'
]
??
'30.00'
;
$fee
=
bcdiv
(
bcmul
(
$membershipValue
,
$percentage
,
4
),
'100'
,
2
);
return
[
'age'
=>
$childAge
,
'rule_code'
=>
$ruleCode
,
'percentage'
=>
$percentage
,
'fee'
=>
$fee
,
'membership_value'
=>
$membershipValue
,
];
}
public
static
function
getFeePercentageByYear
(
int
$year
)
:
string
public
static
function
getFeePercentageByYear
(
int
$year
)
:
string
{
{
$ruleMap
=
[
$ruleMap
=
[
...
...
app/Modules/Transfers/Services/TransferEligibility.php
0 → 100644
View file @
2b105af3
<?php
declare
(
strict_types
=
1
);
namespace
App\Modules\Transfers\Services
;
use
App\Core\App
;
use
App\Modules\Rules\Services\RuleEngine
;
final
class
TransferEligibility
{
public
static
function
canChildSeparate
(
int
$childId
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$child
=
$db
->
selectOne
(
"SELECT * FROM children WHERE id = ? AND is_archived = 0"
,
[
$childId
]);
if
(
!
$child
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'لم يتم العثور على بيانات الابن/الابنة'
];
}
$age
=
self
::
calculateAge
(
$child
[
'date_of_birth'
]);
$gender
=
$child
[
'gender'
];
if
(
$gender
===
'female'
)
{
return
[
'eligible'
=>
true
,
'reason'
=>
'البنات: يتم الفصل عند الزواج'
,
'condition'
=>
'marriage'
];
}
$maxAge
=
25
;
$ruleData
=
RuleEngine
::
get
(
'MALE_CHILD_FREEZE_AGE'
);
if
(
$ruleData
)
{
$maxAge
=
(
int
)
(
$ruleData
[
'value'
]
??
25
);
}
if
(
$age
>=
$maxAge
)
{
return
[
'eligible'
=>
true
,
'reason'
=>
'بلوغ سن '
.
$maxAge
.
' عام'
,
'condition'
=>
'age'
];
}
return
[
'eligible'
=>
true
,
'reason'
=>
'الأبناء: التخرج من الجامعة ويعمل بأجر أو بلوغ 25 عام إيهما اسبق'
,
'condition'
=>
'graduation_or_age'
,
'requires_verification'
=>
true
,
];
}
public
static
function
canSpouseSeparateDivorce
(
int
$spouseId
,
?
string
$divorceDate
=
null
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$spouse
=
$db
->
selectOne
(
"SELECT * FROM spouses WHERE id = ? AND is_archived = 0"
,
[
$spouseId
]);
if
(
!
$spouse
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'لم يتم العثور على بيانات الزوج/الزوجة'
];
}
$member
=
$db
->
selectOne
(
"SELECT * FROM members WHERE id = ?"
,
[(
int
)
$spouse
[
'member_id'
]]);
if
(
!
$member
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'العضو الأساسي غير موجود'
];
}
$divorceWindowData
=
RuleEngine
::
get
(
'DIVORCE_REQUEST_WINDOW'
);
$maxYears
=
(
int
)
(
$divorceWindowData
[
'max_years'
]
??
1
);
if
(
$divorceDate
)
{
$divorceDateTime
=
new
\DateTime
(
$divorceDate
);
$now
=
new
\DateTime
();
$diff
=
$now
->
diff
(
$divorceDateTime
);
if
(
$diff
->
y
>=
$maxYears
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'مر أكثر من '
.
$maxYears
.
' عام على تاريخ الطلاق'
];
}
}
$minYearsData
=
RuleEngine
::
get
(
'DIVORCE_MIN_MEMBERSHIP_YEARS'
);
$minYears
=
(
int
)
(
$minYearsData
[
'min_years'
]
??
5
);
$waivedIfChildren
=
(
bool
)
(
$minYearsData
[
'waived_if_children'
]
??
true
);
$membershipDate
=
$spouse
[
'join_date'
]
??
$spouse
[
'marriage_date'
]
??
$member
[
'created_at'
];
$membershipYears
=
self
::
calculateAge
(
$membershipDate
);
if
(
$membershipYears
<
$minYears
)
{
if
(
$waivedIfChildren
)
{
$childCount
=
(
int
)
(
$db
->
selectOne
(
"SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0"
,
[(
int
)
$spouse
[
'member_id'
]]
)[
'cnt'
]
??
0
);
if
(
$childCount
===
0
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'لم تمر '
.
$minYears
.
' سنوات على اكتساب العضوية ولا يوجد أبناء'
,
];
}
}
else
{
return
[
'eligible'
=>
false
,
'reason'
=>
'لم تمر '
.
$minYears
.
' سنوات على اكتساب العضوية'
,
];
}
}
return
[
'eligible'
=>
true
,
'reason'
=>
'مستوفي شروط التحويل'
];
}
public
static
function
getDivorceTransferFee
(
int
$spouseId
,
string
$membershipValue
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$spouse
=
$db
->
selectOne
(
"SELECT * FROM spouses WHERE id = ?"
,
[
$spouseId
]);
if
(
!
$spouse
)
{
return
[
'error'
=>
'الزوج/الزوجة غير موجود'
];
}
$classification
=
$spouse
[
'classification'
]
??
'working'
;
if
(
$classification
===
'working'
)
{
return
[
'scenario'
=>
'both_working'
,
'fee_type'
=>
'annual_subscription_only'
,
'percentage'
=>
'0.00'
,
'separation_fee'
=>
'0.00'
,
'description'
=>
'زوج/زوجة أعضاء عاملين — الاشتراك السنوي فقط'
,
];
}
if
(
$classification
===
'initial_form'
||
$spouse
[
'spouse_order'
]
==
1
)
{
$data
=
RuleEngine
::
get
(
'DIVORCE_SAME_FORM_FEE'
);
$pct
=
$data
[
'percentage'
]
??
'10.00'
;
$fee
=
bcdiv
(
bcmul
(
$membershipValue
,
$pct
,
4
),
'100'
,
2
);
return
[
'scenario'
=>
'same_form'
,
'fee_type'
=>
'percentage'
,
'percentage'
=>
$pct
,
'separation_fee'
=>
$fee
,
'treat_as'
=>
'membership_basis'
,
'description'
=>
'تم قبولهم في استمارة العضوية — 10% من قيمة العضوية'
,
];
}
$data
=
RuleEngine
::
get
(
'DIVORCE_JOINED_AFTER_FEE'
);
$pct
=
$data
[
'percentage'
]
??
'50.00'
;
$fee
=
bcdiv
(
bcmul
(
$membershipValue
,
$pct
,
4
),
'100'
,
2
);
return
[
'scenario'
=>
'joined_after'
,
'fee_type'
=>
'percentage'
,
'percentage'
=>
$pct
,
'separation_fee'
=>
$fee
,
'treat_as'
=>
'acquired_member'
,
'description'
=>
'تم الانضمام بعد الحصول على العضوية — 50% من قيمة العضوية'
,
];
}
public
static
function
canWaiver
(
int
$memberId
)
:
array
{
$db
=
App
::
getInstance
()
->
db
();
$member
=
$db
->
selectOne
(
"SELECT * FROM members WHERE id = ? AND is_archived = 0"
,
[
$memberId
]);
if
(
!
$member
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'العضو غير موجود'
];
}
if
(
$member
[
'status'
]
!==
'active'
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'العضوية غير فعالة'
];
}
$unpaidSubs
=
$db
->
selectOne
(
"SELECT COUNT(*) as cnt FROM subscriptions WHERE member_id = ? AND status IN ('pending','overdue')"
,
[
$memberId
]
);
if
((
int
)
(
$unpaidSubs
[
'cnt'
]
??
0
)
>
0
)
{
return
[
'eligible'
=>
false
,
'reason'
=>
'يجب سداد جميع الاشتراكات المتأخرة قبل التنازل'
];
}
return
[
'eligible'
=>
true
,
'reason'
=>
'مستوفي شروط التنازل — يتطلب موافقة مجلس الأمناء'
,
'requires_board_approval'
=>
true
,
];
}
private
static
function
calculateAge
(
string
$dateOfBirth
)
:
int
{
$dob
=
new
\DateTime
(
substr
(
$dateOfBirth
,
0
,
10
));
$now
=
new
\DateTime
();
return
$now
->
diff
(
$dob
)
->
y
;
}
}
database/seeds/Phase_46_001_seed_subscription_rates_and_prices.php
0 → 100644
View file @
2b105af3
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment