Commit 4461e7e6 authored by Mahmoud Aglan's avatar Mahmoud Aglan

xdsgjdfdhf

parent 6e46794f
......@@ -11,6 +11,7 @@ use App\Core\EventBus;
use App\Modules\Children\Models\Child;
use App\Modules\Children\Services\ChildFeeCalculator;
use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Rules\Services\RuleEngine;
class ChildController extends Controller
......@@ -83,31 +84,32 @@ class ChildController extends Controller
$childAge = (int) ($data['age_years'] ?? 0);
// Children 16+ require NID
if ($childAge >= 16 && $nid === '') {
$errors[] = 'الأبناء فوق 16 سنة يجب أن يكون لديهم رقم قومي';
$nidRequiredAge = (int) (RuleEngine::getValue('CHILD_NID_REQUIRED_AGE', 'value') ?? 16);
if ($childAge >= $nidRequiredAge && $nid === '') {
$errors[] = 'الأبناء فوق ' . $nidRequiredAge . ' سنة يجب أن يكون لديهم رقم قومي';
}
// Age gap validation
$minAgeGap = (int) (RuleEngine::getValue('CHILD_MIN_AGE_GAP', 'value') ?? 15);
$maxAgeGap = (int) (RuleEngine::getValue('CHILD_MAX_AGE_GAP', 'value') ?? 60);
if (!empty($data['date_of_birth']) && !empty($member['date_of_birth'])) {
$memberAge = age_from_dob($member['date_of_birth']);
$ageGap = $memberAge['years'] - $childAge;
if ($ageGap < 15) {
$errors[] = 'الحد الأدنى لفرق السن بين العضو والابن 15 سنة';
if ($ageGap < $minAgeGap) {
$errors[] = 'الحد الأدنى لفرق السن بين العضو والابن ' . $minAgeGap . ' سنة';
}
if ($ageGap > 60) {
$errors[] = 'الحد الأقصى لفرق السن بين العضو والابن 60 سنة';
if ($ageGap > $maxAgeGap) {
$errors[] = 'الحد الأقصى لفرق السن بين العضو والابن ' . $maxAgeGap . ' سنة';
}
}
// Stepchild age check
if (($data['relationship'] ?? '') === 'stepchild' && $childAge >= 25) {
$errors[] = 'أبناء الزوج/الزوجة لا يمكن أن يتجاوز عمرهم 25 سنة';
$stepchildMaxAge = (int) (RuleEngine::getValue('STEPCHILD_MAX_AGE', 'value') ?? 25);
$childMaxAge = (int) (RuleEngine::getValue('CHILD_MAX_AGE', 'value') ?? RuleEngine::getValue('CHILD_AUTO_DELETE_AGE', 'value') ?? 25);
if (($data['relationship'] ?? '') === 'stepchild' && $childAge >= $stepchildMaxAge) {
$errors[] = 'أبناء الزوج/الزوجة لا يمكن أن يتجاوز عمرهم ' . $stepchildMaxAge . ' سنة';
}
// Check age 25+ not accepted
if ($childAge >= 25 && ($data['relationship'] ?? '') !== 'stepchild') {
$errors[] = 'لا يمكن إضافة أبناء فوق 25 سنة';
if ($childAge >= $childMaxAge && ($data['relationship'] ?? '') !== 'stepchild') {
$errors[] = 'لا يمكن إضافة أبناء فوق ' . $childMaxAge . ' سنة';
}
if (!empty($errors)) {
......
......@@ -38,8 +38,8 @@ final class PricingEngine
public static function calculateChildFee(string $membershipValue, int $childAge, int $childOrder): array
{
$maxIncluded = RuleEngine::getValue('CHILD_INCLUDED_MAX_COUNT', 'value') ?? 3;
$maxIncludedAge = RuleEngine::getValue('CHILD_INCLUDED_MAX_AGE', 'value') ?? 18;
$maxIncluded = (int) (RuleEngine::getValue('INITIAL_FREE_CHILDREN_COUNT', 'value') ?? RuleEngine::getValue('CHILD_INCLUDED_MAX_COUNT', 'value') ?? 2);
$maxIncludedAge = (int) (RuleEngine::getValue('CHILD_INCLUDED_MAX_AGE', 'value') ?? 18);
if ($childAge < $maxIncludedAge && $childOrder <= $maxIncluded) {
return ['fee' => '0.00', 'rule_applied' => 'included', 'percentage' => '0.00', 'classification' => 'included'];
......
......@@ -11,6 +11,7 @@ use App\Core\EventBus;
use App\Modules\Spouses\Models\Spouse;
use App\Modules\Spouses\Services\SpouseFeeCalculator;
use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Rules\Services\RuleEngine;
class SpouseController extends Controller
......@@ -122,9 +123,9 @@ class SpouseController extends Controller
$data['age_months'] = $age['months'];
}
// ── Minimum age check (18+) ──
if ((int) ($data['age_years'] ?? 0) < 18) {
$errors[] = 'الحد الأدنى للسن 18 سنة';
$spouseMinAge = (int) (RuleEngine::getValue('SPOUSE_MIN_AGE', 'value') ?? 18);
if ((int) ($data['age_years'] ?? 0) < $spouseMinAge) {
$errors[] = 'الحد الأدنى للسن ' . $spouseMinAge . ' سنة';
}
if (!empty($errors)) {
......
......@@ -5,6 +5,7 @@ namespace App\Modules\Spouses\Models;
use App\Core\Model;
use App\Core\App;
use App\Modules\Rules\Services\RuleEngine;
class Spouse extends Model
{
......@@ -109,9 +110,10 @@ class Spouse extends Model
*/
public static function getMaxSpouses(string $memberGender): int
{
// Female member: max 1 husband
// Male member: max 4 wives (as per regulations)
return $memberGender === 'female' ? 1 : 4;
if ($memberGender === 'female') {
return (int) (RuleEngine::getValue('MAX_SPOUSES_FEMALE_MEMBER', 'value') ?? 1);
}
return (int) (RuleEngine::getValue('MAX_SPOUSES_MALE_MEMBER', 'value') ?? 4);
}
/**
......
<?php
declare(strict_types=1);
/**
* Phase 40: Master Document Compliance
*
* Ensures all business_rules values match the master document.
* Adds missing rules that were previously hardcoded.
* After this seed, all variable values are editable via /rules admin.
*/
return function (\App\Core\Database $db) {
$now = date('Y-m-d H:i:s');
$today = date('Y-m-d');
// ═══════════════════════════════════════════════════════════════
// PART 1: Fix existing rules to match master document
// ═══════════════════════════════════════════════════════════════
$corrections = [
// Master document: "First 3 children under 18 years: INCLUDED"
'CHILD_INCLUDED_MAX_COUNT' => ['current_value_json' => '{"value":3}'],
'INITIAL_FREE_CHILDREN_COUNT' => ['current_value_json' => '{"value":3}'],
];
foreach ($corrections as $code => $update) {
$existing = $db->selectOne(
"SELECT id, current_value_json, version FROM business_rules WHERE rule_code = ? AND branch_id IS NULL AND is_active = 1",
[$code]
);
if (!$existing) {
continue;
}
$currentVal = $existing['current_value_json'];
$newVal = $update['current_value_json'];
if ($currentVal === $newVal) {
continue;
}
$newVersion = (int) $existing['version'] + 1;
$db->insert('rule_versions', [
'rule_id' => (int) $existing['id'],
'version_number' => $newVersion,
'old_value_json' => $currentVal,
'new_value_json' => $newVal,
'changed_by' => null,
'changed_at' => $now,
'change_reason' => 'تصحيح طبقاً للائحة الرئيسية',
]);
$db->update('business_rules', [
'current_value_json' => $newVal,
'version' => $newVersion,
'updated_at' => $now,
], '`id` = ?', [(int) $existing['id']]);
}
// ═══════════════════════════════════════════════════════════════
// PART 2: Add missing rules (previously hardcoded)
// ═══════════════════════════════════════════════════════════════
$newRules = [
// ── Spouse limits ──
[
'rule_code' => 'MAX_SPOUSES_MALE_MEMBER',
'category' => 'spouse_fee',
'name_ar' => 'الحد الأقصى لعدد الزوجات (عضو ذكر)',
'name_en' => 'Max Spouses for Male Member',
'data_type' => 'integer',
'current_value_json' => '{"value":4}',
'parameters_json' => '{"value":"integer"}',
],
[
'rule_code' => 'MAX_SPOUSES_FEMALE_MEMBER',
'category' => 'spouse_fee',
'name_ar' => 'الحد الأقصى لعدد الأزواج (عضوة أنثى)',
'name_en' => 'Max Spouses for Female Member',
'data_type' => 'integer',
'current_value_json' => '{"value":1}',
'parameters_json' => '{"value":"integer"}',
],
[
'rule_code' => 'SPOUSE_MIN_AGE',
'category' => 'spouse_fee',
'name_ar' => 'الحد الأدنى لسن الزوج/الزوجة',
'name_en' => 'Minimum Spouse Age',
'data_type' => 'integer',
'current_value_json' => '{"value":18}',
'parameters_json' => '{"value":"integer"}',
],
// ── Child validation limits ──
[
'rule_code' => 'CHILD_NID_REQUIRED_AGE',
'category' => 'age',
'name_ar' => 'سن إلزام الرقم القومي للأبناء',
'name_en' => 'Child NID Required Age',
'data_type' => 'integer',
'current_value_json' => '{"value":16}',
'parameters_json' => '{"value":"integer"}',
],
[
'rule_code' => 'CHILD_MIN_AGE_GAP',
'category' => 'age',
'name_ar' => 'الحد الأدنى لفرق السن بين العضو والابن',
'name_en' => 'Min Parent-Child Age Gap',
'data_type' => 'integer',
'current_value_json' => '{"value":15}',
'parameters_json' => '{"value":"integer"}',
],
[
'rule_code' => 'CHILD_MAX_AGE_GAP',
'category' => 'age',
'name_ar' => 'الحد الأقصى لفرق السن بين العضو والابن',
'name_en' => 'Max Parent-Child Age Gap',
'data_type' => 'integer',
'current_value_json' => '{"value":60}',
'parameters_json' => '{"value":"integer"}',
],
[
'rule_code' => 'CHILD_MAX_AGE',
'category' => 'age',
'name_ar' => 'الحد الأقصى لسن الابن (غير مؤقت)',
'name_en' => 'Max Child Age (non-temp)',
'data_type' => 'integer',
'current_value_json' => '{"value":25}',
'parameters_json' => '{"value":"integer"}',
],
// ── Death/Transfer rules ──
[
'rule_code' => 'DEATH_TRANSFER_WINDOW_MONTHS',
'category' => 'death',
'name_ar' => 'مهلة نقل العضوية بعد الوفاة (بالأشهر)',
'name_en' => 'Death Transfer Window Months',
'data_type' => 'integer',
'current_value_json' => '{"value":12}',
'parameters_json' => '{"value":"integer"}',
],
// ── Annual subscription due date ──
[
'rule_code' => 'ANNUAL_SUB_DUE_MONTH',
'category' => 'workflow',
'name_ar' => 'شهر استحقاق الاشتراك السنوي',
'name_en' => 'Annual Subscription Due Month',
'data_type' => 'integer',
'current_value_json' => '{"value":7}',
'parameters_json' => '{"value":"integer"}',
],
// ── Carnet replacement fee ──
[
'rule_code' => 'CARNET_REPLACEMENT_FEE',
'category' => 'financial',
'name_ar' => 'رسوم بدل فاقد الكارنيه',
'name_en' => 'Carnet Replacement Fee',
'data_type' => 'amount',
'current_value_json' => '{"amount":"200.00"}',
'parameters_json' => '{"amount":"decimal"}',
],
// ── Freeze (تجميد) rules ──
[
'rule_code' => 'FREEZE_MAX_YEARS',
'category' => 'workflow',
'name_ar' => 'أقصى مدة تجميد العضوية (سنوات)',
'name_en' => 'Max Freeze Duration Years',
'data_type' => 'integer',
'current_value_json' => '{"value":3}',
'parameters_json' => '{"value":"integer"}',
],
[
'rule_code' => 'FREEZE_ANNUAL_FEE_PERCENTAGE',
'category' => 'workflow',
'name_ar' => 'نسبة رسوم التجميد السنوية من الاشتراك',
'name_en' => 'Freeze Annual Fee Percentage',
'data_type' => 'percentage',
'current_value_json' => '{"percentage":"50.00"}',
'parameters_json' => '{"percentage":"decimal"}',
],
// ── Seasonal membership limits ──
[
'rule_code' => 'SEASONAL_FEE_PERCENTAGE',
'category' => 'temporary',
'name_ar' => 'نسبة رسوم العضوية الموسمية',
'name_en' => 'Seasonal Fee Percentage',
'data_type' => 'percentage',
'current_value_json' => '{"percentage":"5.00"}',
'parameters_json' => '{"percentage":"decimal"}',
],
// ── Board approval thresholds ──
[
'rule_code' => 'BOARD_APPROVAL_THRESHOLD',
'category' => 'workflow',
'name_ar' => 'حد المبلغ الذي يتطلب موافقة مجلس الإدارة',
'name_en' => 'Board Approval Amount Threshold',
'data_type' => 'amount',
'current_value_json' => '{"amount":"50000.00"}',
'parameters_json' => '{"amount":"decimal"}',
],
];
foreach ($newRules as $rule) {
$exists = $db->selectOne(
"SELECT id FROM business_rules WHERE rule_code = ? AND branch_id IS NULL",
[$rule['rule_code']]
);
if ($exists) {
continue;
}
$db->insert('business_rules', array_merge($rule, [
'branch_id' => null,
'effective_from' => $today,
'effective_to' => null,
'version' => 1,
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
]));
}
};
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