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
e93afcbe
Commit
e93afcbe
authored
May 21, 2026
by
Mahmoud Aglan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
test
parent
c0df407f
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
2324 additions
and
0 deletions
+2324
-0
AcademyPricingController.php
...s/SportsActivity/Controllers/AcademyPricingController.php
+127
-0
Routes.php
app/Modules/SportsActivity/Routes.php
+9
-0
AcademyPricingService.php
...Modules/SportsActivity/Services/AcademyPricingService.php
+374
-0
academies.php
app/Modules/SportsActivity/Views/pricing/academies.php
+130
-0
bootstrap.php
app/Modules/SportsActivity/bootstrap.php
+1
-0
Phase_80_001_create_academy_pricing_tables.php
...migrations/Phase_80_001_create_academy_pricing_tables.php
+91
-0
Phase_80_001_seed_all_academy_pricing.php
database/seeds/Phase_80_001_seed_all_academy_pricing.php
+1543
-0
Phase_80_002_seed_additional_disciplines.php
database/seeds/Phase_80_002_seed_additional_disciplines.php
+49
-0
No files found.
app/Modules/SportsActivity/Controllers/AcademyPricingController.php
0 → 100644
View file @
e93afcbe
<?php
declare
(
strict_types
=
1
);
namespace
App\Modules\SportsActivity\Controllers
;
use
App\Core\Controller
;
use
App\Core\Request
;
use
App\Modules\SportsActivity\Services\AcademyPricingService
;
class
AcademyPricingController
extends
Controller
{
public
function
index
(
Request
$request
)
{
$this
->
authorize
(
'sa.pricing.view'
);
$academies
=
AcademyPricingService
::
listAcademies
();
$sessionCards
=
AcademyPricingService
::
getSessionCardPricing
();
return
$this
->
view
(
'SportsActivity.Views.pricing.academies'
,
[
'academies'
=>
$academies
,
'sessionCards'
=>
$sessionCards
,
]);
}
public
function
academies
(
Request
$request
)
{
$this
->
authorize
(
'sa.pricing.view'
);
$academies
=
AcademyPricingService
::
listAcademies
();
return
$this
->
json
([
'academies'
=>
$academies
]);
}
public
function
academyDetail
(
Request
$request
,
string
$code
)
{
$this
->
authorize
(
'sa.pricing.view'
);
$db
=
\App\Core\App
::
getInstance
()
->
db
();
$rows
=
$db
->
select
(
"SELECT * FROM sa_academy_pricing
WHERE academy_code = ? AND is_active = 1
AND effective_from <= CURDATE()
AND (effective_to IS NULL OR effective_to >= CURDATE())
ORDER BY category, sort_order"
,
[
$code
]
);
if
(
empty
(
$rows
))
{
return
$this
->
json
([
'error'
=>
'لا توجد بيانات لهذه الأكاديمية'
],
404
);
}
$grouped
=
[];
foreach
(
$rows
as
$row
)
{
$grouped
[
$row
[
'category'
]][]
=
$row
;
}
return
$this
->
json
([
'academy_code'
=>
$code
,
'academy_name_ar'
=>
$rows
[
0
][
'academy_name_ar'
],
'categories'
=>
$grouped
,
]);
}
public
function
sessionCards
(
Request
$request
)
{
$this
->
authorize
(
'sa.pricing.view'
);
return
$this
->
json
([
'cards'
=>
AcademyPricingService
::
getSessionCardPricing
()]);
}
public
function
laneRentals
(
Request
$request
)
{
$this
->
authorize
(
'sa.pricing.view'
);
$laneType
=
$request
->
get
(
'lane_type'
);
return
$this
->
json
([
'rentals'
=>
AcademyPricingService
::
getLaneRentalPricing
(
$laneType
)]);
}
public
function
facilityRentals
(
Request
$request
)
{
$this
->
authorize
(
'sa.pricing.view'
);
$facilityType
=
$request
->
get
(
'facility_type'
,
''
);
$entityType
=
$request
->
get
(
'entity_type'
,
''
);
$db
=
\App\Core\App
::
getInstance
()
->
db
();
$sql
=
"SELECT * FROM sa_rental_entity_pricing WHERE is_active = 1
AND effective_from <= CURDATE()
AND (effective_to IS NULL OR effective_to >= CURDATE())"
;
$params
=
[];
if
(
$facilityType
)
{
$sql
.=
" AND facility_type = ?"
;
$params
[]
=
$facilityType
;
}
if
(
$entityType
)
{
$sql
.=
" AND entity_type = ?"
;
$params
[]
=
$entityType
;
}
$sql
.=
" ORDER BY facility_type, entity_type, time_period"
;
return
$this
->
json
([
'rentals'
=>
$db
->
select
(
$sql
,
$params
)]);
}
public
function
calculate
(
Request
$request
)
{
$this
->
authorize
(
'sa.pricing.view'
);
$academyCode
=
(
string
)
(
$request
->
post
(
'academy_code'
)
??
''
);
$levelNameAr
=
(
string
)
(
$request
->
post
(
'level_name_ar'
)
??
''
);
$isMember
=
(
bool
)
(
$request
->
post
(
'is_member'
)
??
false
);
$months
=
max
(
1
,
(
int
)
(
$request
->
post
(
'months'
)
??
1
));
$hasSibling
=
(
bool
)
(
$request
->
post
(
'has_sibling'
)
??
false
);
if
(
!
$academyCode
||
!
$levelNameAr
)
{
return
$this
->
json
([
'error'
=>
'يجب تحديد الأكاديمية والمستوى'
],
422
);
}
$result
=
AcademyPricingService
::
getSubscriptionPrice
(
$academyCode
,
$levelNameAr
,
$isMember
,
$months
,
$hasSibling
);
return
$this
->
json
(
$result
);
}
}
app/Modules/SportsActivity/Routes.php
View file @
e93afcbe
...
...
@@ -215,4 +215,13 @@ return [
[
'POST'
,
'/sa/gate/scan'
,
'SportsActivity\Controllers\GateController@scan'
,
[
'auth'
,
'csrf'
],
'sa.gate.scan'
],
[
'GET'
,
'/sa/gate/log'
,
'SportsActivity\Controllers\GateController@log'
,
[
'auth'
],
'sa.gate.view'
],
[
'GET'
,
'/sa/gate/report'
,
'SportsActivity\Controllers\GateController@report'
,
[
'auth'
],
'sa.gate.view'
],
// ─── Academy Pricing ────────────────────────────────────────────────────────
[
'GET'
,
'/sa/academy-pricing'
,
'SportsActivity\Controllers\AcademyPricingController@index'
,
[
'auth'
],
'sa.pricing.view'
],
[
'GET'
,
'/sa/academy-pricing/academies'
,
'SportsActivity\Controllers\AcademyPricingController@academies'
,
[
'auth'
],
'sa.pricing.view'
],
[
'GET'
,
'/sa/academy-pricing/academy/{code}'
,
'SportsActivity\Controllers\AcademyPricingController@academyDetail'
,
[
'auth'
],
'sa.pricing.view'
],
[
'GET'
,
'/sa/academy-pricing/session-cards'
,
'SportsActivity\Controllers\AcademyPricingController@sessionCards'
,
[
'auth'
],
'sa.pricing.view'
],
[
'GET'
,
'/sa/academy-pricing/lane-rentals'
,
'SportsActivity\Controllers\AcademyPricingController@laneRentals'
,
[
'auth'
],
'sa.pricing.view'
],
[
'GET'
,
'/sa/academy-pricing/facility-rentals'
,
'SportsActivity\Controllers\AcademyPricingController@facilityRentals'
,
[
'auth'
],
'sa.pricing.view'
],
[
'POST'
,
'/sa/academy-pricing/calculate'
,
'SportsActivity\Controllers\AcademyPricingController@calculate'
,
[
'auth'
,
'csrf'
],
'sa.pricing.view'
],
];
app/Modules/SportsActivity/Services/AcademyPricingService.php
0 → 100644
View file @
e93afcbe
This diff is collapsed.
Click to expand it.
app/Modules/SportsActivity/Views/pricing/academies.php
0 → 100644
View file @
e93afcbe
<?php
$__template
->
layout
(
'Layout.main'
);
?>
<?php
$__template
->
section
(
'title'
);
?>
تسعير الأكاديميات
<?php
$__template
->
endSection
();
?>
<?php
$__template
->
section
(
'page_actions'
);
?>
<a
href=
"/sa/pricing"
class=
"btn btn-outline"
><i
data-lucide=
"calculator"
style=
"width:15px;height:15px;vertical-align:middle;margin-left:4px;"
></i>
تسعير المرافق
</a>
<?php
$__template
->
endSection
();
?>
<?php
$__template
->
section
(
'content'
);
?>
<div
style=
"display:grid;grid-template-columns:repeat(auto-fit, minmax(140px, 1fr));gap:12px;margin-bottom:20px;"
>
<div
class=
"card"
style=
"padding:14px;text-align:center;"
>
<div
style=
"font-size:11px;color:#6B7280;"
>
أكاديميات
</div>
<div
style=
"font-size:22px;font-weight:800;"
>
<?=
count
(
$academies
)
?>
</div>
</div>
<div
class=
"card"
style=
"padding:14px;text-align:center;"
>
<div
style=
"font-size:11px;color:#6B7280;"
>
كروت الحصص
</div>
<div
style=
"font-size:22px;font-weight:800;"
>
<?=
count
(
$sessionCards
)
?>
</div>
</div>
</div>
<!-- Academies List -->
<div
class=
"card"
style=
"margin-bottom:20px;"
>
<div
style=
"padding:15px 20px;border-bottom:1px solid #E5E7EB;"
>
<h3
style=
"margin:0;font-size:16px;font-weight:600;"
><i
data-lucide=
"building-2"
style=
"width:18px;height:18px;vertical-align:middle;margin-left:6px;"
></i>
قائمة أسعار الأكاديميات — موسم 2025/2026
</h3>
</div>
<table
style=
"width:100%;border-collapse:collapse;font-size:13px;"
>
<thead>
<tr
style=
"background:#F9FAFB;"
>
<th
style=
"padding:10px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
الأكاديمية
</th>
<th
style=
"padding:10px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
التخصص
</th>
<th
style=
"padding:10px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
عدد المستويات
</th>
<th
style=
"padding:10px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
أقل سعر عضو
</th>
<th
style=
"padding:10px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
أعلى سعر عضو
</th>
<th
style=
"padding:10px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
تفاصيل
</th>
</tr>
</thead>
<tbody>
<?php
if
(
empty
(
$academies
))
:
?>
<tr><td
colspan=
"6"
style=
"padding:30px;text-align:center;color:#9CA3AF;"
>
لا توجد بيانات — يرجى تشغيل php cli.php seed
</td></tr>
<?php
else
:
?>
<?php
foreach
(
$academies
as
$a
)
:
?>
<tr
style=
"border-top:1px solid #F3F4F6;"
>
<td
style=
"padding:10px 15px;font-weight:600;"
>
<?=
e
(
$a
[
'academy_name_ar'
])
?>
</td>
<td
style=
"padding:10px 15px;font-size:12px;color:#6B7280;"
>
<?=
e
(
$a
[
'discipline_code'
])
?>
</td>
<td
style=
"padding:10px 15px;"
>
<?=
(
int
)
$a
[
'pricing_count'
]
?>
</td>
<td
style=
"padding:10px 15px;direction:ltr;text-align:right;"
>
<?=
number_format
((
float
)
$a
[
'min_price_member'
],
0
)
?>
ج.م
</td>
<td
style=
"padding:10px 15px;direction:ltr;text-align:right;"
>
<?=
number_format
((
float
)
$a
[
'max_price_member'
],
0
)
?>
ج.م
</td>
<td
style=
"padding:10px 15px;"
>
<button
type=
"button"
class=
"btn btn-outline"
style=
"padding:4px 10px;font-size:11px;"
onclick=
"loadAcademy('
<?=
e
(
$a
[
'academy_code'
])
?>
')"
>
<i
data-lucide=
"eye"
style=
"width:12px;height:12px;vertical-align:middle;"
></i>
</button>
</td>
</tr>
<?php
endforeach
;
?>
<?php
endif
;
?>
</tbody>
</table>
</div>
<!-- Session Cards -->
<?php
if
(
!
empty
(
$sessionCards
))
:
?>
<div
class=
"card"
style=
"margin-bottom:20px;"
>
<div
style=
"padding:15px 20px;border-bottom:1px solid #E5E7EB;"
>
<h3
style=
"margin:0;font-size:15px;font-weight:600;"
><i
data-lucide=
"credit-card"
style=
"width:16px;height:16px;vertical-align:middle;margin-left:6px;"
></i>
كروت حصص السباحة
</h3>
</div>
<table
style=
"width:100%;border-collapse:collapse;font-size:13px;"
>
<thead>
<tr
style=
"background:#F9FAFB;"
>
<th
style=
"padding:8px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
الكارت
</th>
<th
style=
"padding:8px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
عدد الحصص
</th>
<th
style=
"padding:8px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
السعر
</th>
<th
style=
"padding:8px 15px;text-align:right;font-weight:600;color:#6B7280;"
>
سعر الحصة
</th>
</tr>
</thead>
<tbody>
<?php
foreach
(
$sessionCards
as
$card
)
:
?>
<tr
style=
"border-top:1px solid #F3F4F6;"
>
<td
style=
"padding:8px 15px;"
>
<?=
e
(
$card
[
'level_name_ar'
])
?>
</td>
<td
style=
"padding:8px 15px;"
>
<?=
$card
[
'total_sessions'
]
?>
</td>
<td
style=
"padding:8px 15px;direction:ltr;text-align:right;"
>
<?=
number_format
(
$card
[
'price'
],
0
)
?>
ج.م
</td>
<td
style=
"padding:8px 15px;direction:ltr;text-align:right;color:#059669;"
>
<?=
number_format
(
$card
[
'price_per_session'
],
0
)
?>
ج.م
</td>
</tr>
<?php
endforeach
;
?>
</tbody>
</table>
</div>
<?php
endif
;
?>
<!-- Academy Detail Modal -->
<div
id=
"academyDetail"
style=
"display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:1000;display:none;align-items:center;justify-content:center;"
>
<div
style=
"background:white;border-radius:12px;padding:25px;max-width:800px;width:90%;max-height:80vh;overflow-y:auto;"
>
<div
style=
"display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;"
>
<h3
id=
"academyDetailTitle"
style=
"margin:0;font-size:18px;font-weight:700;"
></h3>
<button
type=
"button"
onclick=
"closeDetail()"
style=
"background:none;border:none;cursor:pointer;font-size:20px;"
>
✕
</button>
</div>
<div
id=
"academyDetailBody"
></div>
</div>
</div>
<script>
document
.
addEventListener
(
'DOMContentLoaded'
,
function
()
{
if
(
typeof
lucide
!==
'undefined'
)
lucide
.
createIcons
();
});
function
loadAcademy
(
code
)
{
fetch
(
'/sa/academy-pricing/academy/'
+
code
,
{
headers
:{
'X-Requested-With'
:
'XMLHttpRequest'
}})
.
then
(
function
(
r
){
return
r
.
json
();})
.
then
(
function
(
data
)
{
if
(
data
.
error
)
{
alert
(
data
.
error
);
return
;
}
document
.
getElementById
(
'academyDetailTitle'
).
textContent
=
data
.
academy_name_ar
;
var
html
=
''
;
for
(
var
cat
in
data
.
categories
)
{
var
catLabel
=
{
'subscription'
:
'اشتراكات'
,
'team'
:
'فرق'
,
'private_training'
:
'تدريب خاص'
,
'session_card'
:
'كروت حصص'
,
'lane_rental'
:
'ايجار حارات'
}[
cat
]
||
cat
;
html
+=
'<h4 style="margin:15px 0 8px;font-size:14px;color:#4B5563;border-bottom:1px solid #E5E7EB;padding-bottom:5px;">'
+
catLabel
+
'</h4>'
;
html
+=
'<table style="width:100%;border-collapse:collapse;font-size:12px;margin-bottom:10px;"><thead><tr style="background:#F9FAFB;"><th style="padding:6px 10px;text-align:right;">المستوى</th><th style="padding:6px 10px;text-align:right;">عضو</th><th style="padding:6px 10px;text-align:right;">غير عضو</th><th style="padding:6px 10px;text-align:right;">تدريبات/اسبوع</th></tr></thead><tbody>'
;
data
.
categories
[
cat
].
forEach
(
function
(
row
)
{
html
+=
'<tr style="border-top:1px solid #F3F4F6;"><td style="padding:6px 10px;">'
+
(
row
.
level_name_ar
||
'—'
)
+
'</td><td style="padding:6px 10px;direction:ltr;text-align:right;">'
+
Number
(
row
.
price_member
).
toLocaleString
()
+
'</td><td style="padding:6px 10px;direction:ltr;text-align:right;">'
+
Number
(
row
.
price_nonmember
).
toLocaleString
()
+
'</td><td style="padding:6px 10px;">'
+
(
row
.
sessions_per_week
||
'—'
)
+
'</td></tr>'
;
});
html
+=
'</tbody></table>'
;
}
document
.
getElementById
(
'academyDetailBody'
).
innerHTML
=
html
;
document
.
getElementById
(
'academyDetail'
).
style
.
display
=
'flex'
;
});
}
function
closeDetail
()
{
document
.
getElementById
(
'academyDetail'
).
style
.
display
=
'none'
;
}
</script>
<?php
$__template
->
endSection
();
?>
app/Modules/SportsActivity/bootstrap.php
View file @
e93afcbe
...
...
@@ -32,6 +32,7 @@ MenuRegistry::register('sports_activity', [
[
'label_ar'
=>
'الجدول'
,
'label_en'
=>
'Schedule'
,
'route'
=>
'/sa/schedule'
,
'permission'
=>
'sa.schedule.view'
,
'order'
=>
14
],
[
'label_ar'
=>
'الحجوزات'
,
'label_en'
=>
'Bookings'
,
'route'
=>
'/sa/bookings'
,
'permission'
=>
'sa.booking.view'
,
'order'
=>
15
],
[
'label_ar'
=>
'التسعير'
,
'label_en'
=>
'Pricing'
,
'route'
=>
'/sa/pricing'
,
'permission'
=>
'sa.pricing.view'
,
'order'
=>
16
],
[
'label_ar'
=>
'أسعار الأكاديميات'
,
'label_en'
=>
'Academy Pricing'
,
'route'
=>
'/sa/academy-pricing'
,
'permission'
=>
'sa.pricing.view'
,
'order'
=>
16.5
],
[
'label_ar'
=>
'الاشتراكات'
,
'label_en'
=>
'Subscriptions'
,
'route'
=>
'/sa/subscriptions'
,
'permission'
=>
'sa.subscription.view'
,
'order'
=>
17
],
[
'label_ar'
=>
'الحضور'
,
'label_en'
=>
'Attendance'
,
'route'
=>
'/sa/attendance'
,
'permission'
=>
'sa.attendance.view'
,
'order'
=>
18
],
[
'label_ar'
=>
'قائمة الانتظار'
,
'label_en'
=>
'Waitlist'
,
'route'
=>
'/sa/waitlist'
,
'permission'
=>
'sa.waitlist.view'
,
'order'
=>
19
],
...
...
database/migrations/Phase_80_001_create_academy_pricing_tables.php
0 → 100644
View file @
e93afcbe
<?php
declare
(
strict_types
=
1
);
return
[
'up'
=>
"
CREATE TABLE `sa_academy_pricing` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`academy_code` VARCHAR(50) NOT NULL,
`academy_name_ar` VARCHAR(300) NOT NULL,
`discipline_code` VARCHAR(30) NOT NULL,
`category` VARCHAR(50) NOT NULL COMMENT 'subscription, team, session_card, private_training, lane_rental',
`level_name_ar` VARCHAR(200) NULL COMMENT 'Group name/level/age group',
`level_name_en` VARCHAR(200) NULL,
`age_group` VARCHAR(50) NULL COMMENT 'under_13, adult, under_6, under_7, etc.',
`birth_year` INT NULL COMMENT 'Specific birth year for age-based pricing',
`age_from` INT UNSIGNED NULL,
`age_to` INT UNSIGNED NULL,
`gender` VARCHAR(10) NULL COMMENT 'male, female, NULL=mixed',
`group_size_max` INT UNSIGNED NULL COMMENT 'Max swimmers in group (for swimming)',
`sessions_per_week` INT UNSIGNED NULL,
`sessions_per_month` INT UNSIGNED NULL,
`session_duration_minutes` INT UNSIGNED NULL,
`price_member` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`price_nonmember` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
`price_subscription` DECIMAL(10,2) NULL COMMENT 'Base subscription price before member/nonmember',
`billing_period` VARCHAR(20) NOT NULL DEFAULT 'monthly' COMMENT 'monthly, quarterly, per_session, per_card, yearly',
`includes_fitness` TINYINT(1) NOT NULL DEFAULT 0,
`num_players_max` INT UNSIGNED NULL COMMENT 'For private training - max players per session',
`coach_level` VARCHAR(50) NULL COMMENT 'For private training - director/coach/fitness',
`lane_type` VARCHAR(30) NULL COMMENT '50m, 25m, mix',
`total_sessions` INT UNSIGNED NULL COMMENT 'For session cards - total sessions in card',
`season` VARCHAR(20) NOT NULL DEFAULT '2025_2026',
`notes_ar` TEXT NULL,
`sort_order` INT UNSIGNED NOT NULL DEFAULT 0,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`effective_from` DATE NOT NULL DEFAULT '2025-09-01',
`effective_to` DATE NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_sap_academy` (`academy_code`),
INDEX `idx_sap_discipline` (`discipline_code`),
INDEX `idx_sap_category` (`category`),
INDEX `idx_sap_age` (`age_from`, `age_to`),
INDEX `idx_sap_active` (`is_active`, `effective_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `sa_rental_entity_pricing` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`facility_type` VARCHAR(50) NOT NULL COMMENT 'football_full, football_five, tennis, padel, squash, multi_court, bowling, table_tennis, playstation, billiards, track, combat_hall',
`facility_name_ar` VARCHAR(200) NOT NULL,
`entity_type` VARCHAR(50) NOT NULL COMMENT 'member, non_member, non_member_private_coach, schools_gov, clubs_federations, other_entities, foreign_entities',
`usage_type` VARCHAR(50) NOT NULL DEFAULT 'practice' COMMENT 'practice, match, activity',
`time_period` VARCHAR(20) NOT NULL COMMENT 'morning, evening',
`duration_minutes` INT UNSIGNED NOT NULL DEFAULT 60,
`price_amount` DECIMAL(10,2) NOT NULL,
`max_players` INT UNSIGNED NULL,
`notes_ar` VARCHAR(500) NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`effective_from` DATE NOT NULL DEFAULT '2025-09-01',
`effective_to` DATE NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_srep_facility` (`facility_type`),
INDEX `idx_srep_entity` (`entity_type`),
INDEX `idx_srep_usage` (`usage_type`),
INDEX `idx_srep_active` (`is_active`, `effective_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `sa_discount_rules` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`rule_code` VARCHAR(50) NOT NULL UNIQUE,
`name_ar` VARCHAR(200) NOT NULL,
`discount_type` VARCHAR(20) NOT NULL COMMENT 'percentage, fixed',
`discount_value` DECIMAL(10,2) NOT NULL,
`applies_to` VARCHAR(50) NOT NULL COMMENT 'academy, facility, all',
`academy_code` VARCHAR(50) NULL COMMENT 'NULL = all academies',
`condition_type` VARCHAR(50) NOT NULL COMMENT 'advance_payment, sibling, nonmember_surcharge, free_period',
`condition_value` VARCHAR(200) NULL COMMENT 'JSON or simple value for condition params',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_sdr_applies` (`applies_to`, `academy_code`),
INDEX `idx_sdr_condition` (`condition_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
"
,
'down'
=>
"
DROP TABLE IF EXISTS `sa_discount_rules`;
DROP TABLE IF EXISTS `sa_rental_entity_pricing`;
DROP TABLE IF EXISTS `sa_academy_pricing`;
"
];
database/seeds/Phase_80_001_seed_all_academy_pricing.php
0 → 100644
View file @
e93afcbe
This diff is collapsed.
Click to expand it.
database/seeds/Phase_80_002_seed_additional_disciplines.php
0 → 100644
View file @
e93afcbe
<?php
declare
(
strict_types
=
1
);
use
App\Core\Database
;
/**
* إضافة التخصصات الرياضية الجديدة من قائمة الأسعار 2025/2026
* هذه التخصصات مطلوبة لربط الأسعار بالأكاديميات
*/
return
function
(
Database
$db
)
:
void
{
$ts
=
date
(
'Y-m-d H:i:s'
);
$disciplines
=
[
[
'code'
=>
'BASKETBALL'
,
'name_ar'
=>
'كرة السلة'
,
'name_en'
=>
'Basketball'
,
'category'
=>
'team'
,
'icon'
=>
'circle-dot'
,
'desc'
=>
'كرة السلة — أكاديمي وفرق'
,
'sort'
=>
11
],
[
'code'
=>
'VOLLEYBALL'
,
'name_ar'
=>
'الكرة الطائرة'
,
'name_en'
=>
'Volleyball'
,
'category'
=>
'team'
,
'icon'
=>
'circle'
,
'desc'
=>
'الكرة الطائرة — أكاديمي وفرق'
,
'sort'
=>
12
],
[
'code'
=>
'HANDBALL'
,
'name_ar'
=>
'كرة اليد'
,
'name_en'
=>
'Handball'
,
'category'
=>
'team'
,
'icon'
=>
'hand'
,
'desc'
=>
'كرة اليد — أكاديمي وفرق'
,
'sort'
=>
13
],
[
'code'
=>
'CHESS'
,
'name_ar'
=>
'الشطرنج'
,
'name_en'
=>
'Chess'
,
'category'
=>
'individual'
,
'icon'
=>
'crown'
,
'desc'
=>
'الشطرنج — عدلى أكاديمى'
,
'sort'
=>
14
],
[
'code'
=>
'MUSIC'
,
'name_ar'
=>
'الموسيقى'
,
'name_en'
=>
'Music'
,
'category'
=>
'individual'
,
'icon'
=>
'music'
,
'desc'
=>
'أكاديمية الآلات الموسيقية'
,
'sort'
=>
15
],
[
'code'
=>
'SKATING'
,
'name_ar'
=>
'الباتيناج'
,
'name_en'
=>
'Skating'
,
'category'
=>
'individual'
,
'icon'
=>
'zap'
,
'desc'
=>
'الباتيناج والتزحلق'
,
'sort'
=>
16
],
[
'code'
=>
'DRAWING'
,
'name_ar'
=>
'الرسم'
,
'name_en'
=>
'Drawing'
,
'category'
=>
'individual'
,
'icon'
=>
'pen-tool'
,
'desc'
=>
'أكاديمية الرسم والفنون'
,
'sort'
=>
17
],
[
'code'
=>
'KICKBOXING'
,
'name_ar'
=>
'الكيك بوكس'
,
'name_en'
=>
'Kickboxing'
,
'category'
=>
'combat'
,
'icon'
=>
'shield-alert'
,
'desc'
=>
'الكيك بوكس — فنون قتالية'
,
'sort'
=>
18
],
];
foreach
(
$disciplines
as
$d
)
{
$existing
=
$db
->
selectOne
(
"SELECT id FROM sa_disciplines WHERE code = ?"
,
[
$d
[
'code'
]]);
if
(
!
$existing
)
{
$db
->
insert
(
'sa_disciplines'
,
[
'code'
=>
$d
[
'code'
],
'name_ar'
=>
$d
[
'name_ar'
],
'name_en'
=>
$d
[
'name_en'
],
'category'
=>
$d
[
'category'
],
'icon'
=>
$d
[
'icon'
],
'description_ar'
=>
$d
[
'desc'
],
'config_json'
=>
json_encode
([
'skill_levels'
=>
[
[
'code'
=>
'beginner'
,
'label_ar'
=>
'مبتدئ'
],
[
'code'
=>
'intermediate'
,
'label_ar'
=>
'متوسط'
],
[
'code'
=>
'advanced'
,
'label_ar'
=>
'متقدم'
],
],
],
JSON_UNESCAPED_UNICODE
),
'sort_order'
=>
$d
[
'sort'
],
'is_active'
=>
1
,
'is_archived'
=>
0
,
'created_at'
=>
$ts
,
'updated_at'
=>
$ts
,
]);
}
}
};
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