Commit 9e075ef2 authored by Mahmoud Aglan's avatar Mahmoud Aglan

CREATED USERMATRIX

parent 6141de46
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
Subproject commit 280686c4f5a46880d5a32e240b58389d11a56a78
......@@ -295,6 +295,42 @@ if (!function_exists('number_to_arabic_words')) {
}
}
if (!function_exists('branch_scope')) {
function branch_scope(string $alias, string $column = 'branch_id'): string
{
return \App\Shared\Services\BranchScopeService::scopeQuery($alias, $column);
}
}
if (!function_exists('branch_params')) {
function branch_params(): array
{
return \App\Shared\Services\BranchScopeService::scopeParams();
}
}
if (!function_exists('can')) {
function can(string $permission): bool
{
$employee = \App\Core\App::getInstance()->currentEmployee();
if (!$employee) {
return false;
}
return $employee->hasPermission($permission);
}
}
if (!function_exists('canAny')) {
function canAny(array $permissions): bool
{
$employee = \App\Core\App::getInstance()->currentEmployee();
if (!$employee) {
return false;
}
return $employee->hasAnyPermission($permissions);
}
}
if (!function_exists('flash_success')) {
function flash_success(string $message): void
{
......
......@@ -192,6 +192,20 @@ final class QueryBuilder
return $clone;
}
public function branchScope(string $column = 'branch_id'): self
{
if (\App\Shared\Services\BranchScopeService::hasAllBranches()) {
return $this;
}
$branchIds = \App\Shared\Services\BranchScopeService::getAccessibleBranchIds();
if (empty($branchIds)) {
return $this->whereRaw('1=0');
}
return $this->whereIn($column, $branchIds);
}
public function when(bool $condition, callable $fn): self
{
if ($condition) {
......
......@@ -8,6 +8,7 @@ use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Modules\Users\Models\Employee;
use App\Shared\Services\BranchScopeService;
final class AuthMiddleware implements MiddlewareInterface
{
......@@ -36,6 +37,7 @@ final class AuthMiddleware implements MiddlewareInterface
$session->set('last_activity_at', time());
$app->setCurrentEmployee($employee);
BranchScopeService::reset();
if ($employee->branch_id) {
$branch = $app->db()->selectOne("SELECT * FROM branches WHERE id = ?", [$employee->branch_id]);
......
......@@ -6,7 +6,9 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?>الأكاديميات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('sa.academy.manage')): ?>
<a href="/academies/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة أكاديمية</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -129,9 +131,11 @@ $allTypes = Academy::getAcademyTypes();
<a href="/academies/<?= (int) $a['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
<?php if (can('sa.academy.manage')): ?>
<a href="/academies/<?= (int) $a['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit-3" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
<?php endif; ?>
</div>
</div>
</div>
......@@ -157,7 +161,9 @@ $allTypes = Academy::getAcademyTypes();
ابدأ بإضافة أكاديمية جديدة للنادي.
<?php endif; ?>
</p>
<?php if (can('sa.academy.manage')): ?>
<a href="/academies/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة أكاديمية</a>
<?php endif; ?>
</div>
<?php endif; ?>
......
......@@ -6,7 +6,9 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?><?= e($academy->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('sa.academy.manage')): ?>
<a href="/academies/<?= (int) $academy->id ?>/edit" class="btn btn-outline"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<?php endif; ?>
<a href="/academies" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
......
......@@ -6,7 +6,9 @@ $__template->layout('Layout.main');
<?php $__template->section('page_actions'); ?>
<a href="/academy-settlements" class="btn btn-outline"><i data-lucide="calculator" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> التسويات</a>
<?php if (can('academy_contract.manage')): ?>
<a href="/academy-contracts/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> عقد جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -6,7 +6,7 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?>عقد <?= e($contract->contract_number) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (in_array($contract->status, ['draft', 'pending_approval'])): ?>
<?php if (in_array($contract->status, ['draft', 'pending_approval']) && can('academy_contract.manage')): ?>
<a href="/academy-contracts/<?= (int) $contract->id ?>/edit" class="btn btn-outline"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<?php endif; ?>
<a href="/academy-contracts" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
......@@ -51,7 +51,7 @@ $typeLabel = AcademyContract::getContractTypeLabel($contract->contract_type ??
</div>
</div>
<div style="flex-shrink:0;display:flex;gap:8px;">
<?php if ($contract->status === 'draft' || $contract->status === 'pending_approval'): ?>
<?php if (($contract->status === 'draft' || $contract->status === 'pending_approval') && can('academy_contract.manage')): ?>
<form method="POST" action="/academy-contracts/<?= (int) $contract->id ?>/activate" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-primary" onclick="return confirm('هل أنت متأكد من تفعيل هذا العقد؟')">
......@@ -59,7 +59,7 @@ $typeLabel = AcademyContract::getContractTypeLabel($contract->contract_type ??
</button>
</form>
<?php endif; ?>
<?php if ($contract->status === 'active'): ?>
<?php if ($contract->status === 'active' && can('academy_contract.manage')): ?>
<button type="button" class="btn btn-outline" style="color:#DC2626;border-color:#DC2626;" onclick="document.getElementById('terminateModal').style.display='flex'">
<i data-lucide="x-circle" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> إنهاء العقد
</button>
......
<?php
declare(strict_types=1);
namespace App\Modules\AccessMatrix\Controllers;
use App\Core\App;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Modules\AccessMatrix\Services\MatrixService;
use App\Modules\AccessMatrix\Services\PermissionDiscoveryService;
final class AccessMatrixController extends Controller
{
public function index(Request $request): Response
{
$matrixData = MatrixService::buildMatrix();
return $this->view('AccessMatrix.Views.index', [
'roles' => $matrixData['roles'],
'matrix' => $matrixData['matrix'],
]);
}
public function compare(Request $request): Response
{
$db = App::getInstance()->db();
$roles = $db->select("SELECT id, role_code, name_ar FROM roles WHERE is_active = 1 ORDER BY name_ar");
$roleA = (int) ($request->get('role_a') ?? 0);
$roleB = (int) ($request->get('role_b') ?? 0);
$comparison = null;
$roleAData = null;
$roleBData = null;
if ($roleA && $roleB) {
$comparison = MatrixService::compareRoles($roleA, $roleB);
$roleAData = $db->selectOne("SELECT id, role_code, name_ar FROM roles WHERE id = ?", [$roleA]);
$roleBData = $db->selectOne("SELECT id, role_code, name_ar FROM roles WHERE id = ?", [$roleB]);
}
return $this->view('AccessMatrix.Views.compare', [
'roles' => $roles,
'roleA' => $roleA,
'roleB' => $roleB,
'roleAData' => $roleAData,
'roleBData' => $roleBData,
'comparison' => $comparison,
]);
}
public function toggle(Request $request): Response
{
$this->authorize('access_matrix.manage');
$roleId = (int) ($request->post('role_id') ?? 0);
$permissionKey = $request->post('permission_key') ?? '';
if (!$roleId || !$permissionKey) {
return $this->json(['error' => 'بيانات غير صالحة'], 400);
}
$db = App::getInstance()->db();
$role = $db->selectOne("SELECT id, is_system FROM roles WHERE id = ?", [$roleId]);
if (!$role) {
return $this->json(['error' => 'الدور غير موجود'], 404);
}
$existing = $db->selectOne(
"SELECT id FROM role_permissions WHERE role_id = ? AND permission_key = ?",
[$roleId, $permissionKey]
);
if ($existing) {
$db->delete('role_permissions', '`id` = ?', [(int) $existing['id']]);
$granted = false;
} else {
$db->insert('role_permissions', [
'role_id' => $roleId,
'permission_key' => $permissionKey,
'granted_at' => date('Y-m-d H:i:s'),
'granted_by' => (int) App::getInstance()->currentEmployee()->id,
]);
$granted = true;
}
return $this->json(['granted' => $granted, 'role_id' => $roleId, 'permission' => $permissionKey]);
}
public function export(Request $request): Response
{
$matrixData = MatrixService::buildMatrix();
$csv = MatrixService::exportCsv($matrixData);
$filename = 'access_matrix_' . date('Y-m-d_His') . '.csv';
return (new Response())
->header('Content-Type', 'text/csv; charset=utf-8')
->header('Content-Disposition', 'attachment; filename="' . $filename . '"')
->html($csv);
}
public function clone(Request $request): Response
{
$this->authorize('access_matrix.manage');
$sourceId = (int) ($request->post('source_role_id') ?? 0);
$newCode = trim($request->post('new_code') ?? '');
$nameAr = trim($request->post('name_ar') ?? '');
if (!$sourceId || !$newCode || !$nameAr) {
return $this->redirect('/access-matrix')->withError('يرجى ملء جميع الحقول المطلوبة');
}
$db = App::getInstance()->db();
$existingCode = $db->selectOne("SELECT 1 FROM roles WHERE role_code = ?", [$newCode]);
if ($existingCode) {
return $this->redirect('/access-matrix')->withError('كود الدور موجود مسبقاً');
}
try {
MatrixService::cloneRole($sourceId, $newCode, $nameAr);
return $this->redirect('/access-matrix')->withSuccess('تم نسخ الدور بنجاح');
} catch (\Throwable $e) {
return $this->redirect('/access-matrix')->withError('خطأ: ' . $e->getMessage());
}
}
public function health(Request $request): Response
{
$orphans = PermissionDiscoveryService::findOrphanPermissions();
$unprotected = PermissionDiscoveryService::findUnprotectedRoutes();
$stats = PermissionDiscoveryService::getPermissionStats();
$db = App::getInstance()->db();
$roles = $db->select("SELECT id, role_code, name_ar FROM roles WHERE is_active = 1 ORDER BY name_ar");
$violations = [];
foreach ($roles as $role) {
$v = PermissionDiscoveryService::validateRoleDependencies((int) $role['id']);
if (!empty($v)) {
$violations[$role['name_ar']] = $v;
}
}
return $this->view('AccessMatrix.Views.health', [
'orphans' => $orphans,
'unprotected' => $unprotected,
'stats' => $stats,
'violations' => $violations,
]);
}
}
<?php
declare(strict_types=1);
return [
['GET', '/access-matrix', 'AccessMatrix\Controllers\AccessMatrixController@index', ['auth'], 'access_matrix.view'],
['GET', '/access-matrix/compare', 'AccessMatrix\Controllers\AccessMatrixController@compare', ['auth'], 'access_matrix.view'],
['POST', '/access-matrix/toggle', 'AccessMatrix\Controllers\AccessMatrixController@toggle', ['auth', 'csrf'], 'access_matrix.manage'],
['GET', '/access-matrix/export', 'AccessMatrix\Controllers\AccessMatrixController@export', ['auth'], 'access_matrix.view'],
['POST', '/access-matrix/clone', 'AccessMatrix\Controllers\AccessMatrixController@clone', ['auth', 'csrf'], 'access_matrix.manage'],
['GET', '/access-matrix/health', 'AccessMatrix\Controllers\AccessMatrixController@health', ['auth'], 'access_matrix.view'],
];
<?php
declare(strict_types=1);
namespace App\Modules\AccessMatrix\Services;
use App\Core\App;
use App\Core\Registries\PermissionRegistry;
final class MatrixService
{
public static function buildMatrix(?array $roleIds = null): array
{
$db = App::getInstance()->db();
$roleQuery = "SELECT id, role_code, name_ar, name_en, category, is_system, has_all_branches FROM roles WHERE is_active = 1";
$roleParams = [];
if ($roleIds) {
$placeholders = implode(',', array_fill(0, count($roleIds), '?'));
$roleQuery .= " AND id IN ({$placeholders})";
$roleParams = $roleIds;
}
$roleQuery .= " ORDER BY `level` ASC, name_ar ASC";
$roles = $db->select($roleQuery, $roleParams);
$allPermissions = PermissionRegistry::getAllGrouped();
$rolePermMap = [];
foreach ($roles as $role) {
$perms = $db->select(
"SELECT permission_key FROM role_permissions WHERE role_id = ?",
[(int) $role['id']]
);
$rolePermMap[(int) $role['id']] = array_column($perms, 'permission_key');
}
$matrix = [];
foreach ($allPermissions as $group => $permissions) {
$matrix[$group] = [
'group' => $group,
'permissions' => [],
];
foreach ($permissions as $key => $meta) {
$row = [
'key' => $key,
'label' => $meta['ar'] ?? $key,
'roles' => [],
];
foreach ($roles as $role) {
$roleId = (int) $role['id'];
$hasWildcard = in_array('*', $rolePermMap[$roleId], true);
$hasPermission = $hasWildcard || in_array($key, $rolePermMap[$roleId], true);
$row['roles'][$roleId] = $hasPermission;
}
$matrix[$group]['permissions'][] = $row;
}
}
return [
'roles' => $roles,
'matrix' => $matrix,
];
}
public static function compareRoles(int $roleA, int $roleB): array
{
$db = App::getInstance()->db();
$permsA = array_column(
$db->select("SELECT permission_key FROM role_permissions WHERE role_id = ?", [$roleA]),
'permission_key'
);
$permsB = array_column(
$db->select("SELECT permission_key FROM role_permissions WHERE role_id = ?", [$roleB]),
'permission_key'
);
$onlyA = array_diff($permsA, $permsB);
$onlyB = array_diff($permsB, $permsA);
$shared = array_intersect($permsA, $permsB);
return [
'only_a' => array_values($onlyA),
'only_b' => array_values($onlyB),
'shared' => array_values($shared),
];
}
public static function cloneRole(int $sourceRoleId, string $newCode, string $nameAr, string $nameEn = ''): int
{
$db = App::getInstance()->db();
$source = $db->selectOne("SELECT * FROM roles WHERE id = ?", [$sourceRoleId]);
if (!$source) {
throw new \RuntimeException('الدور المصدر غير موجود', 404);
}
$db->insert('roles', [
'role_code' => $newCode,
'name_ar' => $nameAr,
'name_en' => $nameEn ?: $source['name_en'],
'description_ar' => $source['description_ar'],
'description_en' => $source['description_en'] ?? null,
'is_system' => 0,
'parent_role_id' => $source['parent_role_id'],
'has_all_branches' => (int) $source['has_all_branches'],
'category' => $source['category'],
'level' => (int) $source['level'],
'is_template' => 0,
'is_active' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
$newRole = $db->selectOne("SELECT id FROM roles WHERE role_code = ?", [$newCode]);
$newRoleId = (int) $newRole['id'];
$sourcePerms = $db->select("SELECT permission_key FROM role_permissions WHERE role_id = ?", [$sourceRoleId]);
foreach ($sourcePerms as $perm) {
$db->insert('role_permissions', [
'role_id' => $newRoleId,
'permission_key' => $perm['permission_key'],
'granted_at' => date('Y-m-d H:i:s'),
]);
}
return $newRoleId;
}
public static function exportCsv(array $matrixData): string
{
$roles = $matrixData['roles'];
$matrix = $matrixData['matrix'];
$lines = [];
$header = ['المجموعة', 'الصلاحية'];
foreach ($roles as $role) {
$header[] = $role['name_ar'] . ' (' . $role['role_code'] . ')';
}
$lines[] = implode(',', array_map(fn($v) => '"' . str_replace('"', '""', $v) . '"', $header));
foreach ($matrix as $group => $groupData) {
foreach ($groupData['permissions'] as $perm) {
$row = [$group, $perm['label']];
foreach ($roles as $role) {
$row[] = ($perm['roles'][(int) $role['id']] ?? false) ? '✓' : '';
}
$lines[] = implode(',', array_map(fn($v) => '"' . str_replace('"', '""', (string) $v) . '"', $row));
}
}
return "\xEF\xBB\xBF" . implode("\n", $lines);
}
}
<?php
declare(strict_types=1);
namespace App\Modules\AccessMatrix\Services;
use App\Core\App;
use App\Core\Registries\PermissionRegistry;
final class PermissionDiscoveryService
{
public static function findOrphanPermissions(): array
{
$registered = array_keys(PermissionRegistry::getAll());
$usedInRoutes = self::getRoutePermissions();
return array_values(array_diff($registered, $usedInRoutes));
}
public static function findUnprotectedRoutes(): array
{
$unprotected = [];
$modulesPath = App::getInstance()->basePath() . '/app/Modules';
$dirs = glob($modulesPath . '/*/Routes.php');
foreach ($dirs as $routeFile) {
$routes = require $routeFile;
if (!is_array($routes)) {
continue;
}
foreach ($routes as $route) {
$permission = $route[4] ?? null;
if (empty($permission)) {
$unprotected[] = [
'method' => $route[0] ?? '?',
'path' => $route[1] ?? '?',
'handler' => $route[2] ?? '?',
'file' => basename(dirname($routeFile)),
];
}
}
}
return $unprotected;
}
public static function getPermissionStats(): array
{
$grouped = PermissionRegistry::getAllGrouped();
$stats = [];
$total = 0;
foreach ($grouped as $group => $perms) {
$count = count($perms);
$stats[$group] = $count;
$total += $count;
}
arsort($stats);
return [
'total' => $total,
'groups' => count($grouped),
'by_group' => $stats,
];
}
public static function getDependencies(): array
{
$db = App::getInstance()->db();
try {
$rows = $db->select("SELECT permission_key, requires_key, is_auto_grant FROM permission_dependencies ORDER BY permission_key");
} catch (\Throwable $e) {
return [];
}
$deps = [];
foreach ($rows as $row) {
$deps[$row['permission_key']][] = [
'requires' => $row['requires_key'],
'auto_grant' => (bool) $row['is_auto_grant'],
];
}
return $deps;
}
public static function validateRoleDependencies(int $roleId): array
{
$db = App::getInstance()->db();
$violations = [];
$rolePerms = array_column(
$db->select("SELECT permission_key FROM role_permissions WHERE role_id = ?", [$roleId]),
'permission_key'
);
if (in_array('*', $rolePerms, true)) {
return [];
}
$deps = self::getDependencies();
foreach ($rolePerms as $perm) {
if (!isset($deps[$perm])) {
continue;
}
foreach ($deps[$perm] as $dep) {
if (!in_array($dep['requires'], $rolePerms, true)) {
$violations[] = [
'permission' => $perm,
'missing' => $dep['requires'],
];
}
}
}
return $violations;
}
private static function getRoutePermissions(): array
{
$permissions = [];
$modulesPath = App::getInstance()->basePath() . '/app/Modules';
$dirs = glob($modulesPath . '/*/Routes.php');
foreach ($dirs as $routeFile) {
$routes = require $routeFile;
if (!is_array($routes)) {
continue;
}
foreach ($routes as $route) {
$permission = $route[4] ?? null;
if ($permission) {
$permissions[] = $permission;
}
}
}
return array_unique($permissions);
}
}
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>مقارنة الأدوار<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/access-matrix" class="btn btn-outline">← المصفوفة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;padding:20px;">
<form method="GET" action="/access-matrix/compare" style="display:flex;gap:15px;align-items:end;flex-wrap:wrap;">
<div>
<label class="form-label">الدور الأول</label>
<select name="role_a" class="form-select" style="min-width:200px;" required>
<option value="">اختر...</option>
<?php foreach ($roles as $r): ?>
<option value="<?= (int) $r['id'] ?>" <?= $roleA == $r['id'] ? 'selected' : '' ?>><?= e($r['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="font-size:24px;color:#6B7280;padding-bottom:5px;"></div>
<div>
<label class="form-label">الدور الثاني</label>
<select name="role_b" class="form-select" style="min-width:200px;" required>
<option value="">اختر...</option>
<?php foreach ($roles as $r): ?>
<option value="<?= (int) $r['id'] ?>" <?= $roleB == $r['id'] ? 'selected' : '' ?>><?= e($r['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">مقارنة</button>
</form>
</div>
<?php if ($comparison): ?>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;">
<!-- Only in Role A -->
<div class="card">
<div style="padding:12px 15px;background:#FEF2F2;border-bottom:2px solid #FECACA;">
<h4 style="margin:0;color:#DC2626;font-size:14px;">
فقط في: <?= e($roleAData['name_ar']) ?>
<span style="float:left;background:#DC2626;color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;"><?= count($comparison['only_a']) ?></span>
</h4>
</div>
<div style="padding:10px 15px;max-height:400px;overflow-y:auto;">
<?php if (empty($comparison['only_a'])): ?>
<p style="color:#9CA3AF;text-align:center;">لا يوجد</p>
<?php else: ?>
<?php foreach ($comparison['only_a'] as $p): ?>
<div style="padding:4px 0;font-size:12px;border-bottom:1px solid #F9FAFB;direction:ltr;"><?= e($p) ?></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Shared -->
<div class="card">
<div style="padding:12px 15px;background:#F0FDF4;border-bottom:2px solid #A7F3D0;">
<h4 style="margin:0;color:#059669;font-size:14px;">
مشتركة
<span style="float:left;background:#059669;color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;"><?= count($comparison['shared']) ?></span>
</h4>
</div>
<div style="padding:10px 15px;max-height:400px;overflow-y:auto;">
<?php if (empty($comparison['shared'])): ?>
<p style="color:#9CA3AF;text-align:center;">لا يوجد</p>
<?php else: ?>
<?php foreach ($comparison['shared'] as $p): ?>
<div style="padding:4px 0;font-size:12px;border-bottom:1px solid #F9FAFB;direction:ltr;"><?= e($p) ?></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Only in Role B -->
<div class="card">
<div style="padding:12px 15px;background:#EFF6FF;border-bottom:2px solid #BFDBFE;">
<h4 style="margin:0;color:#0284C7;font-size:14px;">
فقط في: <?= e($roleBData['name_ar']) ?>
<span style="float:left;background:#0284C7;color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;"><?= count($comparison['only_b']) ?></span>
</h4>
</div>
<div style="padding:10px 15px;max-height:400px;overflow-y:auto;">
<?php if (empty($comparison['only_b'])): ?>
<p style="color:#9CA3AF;text-align:center;">لا يوجد</p>
<?php else: ?>
<?php foreach ($comparison['only_b'] as $p): ?>
<div style="padding:4px 0;font-size:12px;border-bottom:1px solid #F9FAFB;direction:ltr;"><?= e($p) ?></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
<?php else: ?>
<div class="card" style="padding:40px;text-align:center;color:#9CA3AF;">
<div style="font-size:48px;margin-bottom:15px;">🔍</div>
<p>اختر دورين للمقارنة بينهما</p>
</div>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>صحة الصلاحيات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/access-matrix" class="btn btn-outline">← المصفوفة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<!-- Stats Overview -->
<div style="display:grid;grid-template-columns:repeat(4, 1fr);gap:15px;margin-bottom:20px;">
<div class="card" style="padding:20px;text-align:center;">
<div style="font-size:28px;font-weight:700;color:#0D7377;"><?= (int) $stats['total'] ?></div>
<div style="color:#6B7280;font-size:12px;">إجمالي الصلاحيات</div>
</div>
<div class="card" style="padding:20px;text-align:center;">
<div style="font-size:28px;font-weight:700;color:#059669;"><?= (int) $stats['groups'] ?></div>
<div style="color:#6B7280;font-size:12px;">مجموعات</div>
</div>
<div class="card" style="padding:20px;text-align:center;">
<div style="font-size:28px;font-weight:700;color:#D97706;"><?= count($orphans) ?></div>
<div style="color:#6B7280;font-size:12px;">صلاحيات يتيمة</div>
</div>
<div class="card" style="padding:20px;text-align:center;">
<div style="font-size:28px;font-weight:700;color:#DC2626;"><?= count($unprotected) ?></div>
<div style="color:#6B7280;font-size:12px;">مسارات بدون حماية</div>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;">
<!-- Orphan Permissions -->
<div class="card">
<div style="padding:12px 15px;background:#FFFBEB;border-bottom:2px solid #FDE68A;">
<h4 style="margin:0;color:#D97706;font-size:14px;">⚠️ صلاحيات مسجلة بدون استخدام في المسارات (<?= count($orphans) ?>)</h4>
<p style="margin:5px 0 0;font-size:11px;color:#6B7280;">مسجلة في bootstrap.php لكن لا تُستخدم في أي Route</p>
</div>
<div style="padding:10px 15px;max-height:300px;overflow-y:auto;">
<?php if (empty($orphans)): ?>
<p style="color:#059669;text-align:center;padding:20px;">✅ لا يوجد صلاحيات يتيمة</p>
<?php else: ?>
<?php foreach ($orphans as $o): ?>
<div style="padding:4px 0;font-size:12px;border-bottom:1px solid #F9FAFB;direction:ltr;color:#374151;"><?= e($o) ?></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Unprotected Routes -->
<div class="card">
<div style="padding:12px 15px;background:#FEF2F2;border-bottom:2px solid #FECACA;">
<h4 style="margin:0;color:#DC2626;font-size:14px;">🚨 مسارات بدون صلاحية محددة (<?= count($unprotected) ?>)</h4>
<p style="margin:5px 0 0;font-size:11px;color:#6B7280;">مسارات في Routes.php بدون عنصر permission (العنصر الخامس)</p>
</div>
<div style="padding:10px 15px;max-height:300px;overflow-y:auto;">
<?php if (empty($unprotected)): ?>
<p style="color:#059669;text-align:center;padding:20px;">✅ جميع المسارات محمية</p>
<?php else: ?>
<table style="width:100%;font-size:11px;">
<thead><tr><th style="text-align:right;">الوحدة</th><th style="text-align:right;">المسار</th><th>الطريقة</th></tr></thead>
<tbody>
<?php foreach ($unprotected as $u): ?>
<tr style="border-bottom:1px solid #F9FAFB;">
<td style="padding:3px 0;"><?= e($u['file']) ?></td>
<td style="padding:3px 0;direction:ltr;font-size:10px;"><?= e($u['path']) ?></td>
<td style="padding:3px 0;"><span style="background:#E5E7EB;padding:1px 5px;border-radius:4px;font-size:10px;"><?= e($u['method']) ?></span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
</div>
<!-- Permission Distribution -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:12px 15px;border-bottom:1px solid #E5E7EB;">
<h4 style="margin:0;color:#0D7377;font-size:14px;">📊 توزيع الصلاحيات حسب المجموعة</h4>
</div>
<div style="padding:15px;display:grid;grid-template-columns:repeat(auto-fill, minmax(250px, 1fr));gap:10px;">
<?php foreach ($stats['by_group'] as $group => $count): ?>
<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 12px;background:#F9FAFB;border-radius:8px;">
<span style="font-size:13px;font-weight:500;"><?= e($group) ?></span>
<span style="background:#0D7377;color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;"><?= (int) $count ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Dependency Violations -->
<?php if (!empty($violations)): ?>
<div class="card">
<div style="padding:12px 15px;background:#FEF2F2;border-bottom:2px solid #FECACA;">
<h4 style="margin:0;color:#DC2626;font-size:14px;">🔗 انتهاكات التبعية (<?= array_sum(array_map('count', $violations)) ?>)</h4>
<p style="margin:5px 0 0;font-size:11px;color:#6B7280;">أدوار تملك صلاحيات بدون الصلاحيات المطلوبة لها</p>
</div>
<div style="padding:15px;">
<?php foreach ($violations as $roleName => $roleViolations): ?>
<div style="margin-bottom:15px;">
<div style="font-weight:600;color:#374151;margin-bottom:5px;"><?= e($roleName) ?></div>
<?php foreach ($roleViolations as $v): ?>
<div style="font-size:12px;padding:3px 12px;color:#6B7280;">
<span style="color:#DC2626;direction:ltr;display:inline-block;"><?= e($v['permission']) ?></span>
يتطلب
<span style="color:#059669;direction:ltr;display:inline-block;"><?= e($v['missing']) ?></span>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>مصفوفة الصلاحيات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<a href="/access-matrix/export" class="btn btn-outline">📥 تصدير CSV</a>
<a href="/access-matrix/compare" class="btn btn-outline">🔍 مقارنة أدوار</a>
<a href="/access-matrix/health" class="btn btn-outline">🏥 صحة الصلاحيات</a>
<?php if (can('access_matrix.manage')): ?>
<button onclick="document.getElementById('clone-modal').style.display='flex'" class="btn btn-primary">+ نسخ دور</button>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div style="margin-bottom:15px;display:flex;gap:10px;align-items:center;">
<input type="text" id="matrix-search" placeholder="بحث في الصلاحيات..." class="form-input" style="max-width:300px;" oninput="filterMatrix(this.value)">
<span style="color:#6B7280;font-size:13px;">
<?= count($roles) ?> دور |
<?php $totalPerms = 0; foreach ($matrix as $g) $totalPerms += count($g['permissions']); ?>
<?= $totalPerms ?> صلاحية
</span>
<div style="margin-right:auto;display:flex;gap:8px;font-size:12px;">
<span style="display:inline-flex;align-items:center;gap:4px;"><span style="width:14px;height:14px;background:#059669;border-radius:3px;display:inline-block;"></span> ممنوحة</span>
<span style="display:inline-flex;align-items:center;gap:4px;"><span style="width:14px;height:14px;background:#E5E7EB;border-radius:3px;display:inline-block;"></span> غير ممنوحة</span>
</div>
</div>
<div class="card" style="overflow:auto;max-height:75vh;">
<table id="matrix-table" style="width:100%;border-collapse:collapse;font-size:12px;">
<thead style="position:sticky;top:0;z-index:10;">
<tr style="background:#1A1A2E;">
<th style="position:sticky;right:0;z-index:11;background:#1A1A2E;color:#fff;padding:10px 12px;text-align:right;min-width:250px;border-left:2px solid #374151;">الصلاحية</th>
<?php foreach ($roles as $role): ?>
<th style="color:#fff;padding:8px 6px;text-align:center;min-width:90px;max-width:120px;white-space:normal;font-size:11px;border-left:1px solid #374151;">
<div style="font-weight:700;"><?= e($role['name_ar']) ?></div>
<div style="opacity:0.6;font-size:10px;direction:ltr;"><?= e($role['role_code']) ?></div>
<?php if ($role['has_all_branches']): ?>
<span style="background:#F59E0B;color:#000;font-size:9px;padding:1px 4px;border-radius:8px;">كل الفروع</span>
<?php endif; ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($matrix as $group => $groupData): ?>
<tr class="matrix-group-header" data-group="<?= e($group) ?>" onclick="toggleGroup('<?= e($group) ?>')" style="cursor:pointer;background:#F3F4F6;">
<td colspan="<?= count($roles) + 1 ?>" style="padding:10px 12px;font-weight:700;color:#0D7377;font-size:13px;border-top:2px solid #D1D5DB;">
<span class="group-arrow" id="arrow-<?= e($group) ?>"></span>
<?= e($group) ?>
<span style="color:#9CA3AF;font-weight:400;font-size:11px;">(<?= count($groupData['permissions']) ?>)</span>
</td>
</tr>
<?php foreach ($groupData['permissions'] as $perm): ?>
<tr class="matrix-row" data-group="<?= e($group) ?>" data-key="<?= e($perm['key']) ?>">
<td style="position:sticky;right:0;background:#fff;padding:6px 12px;border-left:2px solid #E5E7EB;border-bottom:1px solid #F3F4F6;z-index:1;">
<div style="font-weight:500;"><?= e($perm['label']) ?></div>
<div style="font-size:10px;color:#9CA3AF;direction:ltr;"><?= e($perm['key']) ?></div>
</td>
<?php foreach ($roles as $role): ?>
<?php $hasIt = $perm['roles'][(int) $role['id']] ?? false; ?>
<td style="text-align:center;padding:4px;border-bottom:1px solid #F9FAFB;border-left:1px solid #F3F4F6;">
<?php if (can('access_matrix.manage') && $role['role_code'] !== 'super_admin'): ?>
<button
class="matrix-cell"
data-role="<?= (int) $role['id'] ?>"
data-perm="<?= e($perm['key']) ?>"
onclick="togglePermission(this)"
style="width:28px;height:28px;border:none;border-radius:4px;cursor:pointer;background:<?= $hasIt ? '#059669' : '#E5E7EB' ?>;color:<?= $hasIt ? '#fff' : '#9CA3AF' ?>;font-size:14px;font-weight:700;"
><?= $hasIt ? '✓' : '' ?></button>
<?php else: ?>
<span style="display:inline-block;width:28px;height:28px;border-radius:4px;line-height:28px;background:<?= $hasIt ? '#059669' : '#E5E7EB' ?>;color:<?= $hasIt ? '#fff' : '#9CA3AF' ?>;font-size:14px;font-weight:700;"><?= $hasIt ? '✓' : '' ?></span>
<?php endif; ?>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if (can('access_matrix.manage')): ?>
<!-- Clone Modal -->
<div id="clone-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:1000;align-items:center;justify-content:center;">
<div style="background:#fff;border-radius:12px;padding:30px;max-width:450px;width:90%;">
<h3 style="margin:0 0 20px;color:#0D7377;">نسخ دور موجود</h3>
<form method="POST" action="/access-matrix/clone">
<?= csrf_field() ?>
<div class="form-group" style="margin-bottom:15px;">
<label class="form-label">الدور المصدر</label>
<select name="source_role_id" class="form-select" required>
<option value="">اختر...</option>
<?php foreach ($roles as $r): ?>
<option value="<?= (int) $r['id'] ?>"><?= e($r['name_ar']) ?> (<?= e($r['role_code']) ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin-bottom:15px;">
<label class="form-label">كود الدور الجديد</label>
<input type="text" name="new_code" class="form-input" placeholder="مثال: custom_cashier" required style="direction:ltr;">
</div>
<div class="form-group" style="margin-bottom:20px;">
<label class="form-label">اسم الدور بالعربي</label>
<input type="text" name="name_ar" class="form-input" placeholder="مثال: أمين صندوق مخصص" required>
</div>
<div style="display:flex;gap:10px;justify-content:end;">
<button type="button" onclick="document.getElementById('clone-modal').style.display='none'" class="btn btn-outline">إلغاء</button>
<button type="submit" class="btn btn-primary">نسخ</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
<script>
function toggleGroup(group) {
var rows = document.querySelectorAll('.matrix-row[data-group="' + group + '"]');
var arrow = document.getElementById('arrow-' + group);
var hidden = rows[0] && rows[0].style.display === 'none';
rows.forEach(function(r) { r.style.display = hidden ? '' : 'none'; });
arrow.textContent = hidden ? '▼' : '►';
}
function filterMatrix(q) {
q = q.toLowerCase();
var rows = document.querySelectorAll('.matrix-row');
var headers = document.querySelectorAll('.matrix-group-header');
var groupVisible = {};
rows.forEach(function(r) {
var key = r.dataset.key || '';
var visible = !q || key.toLowerCase().indexOf(q) !== -1 || r.querySelector('td').textContent.toLowerCase().indexOf(q) !== -1;
r.style.display = visible ? '' : 'none';
if (visible) groupVisible[r.dataset.group] = true;
});
headers.forEach(function(h) {
h.style.display = groupVisible[h.dataset.group] ? '' : 'none';
});
}
function togglePermission(btn) {
var roleId = btn.dataset.role;
var perm = btn.dataset.perm;
fetch('/access-matrix/toggle', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded', 'X-Requested-With': 'XMLHttpRequest'},
body: '_token=' + encodeURIComponent(document.querySelector('meta[name=csrf-token]')?.content || '<?= csrf_token() ?>') + '&role_id=' + roleId + '&permission_key=' + encodeURIComponent(perm)
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.granted) {
btn.style.background = '#059669';
btn.style.color = '#fff';
btn.textContent = '✓';
} else {
btn.style.background = '#E5E7EB';
btn.style.color = '#9CA3AF';
btn.textContent = '';
}
})
.catch(function(e) { alert('خطأ في الاتصال'); });
}
</script>
<?php $__template->endSection(); ?>
<?php
declare(strict_types=1);
use App\Core\Registries\PermissionRegistry;
use App\Core\Registries\MenuRegistry;
PermissionRegistry::register('access_matrix', [
'access_matrix.view' => ['ar' => 'عرض مصفوفة الصلاحيات', 'en' => 'View Access Matrix'],
'access_matrix.manage' => ['ar' => 'إدارة مصفوفة الصلاحيات', 'en' => 'Manage Access Matrix'],
]);
MenuRegistry::register('access_matrix', [
'label_ar' => 'مصفوفة الصلاحيات',
'label_en' => 'Access Matrix',
'icon' => 'grid-3x3',
'route' => '/access-matrix',
'permission' => 'access_matrix.view',
'order' => 101,
'children' => [
['label_ar' => 'المصفوفة الكاملة', 'route' => '/access-matrix', 'permission' => 'access_matrix.view', 'order' => 1],
['label_ar' => 'مقارنة أدوار', 'route' => '/access-matrix/compare', 'permission' => 'access_matrix.view', 'order' => 2],
['label_ar' => 'صحة الصلاحيات', 'route' => '/access-matrix/health', 'permission' => 'access_matrix.view', 'order' => 3],
],
]);
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>الحسابات البنكية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.bank_account.manage')): ?>
<a href="/accounting/bank-accounts/create" class="btn btn-primary">+ حساب بنكي جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -40,7 +42,7 @@
<td><?= e($acc['currency']) ?></td>
<td style="direction:ltr;text-align:right;font-weight:600;"><?= money($acc['current_balance']) ?></td>
<td><span style="color:<?= (int)$acc['is_active'] ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= (int)$acc['is_active'] ? 'نشط' : 'موقف' ?></span></td>
<td><a href="/accounting/bank-accounts/<?= (int)$acc['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a></td>
<td><?php if (can('accounting.bank_account.manage')): ?><a href="/accounting/bank-accounts/<?= (int)$acc['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a><?php endif; ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($accounts)): ?>
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>المطابقات البنكية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.bank_recon.manage')): ?>
<a href="/accounting/bank-reconciliation/create" class="btn btn-primary">+ مطابقة جديدة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -40,7 +40,7 @@ $statusLabel = match($recon['status']) {
</div>
</div>
<?php if ($recon['status'] !== 'completed' && $recon['status'] !== 'approved'): ?>
<?php if ($recon['status'] !== 'completed' && $recon['status'] !== 'approved' && can('accounting.bank_recon.manage')): ?>
<div style="margin-top:15px;display:flex;gap:8px;">
<?php if (bccomp((string)$recon['difference'], '0.00', 2) === 0): ?>
<form method="POST" action="/accounting/bank-reconciliation/<?= (int)$recon['id'] ?>/complete" style="display:inline;">
......@@ -97,7 +97,7 @@ $statusLabel = match($recon['status']) {
</div>
<!-- Add Item Form -->
<?php if ($recon['status'] !== 'completed' && $recon['status'] !== 'approved'): ?>
<?php if ($recon['status'] !== 'completed' && $recon['status'] !== 'approved' && can('accounting.bank_recon.manage')): ?>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إضافة بند</h3>
......
......@@ -25,6 +25,7 @@
</div>
<?php if ($selectedYearId): ?>
<?php if (can('accounting.budget.manage')): ?>
<div class="card" style="margin-bottom:15px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إدخال موازنة لمركز تكلفة</h3>
......@@ -74,6 +75,7 @@
</form>
</div>
</div>
<?php endif; ?>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
......
......@@ -27,6 +27,7 @@
<?php if ($selectedYearId): ?>
<!-- Budget Entry Form -->
<?php if (can('accounting.budget.manage')): ?>
<div class="card" style="margin-bottom:15px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إدخال موازنة لحساب</h3>
......@@ -76,6 +77,7 @@
</form>
</div>
</div>
<?php endif; ?>
<!-- Existing Budgets Summary -->
<div class="card">
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>دليل الحسابات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.coa.manage')): ?>
<a href="/accounting/chart-of-accounts/create" class="btn btn-primary">+ حساب جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -65,7 +67,7 @@
<span style="flex:1;margin:0 15px;font-weight:<?= $weight ?>;"><?= e($acc['name_ar']) ?></span>
<span style="width:80px;font-size:12px;color:#6B7280;"><?= $typeLabel ?></span>
<span style="width:100px;direction:ltr;text-align:left;font-size:13px;"><?= !$isHeader ? money($acc['current_balance'] ?? '0.00') : '' ?></span>
<a href="/accounting/chart-of-accounts/<?= (int)$acc['id'] ?>/edit" style="font-size:12px;color:#0D7377;">تعديل</a>
<?php if (can('accounting.coa.manage')): ?><a href="/accounting/chart-of-accounts/<?= (int)$acc['id'] ?>/edit" style="font-size:12px;color:#0D7377;">تعديل</a><?php endif; ?>
</div>
<?php
if (!empty($acc['children'])) {
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>مراكز التكلفة<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.cost_center.manage')): ?>
<a href="/accounting/cost-centers/create" class="btn btn-primary">+ مركز تكلفة جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -50,7 +52,7 @@
<td><?= $cc['type'] === 'cost_center' ? 'مركز تكلفة' : 'مركز ربحية' ?></td>
<td><?= e($branchMap[(int)($cc['branch_id'] ?? 0)] ?? '—') ?></td>
<td><span style="color:<?= (int)$cc['is_active'] ? '#059669' : '#DC2626' ?>;"><?= (int)$cc['is_active'] ? 'نشط' : 'موقف' ?></span></td>
<td><a href="/accounting/cost-centers/<?= (int)$cc['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a></td>
<td><?php if (can('accounting.cost_center.manage')): ?><a href="/accounting/cost-centers/<?= (int)$cc['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a><?php endif; ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($centers)): ?>
......
......@@ -20,6 +20,7 @@
</div>
<!-- Quick Entry Form -->
<?php if (can('accounting.daily_tx.manage')): ?>
<div class="card" style="margin-bottom:15px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إدخال سريع</h3>
......@@ -82,6 +83,7 @@
</form>
</div>
</div>
<?php endif; ?>
<!-- Transactions List -->
<div class="card" style="margin-bottom:15px;">
......@@ -90,7 +92,7 @@
<?php
$unconsolidated = array_filter($transactions, fn($t) => !(int)$t['is_consolidated']);
?>
<?php if (!empty($unconsolidated)): ?>
<?php if (!empty($unconsolidated) && can('accounting.daily_tx.manage')): ?>
<form method="POST" action="/accounting/daily-transactions/consolidate" style="display:inline;">
<?= csrf_field() ?>
<input type="hidden" name="date" value="<?= e($date) ?>">
......
......@@ -73,7 +73,7 @@
<div class="card" style="padding:20px;">
<h3 style="margin:0 0 15px;">إجراءات سريعة</h3>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<a href="/accounting/journal-entries/create" class="btn btn-primary">إنشاء قيد يومية</a>
<?php if (can('accounting.journal.create')): ?><a href="/accounting/journal-entries/create" class="btn btn-primary">إنشاء قيد يومية</a><?php endif; ?>
<a href="/accounting/reports/trial-balance" class="btn btn-outline">ميزان المراجعة</a>
<a href="/accounting/reports/income-statement" class="btn btn-outline">قائمة الدخل</a>
<a href="/accounting/reports/balance-sheet" class="btn btn-outline">الميزانية العمومية</a>
......
......@@ -5,9 +5,10 @@
<?php $__template->section('content'); ?>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;">
<h2 style="margin:0;">الأبعاد المحاسبية</h2>
<button type="button" id="btn-new-dim" class="btn btn-primary">+ بُعد جديد</button>
<?php if (can('accounting.coa.manage')): ?><button type="button" id="btn-new-dim" class="btn btn-primary">+ بُعد جديد</button><?php endif; ?>
</div>
<?php if (can('accounting.coa.manage')): ?>
<div class="card" id="create-form" style="margin-bottom:15px;display:none;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إنشاء بُعد محاسبي</h3>
......@@ -41,6 +42,7 @@
</form>
</div>
</div>
<?php endif; ?>
<div class="card">
<div class="table-responsive">
......@@ -63,12 +65,16 @@
<td dir="ltr" style="text-align:right;"><?= e($dim['name_en']) ?></td>
<td><?= (int)$dim['is_required'] ? '<span style="color:#D97706;">نعم</span>' : '—' ?></td>
<td>
<?php if (can('accounting.coa.manage')): ?>
<form method="POST" action="/accounting/dimensions/<?= (int)$dim['id'] ?>/toggle-active" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" style="background:none;border:none;cursor:pointer;color:<?= (int)$dim['is_active'] ? '#059669' : '#DC2626' ?>;font-weight:600;">
<?= (int)$dim['is_active'] ? 'نشط' : 'موقف' ?>
</button>
</form>
<?php else: ?>
<span style="color:<?= (int)$dim['is_active'] ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= (int)$dim['is_active'] ? 'نشط' : 'موقف' ?></span>
<?php endif; ?>
</td>
<td><a href="/accounting/dimensions/<?= (int)$dim['id'] ?>" class="btn btn-sm btn-outline">القيم</a></td>
</tr>
......
......@@ -8,6 +8,7 @@
<a href="/accounting/dimensions" class="btn btn-outline">العودة</a>
</div>
<?php if (can('accounting.coa.manage')): ?>
<div class="card" style="margin-bottom:15px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إضافة قيمة</h3>
......@@ -44,6 +45,7 @@
</form>
</div>
</div>
<?php endif; ?>
<div class="card">
<div class="table-responsive">
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>الاعتمادات المستندية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.instruments.manage')): ?>
<a href="/accounting/documentary-credits/create" class="btn btn-primary">+ اعتماد جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>السنوات المالية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.fiscal_year.manage')): ?>
<a href="/accounting/fiscal-years/create" class="btn btn-primary">+ سنة مالية جديدة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -29,7 +29,7 @@ $statusLabel = match($fiscal_year['status']) {
<div><span style="color:#6B7280;">تاريخ النهاية:</span><br><strong><?= e($fiscal_year['end_date']) ?></strong></div>
<div><span style="color:#6B7280;">الحالية:</span><br><strong><?= (int)$fiscal_year['is_current'] ? 'نعم' : 'لا' ?></strong></div>
</div>
<?php if ($fiscal_year['status'] === 'open'): ?>
<?php if ($fiscal_year['status'] === 'open' && can('accounting.fiscal_year.close')): ?>
<div style="margin-top:15px;">
<form method="POST" action="/accounting/fiscal-years/<?= (int)$fiscal_year['id'] ?>/close" style="display:inline;">
<?= csrf_field() ?>
......@@ -82,7 +82,7 @@ $statusLabel = match($fiscal_year['status']) {
<td style="direction:ltr;text-align:right;"><?= money($p['total_debit']) ?></td>
<td><span style="color:<?= $pColor ?>;font-weight:600;"><?= $pLabel ?></span></td>
<td>
<?php if ($p['status'] === 'open' || $p['status'] === 'reopened'): ?>
<?php if (($p['status'] === 'open' || $p['status'] === 'reopened') && can('accounting.period.close')): ?>
<form method="POST" action="/accounting/period-closing/close-month" style="display:inline;">
<?= csrf_field() ?>
<input type="hidden" name="fiscal_year_id" value="<?= (int)$fiscal_year['id'] ?>">
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>خطابات الضمان<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.instruments.manage')): ?>
<a href="/accounting/guarantees/create" class="btn btn-primary">+ خطاب ضمان جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -11,7 +11,7 @@ $__template->layout('Layout.main');
<div style="display:flex;gap:8px;">
<a href="/accounting/instruments/due-soon" class="btn btn-outline">مستحقة قريبًا</a>
<a href="/accounting/portfolios" class="btn btn-outline">الحوافظ</a>
<button type="button" id="btn-new-inst" class="btn btn-primary">+ ورقة جديدة</button>
<?php if (can('accounting.instruments.manage')): ?><button type="button" id="btn-new-inst" class="btn btn-primary">+ ورقة جديدة</button><?php endif; ?>
</div>
</div>
......@@ -40,6 +40,7 @@ $__template->layout('Layout.main');
</div>
<!-- Create Form -->
<?php if (can('accounting.instruments.manage')): ?>
<div class="card" id="create-form" style="margin-bottom:15px;display:none;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">تسجيل ورقة تجارية جديدة</h3>
......@@ -111,6 +112,7 @@ $__template->layout('Layout.main');
</form>
</div>
</div>
<?php endif; ?>
<!-- List -->
<div class="card">
......
......@@ -81,7 +81,7 @@ $inst = new NegotiableInstrument($instrument);
$inst->exists = true;
$allowed = $inst->getAllowedTransitions();
?>
<?php if (!empty($allowed)): ?>
<?php if (!empty($allowed) && can('accounting.instruments.manage')): ?>
<div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">تغيير الحالة</h3>
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>قيود اليومية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.journal.create')): ?>
<a href="/accounting/journal-entries/create" class="btn btn-primary">+ قيد جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -41,13 +41,13 @@ $statusLabel = match($entry['status']) {
<!-- Actions -->
<div style="margin-top:20px;display:flex;gap:8px;">
<?php if ($entry['status'] === 'draft'): ?>
<?php if ($entry['status'] === 'draft' && can('accounting.journal.post')): ?>
<form method="POST" action="/accounting/journal-entries/<?= (int)$entry['id'] ?>/post" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-primary" onclick="return confirm('هل تريد ترحيل هذا القيد؟')">ترحيل القيد</button>
</form>
<?php endif; ?>
<?php if ($entry['status'] === 'posted' && !$entry['reversal_of_id']): ?>
<?php if ($entry['status'] === 'posted' && !$entry['reversal_of_id'] && can('accounting.journal.reverse')): ?>
<form method="POST" action="/accounting/journal-entries/<?= (int)$entry['id'] ?>/reverse" style="display:inline;">
<?= csrf_field() ?>
<input type="hidden" name="reason" value="عكس يدوي">
......
......@@ -5,10 +5,11 @@
<?php $__template->section('content'); ?>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;">
<h2 style="margin:0;">أنواع اليومية</h2>
<button type="button" id="btn-new-type" class="btn btn-primary">+ نوع يومية جديد</button>
<?php if (can('accounting.journal.create')): ?><button type="button" id="btn-new-type" class="btn btn-primary">+ نوع يومية جديد</button><?php endif; ?>
</div>
<!-- Create Form (hidden by default) -->
<?php if (can('accounting.journal.create')): ?>
<div class="card" id="create-form" style="margin-bottom:15px;display:none;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;">إنشاء نوع يومية جديد</h3>
......@@ -68,6 +69,7 @@
</form>
</div>
</div>
<?php endif; ?>
<!-- List -->
<div class="card">
......@@ -95,14 +97,18 @@
<td><?= (int)$t['is_auto_generated'] ? '<span style="color:#059669;">&#10003;</span>' : '—' ?></td>
<td><?= (int)$t['is_default'] ? '<span style="color:#2563EB;">&#10003;</span>' : '—' ?></td>
<td>
<?php if (can('accounting.journal.create')): ?>
<form method="POST" action="/accounting/journal-types/<?= (int)$t['id'] ?>/toggle-active" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" style="background:none;border:none;cursor:pointer;color:<?= (int)$t['is_active'] ? '#059669' : '#DC2626' ?>;font-weight:600;">
<?= (int)$t['is_active'] ? 'نشط' : 'موقف' ?>
</button>
</form>
<?php else: ?>
<span style="color:<?= (int)$t['is_active'] ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= (int)$t['is_active'] ? 'نشط' : 'موقف' ?></span>
<?php endif; ?>
</td>
<td><a href="/accounting/journal-types/<?= (int)$t['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a></td>
<td><?php if (can('accounting.journal.create')): ?><a href="/accounting/journal-types/<?= (int)$t['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a><?php endif; ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($types)): ?>
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>القروض البنكية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.loans.manage')): ?>
<a href="/accounting/loans/create" class="btn btn-primary">+ قرض جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -5,7 +5,7 @@
<?php $__template->section('content'); ?>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;">
<h2 style="margin:0;">القيود الافتتاحية والأرصدة التاريخية</h2>
<a href="/accounting/opening-entries/create" class="btn btn-primary">+ قيد افتتاحي جديد</a>
<?php if (can('accounting.opening_entry.manage')): ?><a href="/accounting/opening-entries/create" class="btn btn-primary">+ قيد افتتاحي جديد</a><?php endif; ?>
</div>
<div class="card" style="margin-bottom:15px;">
......@@ -20,7 +20,7 @@
<?php endforeach; ?>
</select>
</div>
<?php if ($selectedYearId): ?>
<?php if ($selectedYearId && can('accounting.opening_entry.manage')): ?>
<div>
<form method="POST" action="/accounting/opening-entries/snapshot" style="display:inline;">
<?= csrf_field() ?>
......
......@@ -66,14 +66,14 @@
<td><span style="color:<?= $pColor ?>;font-weight:600;"><?= $pLabel ?></span></td>
<td style="font-size:12px;color:#6B7280;"><?= e($p['closed_at'] ?? '—') ?></td>
<td>
<?php if ($p['status'] === 'open' || $p['status'] === 'reopened'): ?>
<?php if (($p['status'] === 'open' || $p['status'] === 'reopened') && can('accounting.period.close')): ?>
<form method="POST" action="/accounting/period-closing/close-month" style="display:inline;">
<?= csrf_field() ?>
<input type="hidden" name="fiscal_year_id" value="<?= $fiscal_year_id ?>">
<input type="hidden" name="period" value="<?= e($p['period']) ?>">
<button type="submit" class="btn btn-sm btn-outline" onclick="return confirm('إغلاق الفترة <?= e($p['period']) ?>?')">إغلاق</button>
</form>
<?php elseif ($p['status'] === 'closed'): ?>
<?php elseif ($p['status'] === 'closed' && can('accounting.period.reopen')): ?>
<form method="POST" action="/accounting/period-closing/reopen-month" style="display:inline;" onsubmit="var r=prompt('سبب إعادة الفتح:');if(!r)return false;this.querySelector('[name=reason]').value=r;">
<?= csrf_field() ?>
<input type="hidden" name="fiscal_year_id" value="<?= $fiscal_year_id ?>">
......
......@@ -10,7 +10,7 @@ $__template->layout('Layout.main');
<h2 style="margin:0;">الحوافظ</h2>
<div style="display:flex;gap:8px;">
<a href="/accounting/instruments" class="btn btn-outline">الأوراق التجارية</a>
<a href="/accounting/portfolios/create" class="btn btn-primary">+ حافظة جديدة</a>
<?php if (can('accounting.instruments.manage')): ?><a href="/accounting/portfolios/create" class="btn btn-primary">+ حافظة جديدة</a><?php endif; ?>
</div>
</div>
......
......@@ -83,7 +83,7 @@ $__template->layout('Layout.main');
</div>
</div>
<?php if ($portfolio['status'] === 'draft'): ?>
<?php if ($portfolio['status'] === 'draft' && can('accounting.instruments.manage')): ?>
<form method="POST" action="/accounting/portfolios/<?= (int)$portfolio['id'] ?>/confirm">
<?= csrf_field() ?>
<button type="submit" class="btn btn-primary" onclick="return confirm('هل أنت متأكد من تأكيد الحافظة؟ سيتم تحديث حالة جميع الأوراق.');">تأكيد الحافظة</button>
......
......@@ -3,7 +3,9 @@
<?php $__template->section('title'); ?>التسويات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('accounting.settlements.manage')): ?>
<a href="/accounting/settlements/create" class="btn btn-primary">+ تسوية جديدة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -56,12 +56,14 @@
<?= $rule['last_triggered_at'] ? e($rule['last_triggered_at']) : '—' ?>
</td>
<td>
<?php if (can('alert.manage')): ?>
<form method="POST" action="/alerts/<?= (int)$rule['id'] ?>/toggle" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm <?= (int)$rule['is_active'] === 1 ? 'btn-outline-danger' : 'btn-outline-success' ?>" style="font-size:12px;">
<?= (int)$rule['is_active'] === 1 ? 'إيقاف' : 'تفعيل' ?>
</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -62,12 +62,14 @@
</td>
<td>
<?php if ($log['status'] === 'sent'): ?>
<?php if (can('alert.manage')): ?>
<form method="POST" action="/alerts/log/<?= (int)$log['id'] ?>/acknowledge" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-outline" style="font-size:12px;">
تأكيد الإطلاع
</button>
</form>
<?php endif; ?>
<?php elseif ($log['status'] === 'acknowledged'): ?>
<span style="color:#6B7280;font-size:12px;">✓ تم</span>
<?php endif; ?>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>سجل دخول الضيوف<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('carnet.issue')): ?>
<a href="/carnets/guest-entries/create" class="btn btn-primary">+ تسجيل دخول ضيف</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;padding:15px;">
......
......@@ -36,8 +36,12 @@
<td>
<div style="display:flex;gap:5px;">
<?php if ($r['is_active']): ?>
<?php if (can('carnet.print')): ?>
<a href="/carnets/<?= (int) $r['id'] ?>/print" class="btn btn-sm btn-primary" target="_blank">طباعة</a>
<?php endif; ?>
<?php if (can('carnet.deactivate')): ?>
<form method="POST" action="/carnets/<?= (int) $r['id'] ?>/deactivate" style="display:inline;"><?= csrf_field() ?><input type="hidden" name="reason" value="إلغاء يدوي"><button type="submit" class="btn btn-sm btn-outline" style="color:#DC2626;" onclick="return confirm('إلغاء الكارنيه؟')">إلغاء</button></form>
<?php endif; ?>
<?php else: ?>
<span style="color:#9CA3AF;font-size:12px;"><?= e($r['deactivated_reason'] ?? '—') ?></span>
<?php endif; ?>
......
......@@ -125,6 +125,7 @@ $statusLabel = match($pr['status']) {
<!-- Payment Action -->
<div class="card" style="padding:25px;border:2px solid #0D7377;">
<h3 style="margin:0 0 20px;color:#0D7377;">تحصيل الدفعة</h3>
<?php if (can('cashier.process_payment')): ?>
<form method="POST" action="/cashier/<?= (int)$pr['id'] ?>/complete" id="paymentForm">
<?= csrf_field() ?>
<div class="form-group" style="margin-bottom:20px;">
......@@ -147,7 +148,9 @@ $statusLabel = match($pr['status']) {
تأكيد التحصيل
</button>
</form>
<?php endif; ?>
<?php if (can('cashier.cancel_request')): ?>
<hr style="margin:20px 0;border:0;border-top:1px solid #E5E7EB;">
<form method="POST" action="/cashier/<?= (int)$pr['id'] ?>/cancel" style="text-align:center;">
......@@ -158,6 +161,7 @@ $statusLabel = match($pr['status']) {
إلغاء الطلب
</button>
</form>
<?php endif; ?>
</div>
</div>
</div>
......
......@@ -137,18 +137,22 @@
<td style="font-size:12px;color:#6B7280;"><?= e($r['requested_by_name'] ?? '—') ?></td>
<td>
<?php if ($r['status'] === 'pending' || $r['status'] === 'processing'): ?>
<?php if (can('cashier.process_payment')): ?>
<a href="/cashier/<?= (int)$r['id'] ?>" class="btn btn-sm btn-primary">تحصيل</a>
<?php endif; ?>
<?php elseif ($r['status'] === 'completed'): ?>
<span style="color:#059669;font-size:12px;">&#x2705; تم</span>
<?php if (!empty($r['receipt_id'])): ?>
<?php if (!empty($r['receipt_id']) && can('receipt.print')): ?>
<a href="/receipts/<?= (int)$r['receipt_id'] ?>/print" target="_blank" class="btn btn-sm btn-outline" style="font-size:11px;margin-top:4px;">&#x1f5a8; طباعة</a>
<?php endif; ?>
<?php if (can('cashier.cancel_request')): ?>
<form method="POST" action="/cashier/<?= (int)$r['id'] ?>/cancel" style="display:inline;margin-top:4px;">
<?= csrf_field() ?>
<input type="hidden" name="reason" value="">
<button type="submit" class="btn btn-sm" style="font-size:11px;color:#DC2626;border:1px solid #DC2626;background:transparent;"
onclick="var r=prompt('سبب إلغاء الدفعة:');if(!r)return false;this.form.reason.value=r;return confirm('سيتم إلغاء الدفعة وإرجاع حالة العضوية. متأكد؟');">إلغاء الدفعة</button>
</form>
<?php endif; ?>
<?php endif; ?>
</td>
</tr>
......
......@@ -41,7 +41,9 @@ $memberId = $memberId ?? 0;
<td>
<div style="display:flex;gap:5px;">
<a href="/members/<?= (int) $memberId ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a>
<?php if (can('child.edit')): ?>
<a href="/members/<?= (int) $memberId ?>/children/<?= (int) $c['id'] ?>/edit" class="btn btn-sm btn-outline">تعديل</a>
<?php endif; ?>
</div>
</td>
</tr>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>بيانات الابن: <?= e($child->full_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('child.edit')): ?>
<a href="/members/<?= (int) $member['id'] ?>/children/<?= (int) $child->id ?>/edit" class="btn btn-outline">تعديل</a>
<?php endif; ?>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">← العودة للعضو</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -34,6 +36,7 @@
</table>
<?php if ($child->status === 'active' && !$child->is_frozen && $child->gender === 'male' && (int) $child->age_years >= 25): ?>
<?php if (can('child.freeze')): ?>
<div style="margin-top:20px;padding:15px;background:#FEF2F2;border:1px solid #FECACA;border-radius:8px;">
<strong style="color:#DC2626;">⚠ هذا الابن بلغ سن 25 ويجب تجميد عضويته</strong>
<form method="POST" action="/members/<?= (int) $member['id'] ?>/children/<?= (int) $child->id ?>/freeze" style="margin-top:10px;">
......@@ -43,6 +46,7 @@
</form>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if ($child->remarks): ?>
<div style="margin-top:15px;"><h4 style="color:#6B7280;font-size:13px;">ملاحظات</h4><p style="font-size:14px;"><?= nl2br(e($child->remarks)) ?></p></div>
......
......@@ -5,7 +5,9 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?>المدربين<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('sa.coach.manage')): ?>
<a href="/coaches/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مدرب</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -113,9 +115,11 @@ $__template->layout('Layout.main');
<a href="/coaches/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
<?php if (can('sa.coach.manage')): ?>
<a href="/coaches/<?= (int) $c['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit-3" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
<?php endif; ?>
</div>
</div>
</div>
......@@ -141,7 +145,9 @@ $__template->layout('Layout.main');
ابدأ بإضافة مدرب جديد.
<?php endif; ?>
</p>
<?php if (can('sa.coach.manage')): ?>
<a href="/coaches/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مدرب</a>
<?php endif; ?>
</div>
<?php endif; ?>
......
......@@ -5,6 +5,7 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?><?= e($coach->full_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('sa.coach.manage')): ?>
<a href="/coaches/<?= (int) $coach->id ?>/edit" class="btn btn-outline"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<form method="POST" action="/coaches/<?= (int) $coach->id ?>/toggle" style="display:inline;">
<?= csrf_field() ?>
......@@ -13,6 +14,7 @@ $__template->layout('Layout.main');
<?= $coach->is_active ? 'إيقاف' : 'تفعيل' ?>
</button>
</form>
<?php endif; ?>
<a href="/coaches" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
......
......@@ -52,11 +52,13 @@
<div style="display:flex;gap:5px;">
<a href="/documents/<?= (int) $doc['id'] ?>/preview" class="btn btn-sm btn-outline" target="_blank">معاينة</a>
<a href="/documents/<?= (int) $doc['id'] ?>/download" class="btn btn-sm btn-outline">تحميل</a>
<?php if (can('document.delete')): ?>
<form method="POST" action="/documents/<?= (int) $doc['id'] ?>/archive" style="display:inline;">
<?= csrf_field() ?>
<input type="hidden" name="redirect_url" value="/documents/upload/<?= (int) $member['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline" style="color:#DC2626;" onclick="return confirm('أرشفة المستند؟')">حذف</button>
</form>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
......
......@@ -6,7 +6,9 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?>الملاعب والمرافق<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('facility.manage')): ?>
<a href="/facilities/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مرفق</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -159,9 +161,11 @@ $typeIcons = [
<a href="/facilities/<?= (int) $f['id'] ?>" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="eye" style="width:13px;height:13px;vertical-align:middle;"></i> عرض
</a>
<?php if (can('facility.manage')): ?>
<a href="/facilities/<?= (int) $f['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit-3" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
<?php endif; ?>
</div>
</div>
</div>
......@@ -187,7 +191,9 @@ $typeIcons = [
ابدأ بإضافة مرفق جديد للنادي.
<?php endif; ?>
</p>
<?php if (can('facility.manage')): ?>
<a href="/facilities/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إضافة مرفق</a>
<?php endif; ?>
</div>
<?php endif; ?>
......
......@@ -6,7 +6,9 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?><?= e($facility->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('facility.manage')): ?>
<a href="/facilities/<?= (int) $facility->id ?>/edit" class="btn btn-outline"><i data-lucide="edit-3" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> تعديل</a>
<?php endif; ?>
<a href="/facilities" class="btn btn-outline"><i data-lucide="arrow-right" style="width:15px;height:15px;vertical-align:middle;margin-left:4px;"></i> العودة للقائمة</a>
<?php $__template->endSection(); ?>
......
......@@ -4,7 +4,9 @@ $__template->layout('Layout.main');
<?php $__template->section('title'); ?>شبكات المرافق<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('facility.manage')): ?>
<a href="/facility-grids/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> إنشاء شبكة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -13,7 +15,9 @@ $__template->layout('Layout.main');
<div class="card" style="padding:60px;text-align:center;">
<i data-lucide="grid-3x3" style="width:48px;height:48px;color:#9CA3AF;margin-bottom:16px;"></i>
<p style="font-size:16px;color:#6B7280;margin-bottom:16px;">لا توجد شبكات مرافق بعد</p>
<?php if (can('facility.manage')): ?>
<a href="/facility-grids/create" class="btn btn-primary">إنشاء أول شبكة</a>
<?php endif; ?>
</div>
<?php else: ?>
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:20px;">
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الخطط الشهرية — <?= e($grid->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('facility.manage')): ?>
<a href="/facility-grids/<?= (int) $grid->id ?>/plans/create" class="btn btn-primary">+ خطة جديدة</a>
<?php endif; ?>
<a href="/facility-grids/<?= (int) $grid->id ?>" class="btn btn-outline">العودة للشبكة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -19,7 +19,7 @@
<td><span style="color:<?= match($r['status']) { 'imposed' => '#DC2626', 'paid' => '#059669', 'appealed' => '#D97706', 'cancelled' => '#6B7280', 'waived' => '#0284C7', default => '#6B7280' } ?>;font-weight:600;"><?= match($r['status']) { 'imposed' => 'مفروضة', 'paid' => 'مدفوعة', 'appealed' => 'متظلم', 'appeal_upheld' => 'تأييد', 'cancelled' => 'ملغاة', 'waived' => 'معفاة', default => $r['status'] } ?></span></td>
<td>
<div style="display:flex;gap:5px;flex-wrap:wrap;">
<?php if (in_array($r['status'], ['imposed', 'appeal_upheld']) && $r['penalty_type'] === 'fine' && bccomp($r['amount'], $r['paid_amount'] ?? '0', 2) > 0): ?>
<?php if (in_array($r['status'], ['imposed', 'appeal_upheld']) && $r['penalty_type'] === 'fine' && bccomp($r['amount'], $r['paid_amount'] ?? '0', 2) > 0 && can('fine.collect')): ?>
<form method="POST" action="/fines/<?= (int) $r['id'] ?>/pay" style="display:flex;gap:5px;">
<?= csrf_field() ?>
<input type="hidden" name="payment_method" value="cash">
......@@ -29,7 +29,7 @@
<?php if ($r['status'] === 'imposed' && !$r['appeal_submitted']): ?>
<a href="/fines/<?= (int) $r['id'] ?>/appeal" class="btn btn-sm btn-outline">تظلم</a>
<?php endif; ?>
<?php if ($r['status'] === 'appealed'): ?>
<?php if ($r['status'] === 'appealed' && can('fine.waive')): ?>
<form method="POST" action="/fines/<?= (int) $r['id'] ?>/appeal-decide" style="display:flex;gap:3px;">
<?= csrf_field() ?>
<button type="submit" name="appeal_decision" value="upheld" class="btn btn-sm" style="background:#DC2626;color:#fff;border:none;padding:3px 8px;border-radius:4px;cursor:pointer;font-size:11px;">تأييد</button>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تسجيل مخالفة — <?= e($member['full_name_ar']) ?><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php if (can('fine.impose')): ?>
<form method="POST" action="/violations/store/<?= (int) $member['id'] ?>">
<?= csrf_field() ?>
<div class="card" style="padding:20px;">
......@@ -13,4 +14,5 @@
<button type="submit" class="btn btn-primary" style="margin-top:15px;">تسجيل المخالفة</button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline" style="margin-top:15px;">إلغاء</a>
</form>
<?php endif; ?>
<?php $__template->endSection(); ?>
\ No newline at end of file
......@@ -22,7 +22,7 @@
<td style="font-size:13px;"><?= e($r['reported_by_name'] ?? '—') ?></td>
<td><span style="color:<?= $r['status'] === 'reported' ? '#D97706' : '#059669' ?>;font-weight:600;"><?= $r['status'] === 'reported' ? 'مُبلّغ' : 'تم فرض عقوبة' ?></span></td>
<td>
<?php if ($r['status'] === 'reported'): ?>
<?php if ($r['status'] === 'reported' && can('fine.impose')): ?>
<form method="POST" action="/fines/impose/<?= (int) $r['id'] ?>" style="display:flex;gap:5px;align-items:center;">
<?= csrf_field() ?>
<select name="penalty_type" class="form-select" style="width:auto;font-size:12px;" required><option value="">عقوبة</option><option value="warning">لفت نظر</option><option value="caution">إنذار</option><option value="fine">غرامة</option><option value="suspension">إيقاف</option><option value="ban">منع دخول</option><option value="termination">إنهاء عضوية</option></select>
......
......@@ -42,7 +42,9 @@
</table>
</div>
<div style="padding:12px;border-top:1px solid #E5E7EB;display:flex;justify-content:flex-end;">
<?php if (can('hr.attendance.manage')): ?>
<button type="submit" class="btn btn-primary">حفظ الحضور</button>
<?php endif; ?>
</div>
</div>
</form>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>سجل الحضور<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.attendance.manage')): ?>
<a href="/hr/attendance/daily" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="calendar-clock" style="width:16px;height:16px;"></i> تسجيل يومي</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;">
......@@ -46,7 +48,7 @@
<td><?= number_format((float) ($r['actual_hours'] ?? 0), 1) ?></td>
<td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $statusColors[$r['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($statusLabels[$r['status']] ?? $r['status']) ?></span></td>
<td><?= $r['is_approved'] ? '<span style="color:#059669;">&#10003;</span>' : '<span style="color:#9CA3AF;">-</span>' ?></td>
<td><a href="/hr/attendance/<?= (int) $r['id'] ?>/edit" style="color:#2563EB;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a></td>
<td><?php if (can('hr.attendance.manage')): ?><a href="/hr/attendance/<?= (int) $r['id'] ?>/edit" style="color:#2563EB;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a><?php endif; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
......@@ -54,7 +56,11 @@
</table>
</div>
<div style="padding:12px;border-top:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<?php if (can('hr.attendance.approve')): ?>
<button type="submit" class="btn btn-primary">اعتماد المحدد</button>
<?php else: ?>
<div></div>
<?php endif; ?>
<?php if (!empty($pagination)): ?>
<?php $__template->include('Shared.Components.pagination', ['pagination' => $pagination]); ?>
<?php endif; ?>
......
......@@ -2,9 +2,11 @@
<?php $__template->section('title'); ?>العقود<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.contract.manage')): ?>
<a href="/hr/contracts/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="plus" style="width:16px;height:16px;"></i> إنشاء عقد
</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -2,7 +2,7 @@
<?php $__template->section('title'); ?>العقد: <?= e($contract->contract_number) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if ($contract->status === 'draft'): ?>
<?php if ($contract->status === 'draft' && can('hr.contract.manage')): ?>
<a href="/hr/contracts/<?= (int) $contract->id ?>/edit" class="btn btn-primary">تعديل</a>
<?php endif; ?>
<a href="/hr/contracts" class="btn btn-secondary">رجوع</a>
......@@ -28,8 +28,10 @@
<div><span style="color:#6B7280;font-size:13px;">فترة الاختبار</span><div><?= (int) $contract->probation_months ?> أشهر</div></div>
<div><span style="color:#6B7280;font-size:13px;">ساعات العمل</span><div><?= e($contract->working_hours_per_day) ?> ساعة/يوم</div></div>
<div><span style="color:#6B7280;font-size:13px;">فترة الإنذار</span><div><?= (int) $contract->notice_period_months ?> أشهر</div></div>
<?php if (can('hr.employee.view_salary')): ?>
<div><span style="color:#6B7280;font-size:13px;">الراتب الأساسي</span><div style="font-weight:600;"><?= number_format((float) $contract->basic_salary, 2) ?> ج.م</div></div>
<div><span style="color:#6B7280;font-size:13px;">إجمالي الحزمة</span><div style="font-weight:600;"><?= number_format((float) $contract->total_package, 2) ?> ج.م</div></div>
<?php endif; ?>
</div>
</div>
......@@ -63,7 +65,7 @@
</div>
<?php endif; ?>
<?php if ($contract->status === 'active'): ?>
<?php if ($contract->status === 'active' && can('hr.contract.manage')): ?>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:20px;">
<div class="card" style="flex:1;padding:16px;">
<h4 style="margin:0 0 12px;">تجديد العقد</h4>
......
......@@ -2,9 +2,11 @@
<?php $__template->section('title'); ?>الأقسام<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.department.manage')): ?>
<a href="/hr/departments/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="plus" style="width:16px;height:16px;"></i> إضافة قسم
</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -58,9 +60,11 @@
<?php endif; ?>
</td>
<td>
<?php if (can('hr.department.manage')): ?>
<a href="/hr/departments/<?= (int) $dept['id'] ?>/edit" title="تعديل" style="color:#2563EB;margin-inline-end:8px;">
<i data-lucide="edit" style="width:16px;height:16px;"></i>
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -2,9 +2,11 @@
<?php $__template->section('title'); ?>القسم: <?= e($department->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.department.manage')): ?>
<a href="/hr/departments/<?= (int) $department->id ?>/edit" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="edit" style="width:16px;height:16px;"></i> تعديل
</a>
<?php endif; ?>
<a href="/hr/departments" class="btn btn-secondary">رجوع</a>
<?php $__template->endSection(); ?>
......@@ -54,11 +56,13 @@
</div>
<?php endif; ?>
<?php if (can('hr.department.manage')): ?>
<div style="display:flex;gap:10px;justify-content:flex-end;">
<form method="POST" action="/hr/departments/<?= (int) $department->id ?>/archive" onsubmit="return confirm('هل أنت متأكد من حذف هذا القسم؟');">
<?= csrf_field() ?>
<button type="submit" class="btn btn-danger">حذف القسم</button>
</form>
</div>
<?php endif; ?>
<script>if(typeof lucide!=='undefined')lucide.createIcons();</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>الإجراءات التأديبية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/disciplinary/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إجراء تأديبي جديد</a><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.disciplinary.manage')): ?>
<a href="/hr/disciplinary/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إجراء تأديبي جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php $sl = ['investigation'=>'تحت التحقيق','pending_decision'=>'في انتظار القرار','decided'=>'تم تحديد العقوبة','appealed'=>'متظلم','appeal_reviewed'=>'تم مراجعة التظلم','enforced'=>'منفذ','cancelled'=>'ملغى']; ?>
<?php $sc = ['investigation'=>'#DBEAFE;color:#1E40AF','pending_decision'=>'#FEF3C7;color:#92400E','decided'=>'#FDE8E8;color:#9B1C1C','appealed'=>'#E0E7FF;color:#3730A3','appeal_reviewed'=>'#E0E7FF;color:#3730A3','enforced'=>'#DEF7EC;color:#03543F','cancelled'=>'#E5E7EB;color:#374151']; ?>
......
......@@ -54,11 +54,11 @@
<?php endif; ?>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<?php if (in_array($action->status, ['investigation', 'pending_decision'], true)): ?>
<?php if (in_array($action->status, ['investigation', 'pending_decision'], true) && can('hr.disciplinary.manage')): ?>
<a href="/hr/disciplinary/<?= (int) $action->id ?>/edit" class="btn btn-secondary">تعديل</a>
<?php endif; ?>
<?php if (in_array($action->status, ['investigation', 'pending_decision'], true)): ?>
<?php if (in_array($action->status, ['investigation', 'pending_decision'], true) && can('hr.disciplinary.manage')): ?>
<div class="card" style="flex:1;min-width:300px;padding:16px;">
<h4 style="margin:0 0 12px;font-size:14px;">اتخاذ قرار تأديبي</h4>
<form method="POST" action="/hr/disciplinary/<?= (int) $action->id ?>/decide" style="display:flex;flex-direction:column;gap:10px;">
......
......@@ -3,6 +3,7 @@
<?php $__template->section('page_actions'); ?><a href="/hr/documents" class="btn btn-secondary">رجوع</a><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php if (can('hr.document.manage')): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">رفع مستند جديد</h3></div>
<form method="POST" action="/hr/documents/upload" enctype="multipart/form-data" style="padding:16px;">
......@@ -23,6 +24,7 @@
<button type="submit" class="btn btn-primary">رفع المستند</button>
</form>
</div>
<?php endif; ?>
<div class="card">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">المستندات الحالية (<?= count($documents) ?>)</h3></div>
......@@ -42,13 +44,17 @@
<td>
<?php if ((int) ($d['is_verified'] ?? 0)): ?>
<span style="color:#059669;font-weight:600;">متحقق</span>
<?php else: ?>
<?php elseif (can('hr.document.manage')): ?>
<form method="POST" action="/hr/documents/<?= (int) $d['id'] ?>/verify" style="display:inline;"><?= csrf_field() ?><button type="submit" class="btn btn-secondary" style="padding:2px 8px;font-size:12px;">تحقق</button></form>
<?php else: ?>
<span style="color:#D97706;">غير متحقق</span>
<?php endif; ?>
</td>
<td style="display:flex;gap:6px;">
<?php if (!empty($d['file_path'])): ?><a href="/<?= e($d['file_path']) ?>" target="_blank" style="color:#2563EB;"><i data-lucide="download" style="width:16px;height:16px;"></i></a><?php endif; ?>
<?php if (can('hr.document.manage')): ?>
<form method="POST" action="/hr/documents/<?= (int) $d['id'] ?>/archive" style="display:inline;" onsubmit="return confirm('هل أنت متأكد من حذف هذا المستند؟');"><?= csrf_field() ?><button type="submit" style="background:none;border:none;color:#DC2626;cursor:pointer;padding:0;"><i data-lucide="trash-2" style="width:16px;height:16px;"></i></button></form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -2,9 +2,11 @@
<?php $__template->section('title'); ?>الموظفين<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.employee.manage')): ?>
<a href="/hr/employees/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="user-plus" style="width:16px;height:16px;"></i> إضافة موظف
</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -71,7 +73,9 @@
</td>
<td style="white-space:nowrap;">
<a href="/hr/employees/<?= (int) $emp['id'] ?>" title="عرض" style="color:#2563EB;margin-inline-end:8px;"><i data-lucide="eye" style="width:16px;height:16px;"></i></a>
<?php if (can('hr.employee.manage')): ?>
<a href="/hr/employees/<?= (int) $emp['id'] ?>/edit" title="تعديل" style="color:#059669;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -18,6 +18,7 @@
</div>
<?php if (!empty($components)): ?>
<?php if (can('hr.employee.manage_salary')): ?>
<form method="POST" action="/hr/employees/<?= (int) $profile->id ?>/salary">
<?= csrf_field() ?>
<div class="card" style="margin-bottom:20px;">
......@@ -71,6 +72,45 @@
</div>
</form>
<?php else: ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:16px;">مكونات الراتب</h3>
</div>
<div class="table-responsive">
<table class="data-table">
<thead>
<tr><th>المكون</th><th>النوع</th><th>طريقة الحساب</th><th>المبلغ الحالي</th></tr>
</thead>
<tbody>
<?php foreach ($components as $comp): ?>
<?php
$current = null;
foreach ($salaryDetails as $sd) {
if ((int) $sd['component_id'] === (int) $comp['id']) {
$current = $sd;
break;
}
}
?>
<tr>
<td><?= e($comp['name_ar']) ?></td>
<td>
<?php if ($comp['type'] === 'earning'): ?>
<span style="color:#059669;">استحقاق</span>
<?php else: ?>
<span style="color:#DC2626;">خصم</span>
<?php endif; ?>
</td>
<td><?= e($comp['calculation_type']) ?></td>
<td><?= $current ? number_format((float) $current['amount'], 2) : '-' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
<?php else: ?>
<div class="card" style="padding:40px;text-align:center;color:#9CA3AF;">
<p>لم يتم تحديد هيكل رواتب للموظف. قم بتحديد هيكل الرواتب أولاً من صفحة تعديل الملف.</p>
</div>
......
......@@ -2,10 +2,14 @@
<?php $__template->section('title'); ?><?= e($profile->first_name_ar . ' ' . $profile->last_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.employee.manage')): ?>
<a href="/hr/employees/<?= (int) $profile->id ?>/edit" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="edit" style="width:16px;height:16px;"></i> تعديل
</a>
<?php endif; ?>
<?php if (can('hr.employee.view_salary')): ?>
<a href="/hr/employees/<?= (int) $profile->id ?>/salary" class="btn btn-secondary">تفاصيل الراتب</a>
<?php endif; ?>
<a href="/hr/employees" class="btn btn-secondary">رجوع</a>
<?php $__template->endSection(); ?>
......@@ -47,7 +51,9 @@ $sc = $statusColors[$profile->employment_status] ?? '#E5E7EB;color:#374151';
<div><span style="color:#6B7280;font-size:13px;">سنوات الخدمة</span><div style="font-weight:600;"><?= number_format($yearsOfService, 1) ?> سنة</div></div>
<div><span style="color:#6B7280;font-size:13px;">نوع التوظيف</span><div><?= e($types[$profile->employment_type] ?? '-') ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">هيكل الرواتب</span><div><?= $structure ? e($structure->name_ar) : '-' ?></div></div>
<?php if (can('hr.employee.view_salary')): ?>
<div><span style="color:#6B7280;font-size:13px;">الراتب الأساسي</span><div style="font-weight:600;"><?= number_format((float) $profile->basic_salary, 2) ?> ج.م</div></div>
<?php endif; ?>
<div><span style="color:#6B7280;font-size:13px;">رقم التأمينات</span><div><?= e($profile->insurance_number ?? '-') ?></div></div>
</div>
</div>
......@@ -99,10 +105,18 @@ $sc = $statusColors[$profile->employment_status] ?? '#E5E7EB;color:#374151';
<?php endif; ?>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<?php if (can('hr.contract.manage')): ?>
<a href="/hr/contracts/create?employee_id=<?= (int) $profile->id ?>" class="btn btn-secondary">إنشاء عقد</a>
<?php endif; ?>
<?php if (can('hr.document.view')): ?>
<a href="/hr/documents/employee/<?= (int) $profile->id ?>" class="btn btn-secondary">المستندات</a>
<?php endif; ?>
<?php if (can('hr.attendance.view')): ?>
<a href="/hr/attendance/monthly/<?= (int) $profile->id ?>" class="btn btn-secondary">سجل الحضور</a>
<?php endif; ?>
<?php if (can('hr.leave.view')): ?>
<a href="/hr/leaves/balances/<?= (int) $profile->id ?>" class="btn btn-secondary">أرصدة الإجازات</a>
<?php endif; ?>
</div>
<script>if(typeof lucide!=='undefined')lucide.createIcons();</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>نهاية الخدمة<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/end-of-service/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إنهاء خدمة جديد</a><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.eos.manage')): ?>
<a href="/hr/end-of-service/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إنهاء خدمة جديد</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php $sl = ['draft'=>'مسودة','calculated'=>'تم الحساب','pending_approval'=>'في انتظار الاعتماد','approved'=>'معتمد','paid'=>'تم الصرف','cancelled'=>'ملغى']; ?>
<?php $sc = ['draft'=>'#FEF3C7;color:#92400E','calculated'=>'#DBEAFE;color:#1E40AF','pending_approval'=>'#FDE8E8;color:#9B1C1C','approved'=>'#D1FAE5;color:#065F46','paid'=>'#E5E7EB;color:#374151','cancelled'=>'#E5E7EB;color:#6B7280']; ?>
......@@ -24,17 +28,19 @@
<div class="card">
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>الموظف</th><th>النوع</th><th>تاريخ الإنهاء</th><th>صافي التسوية</th><th>الحالة</th><th>إجراءات</th></tr></thead>
<thead><tr><th>الموظف</th><th>النوع</th><th>تاريخ الإنهاء</th><?php if (can('hr.employee.view_salary')): ?><th>صافي التسوية</th><?php endif; ?><th>الحالة</th><th>إجراءات</th></tr></thead>
<tbody>
<?php if (empty($records)): ?>
<tr><td colspan="6" style="text-align:center;padding:40px;color:#9CA3AF;">لا توجد سجلات</td></tr>
<tr><td colspan="<?= can('hr.employee.view_salary') ? '6' : '5' ?>" style="text-align:center;padding:40px;color:#9CA3AF;">لا توجد سجلات</td></tr>
<?php else: ?>
<?php foreach ($records as $r): ?>
<tr>
<td><a href="/hr/employees/<?= (int) $r['employee_profile_id'] ?>"><?= e($r['full_name_ar'] ?? '-') ?></a></td>
<td><?= e($tl[$r['termination_type']] ?? $r['termination_type']) ?></td>
<td><?= e($r['effective_date'] ?? '-') ?></td>
<?php if (can('hr.employee.view_salary')): ?>
<td style="font-weight:700;"><?= $r['net_settlement'] !== null ? number_format((float) $r['net_settlement'], 2) . ' ج.م' : '-' ?></td>
<?php endif; ?>
<td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $sc[$r['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($sl[$r['status']] ?? $r['status']) ?></span></td>
<td><a href="/hr/end-of-service/<?= (int) $r['id'] ?>" style="color:#2563EB;"><i data-lucide="eye" style="width:16px;height:16px;"></i></a></td>
</tr>
......
......@@ -23,7 +23,7 @@
<?php endif; ?>
</div>
<?php if ($record->status !== 'draft'): ?>
<?php if ($record->status !== 'draft' && can('hr.employee.view_salary')): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">تفاصيل التسوية المالية</h3></div>
<div style="padding:16px;">
......@@ -64,13 +64,13 @@
<?php endif; ?>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<?php if (in_array($record->status, ['draft', 'calculated'], true)): ?>
<?php if (in_array($record->status, ['draft', 'calculated'], true) && can('hr.eos.manage')): ?>
<form method="POST" action="/hr/end-of-service/<?= (int) $record->id ?>/calculate"><?= csrf_field() ?><button type="submit" class="btn btn-primary"><?= $record->status === 'draft' ? 'حساب المستحقات' : 'إعادة الحساب' ?></button></form>
<?php endif; ?>
<?php if ($record->status === 'calculated'): ?>
<?php if ($record->status === 'calculated' && can('hr.eos.approve')): ?>
<form method="POST" action="/hr/end-of-service/<?= (int) $record->id ?>/approve"><?= csrf_field() ?><button type="submit" class="btn btn-primary">اعتماد</button></form>
<?php endif; ?>
<?php if ($record->status === 'approved'): ?>
<?php if ($record->status === 'approved' && can('hr.eos.manage')): ?>
<form method="POST" action="/hr/end-of-service/<?= (int) $record->id ?>/pay" style="display:flex;gap:8px;align-items:end;">
<?= csrf_field() ?>
<div><label style="display:block;margin-bottom:4px;font-size:13px;">تاريخ الصرف</label><input type="date" name="payment_date" value="<?= date('Y-m-d') ?>" class="form-control"></div>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>العطلات الرسمية <?= $year ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.attendance.manage')): ?>
<a href="/hr/holidays/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إضافة عطلة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;">
......@@ -27,10 +29,12 @@
<td><?= e($typeLabels[$h['holiday_type']] ?? $h['holiday_type']) ?></td>
<td><?= $h['is_recurring'] ? 'نعم' : 'لا' ?></td>
<td>
<?php if (can('hr.attendance.manage')): ?>
<a href="/hr/holidays/<?= (int) $h['id'] ?>/edit" style="color:#2563EB;margin-inline-end:8px;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a>
<form method="POST" action="/hr/holidays/<?= (int) $h['id'] ?>/archive" style="display:inline;" onsubmit="return confirm('حذف؟');">
<?= csrf_field() ?><button type="submit" style="border:none;background:none;color:#DC2626;cursor:pointer;"><i data-lucide="trash-2" style="width:16px;height:16px;"></i></button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -2,8 +2,10 @@
<?php $mn = [1=>'يناير',2=>'فبراير',3=>'مارس',4=>'أبريل',5=>'مايو',6=>'يونيو',7=>'يوليو',8=>'أغسطس',9=>'سبتمبر',10=>'أكتوبر',11=>'نوفمبر',12=>'ديسمبر']; ?>
<?php $__template->section('title'); ?>التأمينات الاجتماعية <?= $year ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.insurance.manage')): ?>
<a href="/hr/insurance/form1?year=<?= $year ?>" class="btn btn-secondary">استمارة 1</a>
<a href="/hr/insurance/form6?year=<?= $year ?>" class="btn btn-secondary">استمارة 6</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;">
......
......@@ -2,9 +2,11 @@
<?php $__template->section('title'); ?>المسميات الوظيفية<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.department.manage')): ?>
<a href="/hr/job-titles/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;">
<i data-lucide="plus" style="width:16px;height:16px;"></i> إضافة مسمى وظيفي
</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -43,9 +45,11 @@
<?php endif; ?>
</td>
<td>
<?php if (can('hr.department.manage')): ?>
<a href="/hr/job-titles/<?= (int) $jt['id'] ?>/edit" title="تعديل" style="color:#2563EB;">
<i data-lucide="edit" style="width:16px;height:16px;"></i>
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>طلبات الإجازات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (canAny(['hr.leave.manage', 'hr.leave.request'])): ?>
<a href="/hr/leaves/request" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> طلب إجازة</a>
<?php endif; ?>
<a href="/hr/leaves/balances" class="btn btn-secondary">الأرصدة</a>
<a href="/hr/leaves/calendar" class="btn btn-secondary">التقويم</a>
<?php $__template->endSection(); ?>
......
......@@ -41,13 +41,17 @@
<?php if ($leaveRequest->status === 'pending'): ?>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<?php if (can('hr.leave.approve')): ?>
<form method="POST" action="/hr/leaves/<?= (int) $leaveRequest->id ?>/approve"><?= csrf_field() ?><button type="submit" class="btn btn-primary">موافقة</button></form>
<form method="POST" action="/hr/leaves/<?= (int) $leaveRequest->id ?>/reject" style="display:flex;gap:8px;align-items:end;">
<?= csrf_field() ?>
<input type="text" name="rejection_reason" placeholder="سبب الرفض..." class="form-control" style="min-width:200px;">
<button type="submit" class="btn btn-danger">رفض</button>
</form>
<?php endif; ?>
<?php if (can('hr.leave.manage')): ?>
<form method="POST" action="/hr/leaves/<?= (int) $leaveRequest->id ?>/cancel" onsubmit="return confirm('هل أنت متأكد؟');"><?= csrf_field() ?><button type="submit" class="btn btn-secondary">إلغاء الطلب</button></form>
<?php endif; ?>
</div>
<?php endif; ?>
<script>if(typeof lucide!=='undefined')lucide.createIcons();</script>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>السلف والقروض<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/loans/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> طلب سلفة</a><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.loan.manage')): ?>
<a href="/hr/loans/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> طلب سلفة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card" style="margin-bottom:20px;">
<form method="GET" action="/hr/loans" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;padding:16px;">
......@@ -22,18 +26,20 @@
<?php $sc = ['pending'=>'#FEF3C7;color:#92400E','approved'=>'#DBEAFE;color:#1E40AF','rejected'=>'#FDE8E8;color:#9B1C1C','disbursed'=>'#E0E7FF;color:#3730A3','repaying'=>'#D1FAE5;color:#065F46','settled'=>'#E5E7EB;color:#374151']; ?>
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>الموظف</th><th>النوع</th><th>المبلغ</th><th>المتبقي</th><th>الأقساط</th><th>الحالة</th><th>إجراءات</th></tr></thead>
<thead><tr><th>الموظف</th><th>النوع</th><?php if (can('hr.employee.view_salary')): ?><th>المبلغ</th><th>المتبقي</th><?php endif; ?><th>الأقساط</th><th>الحالة</th><th>إجراءات</th></tr></thead>
<tbody>
<?php if (empty($loans)): ?>
<tr><td colspan="7" style="text-align:center;padding:40px;color:#9CA3AF;">لا توجد سلف</td></tr>
<tr><td colspan="<?= can('hr.employee.view_salary') ? '7' : '5' ?>" style="text-align:center;padding:40px;color:#9CA3AF;">لا توجد سلف</td></tr>
<?php else: ?>
<?php $typeLabels = ['salary_advance'=>'سلفة راتب','personal_loan'=>'قرض شخصي','emergency_loan'=>'قرض طوارئ']; ?>
<?php foreach ($loans as $l): ?>
<tr>
<td><a href="/hr/employees/<?= (int) $l['employee_profile_id'] ?>"><?= e(($l['first_name_ar'] ?? '') . ' ' . ($l['last_name_ar'] ?? '')) ?></a></td>
<td><?= e($typeLabels[$l['loan_type']] ?? $l['loan_type']) ?></td>
<?php if (can('hr.employee.view_salary')): ?>
<td style="font-weight:600;"><?= number_format((float) $l['loan_amount'], 2) ?></td>
<td><?= number_format((float) $l['remaining_amount'], 2) ?></td>
<?php endif; ?>
<td><?= (int) $l['paid_installments'] ?>/<?= (int) $l['number_of_installments'] ?></td>
<td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $sc[$l['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($sl[$l['status']] ?? $l['status']) ?></span></td>
<td><a href="/hr/loans/<?= (int) $l['id'] ?>" style="color:#2563EB;"><i data-lucide="eye" style="width:16px;height:16px;"></i></a></td>
......
......@@ -11,9 +11,11 @@
<div style="padding:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;">
<div><span style="color:#6B7280;font-size:13px;">الموظف</span><div style="font-weight:600;"><?= e($loan['first_name_ar'] . ' ' . $loan['last_name_ar']) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">النوع</span><div><?= e($typeLabels[$loan['loan_type']] ?? $loan['loan_type']) ?></div></div>
<?php if (can('hr.employee.view_salary')): ?>
<div><span style="color:#6B7280;font-size:13px;">المبلغ</span><div style="font-weight:700;font-size:18px;"><?= number_format((float) $loan['loan_amount'], 2) ?> ج.م</div></div>
<div><span style="color:#6B7280;font-size:13px;">المتبقي</span><div style="font-weight:700;color:#DC2626;"><?= number_format((float) $loan['remaining_amount'], 2) ?> ج.م</div></div>
<div><span style="color:#6B7280;font-size:13px;">قيمة القسط</span><div><?= number_format((float) $loan['installment_amount'], 2) ?></div></div>
<?php endif; ?>
<div><span style="color:#6B7280;font-size:13px;">الأقساط</span><div><?= (int) $loan['paid_installments'] ?> / <?= (int) $loan['number_of_installments'] ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">تاريخ الطلب</span><div><?= e($loan['request_date']) ?></div></div>
<div><span style="color:#6B7280;font-size:13px;">بداية الخصم</span><div><?= e($loan['start_deduction_date'] ?? '-') ?></div></div>
......@@ -46,7 +48,7 @@
<?php endif; ?>
<div style="display:flex;gap:10px;">
<?php if ($loan['status'] === 'pending'): ?>
<?php if ($loan['status'] === 'pending' && can('hr.loan.approve')): ?>
<form method="POST" action="/hr/loans/<?= (int) $loan['id'] ?>/approve"><?= csrf_field() ?><button type="submit" class="btn btn-primary">اعتماد</button></form>
<form method="POST" action="/hr/loans/<?= (int) $loan['id'] ?>/reject" style="display:flex;gap:8px;">
<?= csrf_field() ?>
......@@ -54,7 +56,7 @@
<button type="submit" class="btn btn-danger">رفض</button>
</form>
<?php endif; ?>
<?php if ($loan['status'] === 'approved'): ?>
<?php if ($loan['status'] === 'approved' && can('hr.loan.manage')): ?>
<form method="POST" action="/hr/loans/<?= (int) $loan['id'] ?>/disburse"><?= csrf_field() ?><button type="submit" class="btn btn-primary">صرف السلفة</button></form>
<?php endif; ?>
</div>
......
......@@ -2,7 +2,9 @@
<?php $__template->section('title'); ?>طلبات العمل الإضافي<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (canAny(['hr.overtime.create', 'hr.overtime.manage'])): ?>
<a href="/hr/overtime/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> طلب عمل إضافي</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -72,7 +74,7 @@ $statusBgs = ['pending' => '#FFF7ED', 'approved' => '#ECFDF5', 'rejected' => '#F
</span>
</td>
<td>
<?php if ($st === 'pending'): ?>
<?php if ($st === 'pending' && can('hr.overtime.approve')): ?>
<div style="display:flex;gap:4px;">
<form method="POST" action="/hr/overtime/<?= (int) $r['id'] ?>/approve" style="display:inline;">
<?= csrf_field() ?>
......
......@@ -16,6 +16,7 @@
<button type="submit" class="btn btn-secondary">بحث</button>
</form>
</div>
<?php if (can('hr.payroll.process')): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">إنشاء فترة جديدة</h3></div>
<form method="POST" action="/hr/payroll/periods" style="display:flex;gap:10px;align-items:end;padding:16px;">
......@@ -28,6 +29,7 @@
<button type="submit" class="btn btn-primary">إنشاء</button>
</form>
</div>
<?php endif; ?>
<div class="card">
<?php $statusLabels = ['open'=>'مفتوح','processing'=>'جاري','calculated'=>'محسوب','approved'=>'معتمد','paid'=>'مدفوع','closed'=>'مغلق']; ?>
<?php $statusColors = ['open'=>'#DBEAFE;color:#1E40AF','processing'=>'#FEF3C7;color:#92400E','calculated'=>'#E0E7FF;color:#3730A3','approved'=>'#DEF7EC;color:#03543F','paid'=>'#D1FAE5;color:#065F46','closed'=>'#E5E7EB;color:#374151']; ?>
......
......@@ -9,26 +9,28 @@
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px;">
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">الحالة</div><div><span style="display:inline-block;padding:4px 12px;border-radius:9999px;font-size:14px;font-weight:600;background:<?= $statusColors[$period->status] ?? '#E5E7EB;color:#374151' ?>;"><?= e($statusLabels[$period->status] ?? $period->status) ?></span></div></div>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">عدد الموظفين</div><div style="font-size:24px;font-weight:700;"><?= (int) ($totals['employee_count'] ?? 0) ?></div></div>
<?php if (can('hr.employee.view_salary')): ?>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">إجمالي المستحقات</div><div style="font-size:18px;font-weight:700;color:#059669;"><?= number_format((float) ($totals['total_gross'] ?? 0), 2) ?></div></div>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">إجمالي الخصومات</div><div style="font-size:18px;font-weight:700;color:#DC2626;"><?= number_format((float) ($totals['total_deductions'] ?? 0), 2) ?></div></div>
<div class="card" style="padding:16px;text-align:center;"><div style="font-size:13px;color:#6B7280;">صافي الرواتب</div><div style="font-size:24px;font-weight:700;color:#2563EB;"><?= number_format((float) ($totals['total_net'] ?? 0), 2) ?></div></div>
<?php endif; ?>
</div>
<div style="display:flex;gap:10px;margin-bottom:20px;flex-wrap:wrap;">
<?php if (in_array($period->status, ['open', 'calculated'])): ?>
<?php if (in_array($period->status, ['open', 'calculated']) && can('hr.payroll.process')): ?>
<form method="POST" action="/hr/payroll/periods/<?= (int) $period->id ?>/calculate"><?= csrf_field() ?><button type="submit" class="btn btn-primary" onclick="this.disabled=true;this.form.submit();">حساب الرواتب</button></form>
<?php endif; ?>
<?php if ($period->status === 'calculated'): ?>
<?php if ($period->status === 'calculated' && can('hr.payroll.approve')): ?>
<form method="POST" action="/hr/payroll/periods/<?= (int) $period->id ?>/approve"><?= csrf_field() ?><button type="submit" class="btn btn-primary">اعتماد</button></form>
<?php endif; ?>
<?php if ($period->status === 'approved'): ?>
<?php if ($period->status === 'approved' && can('hr.payroll.process')): ?>
<form method="POST" action="/hr/payroll/periods/<?= (int) $period->id ?>/pay" style="display:flex;gap:8px;align-items:end;">
<?= csrf_field() ?>
<input type="date" name="payment_date" value="<?= date('Y-m-d') ?>" class="form-control" style="max-width:160px;">
<button type="submit" class="btn btn-primary">صرف</button>
</form>
<?php endif; ?>
<?php if ($period->status === 'paid'): ?>
<?php if ($period->status === 'paid' && can('hr.payroll.process')): ?>
<form method="POST" action="/hr/payroll/periods/<?= (int) $period->id ?>/close" onsubmit="return confirm('إغلاق الفترة نهائياً؟');"><?= csrf_field() ?><button type="submit" class="btn btn-secondary">إغلاق الفترة</button></form>
<?php endif; ?>
</div>
......@@ -37,18 +39,20 @@
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">تفاصيل الرواتب</h3></div>
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>الموظف</th><th>الأساسي</th><th>إجمالي المستحقات</th><th>الخصومات</th><th>الصافي</th><th>إجراءات</th></tr></thead>
<thead><tr><th>الموظف</th><?php if (can('hr.employee.view_salary')): ?><th>الأساسي</th><th>إجمالي المستحقات</th><th>الخصومات</th><th>الصافي</th><?php endif; ?><th>إجراءات</th></tr></thead>
<tbody>
<?php if (empty($runs)): ?>
<tr><td colspan="6" style="text-align:center;padding:40px;color:#9CA3AF;">لم يتم حساب الرواتب بعد</td></tr>
<tr><td colspan="<?= can('hr.employee.view_salary') ? '6' : '2' ?>" style="text-align:center;padding:40px;color:#9CA3AF;">لم يتم حساب الرواتب بعد</td></tr>
<?php else: ?>
<?php foreach ($runs as $r): ?>
<tr>
<td><a href="/hr/employees/<?= (int) $r['employee_profile_id'] ?>"><?= e(($r['first_name_ar'] ?? '') . ' ' . ($r['last_name_ar'] ?? '')) ?></a></td>
<?php if (can('hr.employee.view_salary')): ?>
<td><?= number_format((float) $r['basic_salary'], 2) ?></td>
<td style="color:#059669;font-weight:600;"><?= number_format((float) $r['gross_earnings'], 2) ?></td>
<td style="color:#DC2626;"><?= number_format((float) $r['total_deductions'], 2) ?></td>
<td style="font-weight:700;"><?= number_format((float) $r['net_salary'], 2) ?></td>
<?php endif; ?>
<td style="white-space:nowrap;">
<a href="/hr/payroll/runs/<?= (int) $r['id'] ?>" style="color:#2563EB;margin-inline-end:8px;" title="التفاصيل"><i data-lucide="eye" style="width:16px;height:16px;"></i></a>
<a href="/hr/payroll/runs/<?= (int) $r['id'] ?>/slip" style="color:#059669;" title="قسيمة الراتب"><i data-lucide="file-text" style="width:16px;height:16px;"></i></a>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تفاصيل راتب: <?= e($profile->first_name_ar . ' ' . $profile->last_name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.employee.view_salary')): ?>
<a href="/hr/payroll/runs/<?= (int) $run['id'] ?>/slip" class="btn btn-primary">قسيمة الراتب</a>
<?php endif; ?>
<a href="/hr/payroll/periods/<?= (int) $run['period_id'] ?>" class="btn btn-secondary">رجوع للفترة</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
......@@ -5,6 +5,7 @@
<?php $csl = ['draft'=>'مسودة','active'=>'نشطة','review'=>'تحت المراجعة','closed'=>'مغلقة']; ?>
<?php $csc = ['draft'=>'#FEF3C7;color:#92400E','active'=>'#D1FAE5;color:#065F46','review'=>'#DBEAFE;color:#1E40AF','closed'=>'#E5E7EB;color:#374151']; ?>
<?php if (can('hr.performance.manage')): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:16px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;font-size:16px;">إنشاء دورة تقييم جديدة</h3></div>
<form method="POST" action="/hr/performance/cycles" style="padding:16px;">
......@@ -25,6 +26,7 @@
<span style="font-size:12px;color:#6B7280;margin-right:8px;">* سيتم إنشاء تقييم تلقائي لكل موظف نشط</span>
</form>
</div>
<?php endif; ?>
<div class="card">
<div class="table-responsive">
......
......@@ -18,7 +18,9 @@
<?php else: ?>
<div class="card" style="margin-bottom:20px;padding:40px;text-align:center;">
<div style="color:#9CA3AF;margin-bottom:12px;">لا توجد دورة تقييم نشطة حالياً</div>
<?php if (can('hr.performance.manage')): ?>
<a href="/hr/performance/cycles" class="btn btn-primary">إنشاء دورة جديدة</a>
<?php endif; ?>
</div>
<?php endif; ?>
......
......@@ -47,7 +47,7 @@ $pct = $total > 0 ? round($completed / $total * 100) : 0;
<td><span style="display:inline-block;padding:2px 8px;border-radius:9999px;font-size:12px;background:<?= $rsc[$r['status']] ?? '#E5E7EB;color:#374151' ?>;"><?= e($rsl[$r['status']] ?? $r['status']) ?></span></td>
<td style="display:flex;gap:6px;">
<a href="/hr/performance/reviews/<?= (int) $r['id'] ?>" style="color:#2563EB;" title="عرض"><i data-lucide="eye" style="width:16px;height:16px;"></i></a>
<?php if ($r['status'] !== 'acknowledged'): ?><a href="/hr/performance/reviews/<?= (int) $r['id'] ?>/edit" style="color:#D97706;" title="تقييم"><i data-lucide="edit" style="width:16px;height:16px;"></i></a><?php endif; ?>
<?php if ($r['status'] !== 'acknowledged' && can('hr.performance.manage')): ?><a href="/hr/performance/reviews/<?= (int) $r['id'] ?>/edit" style="color:#D97706;" title="تقييم"><i data-lucide="edit" style="width:16px;height:16px;"></i></a><?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -78,7 +78,7 @@ $scores = json_decode($review->scores_json ?? '{}', true);
<?php endif; ?>
<?php endif; ?>
<?php if ($review->status !== 'acknowledged'): ?>
<?php if ($review->status !== 'acknowledged' && can('hr.performance.manage')): ?>
<div style="display:flex;gap:10px;">
<a href="/hr/performance/reviews/<?= (int) $review->id ?>/edit" class="btn btn-primary"><?= $review->status === 'pending' ? 'إجراء التقييم' : 'تعديل التقييم' ?></a>
</div>
......
......@@ -2,6 +2,7 @@
<?php $__template->section('title'); ?>تقارير الموارد البشرية<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;">
<?php if (can('hr.report.view')): ?>
<a href="/hr/reports/headcount" class="card" style="padding:20px;text-decoration:none;color:inherit;transition:box-shadow .2s;display:block;">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px;"><i data-lucide="users" style="width:24px;height:24px;color:#2563EB;"></i><h3 style="margin:0;font-size:16px;">تقرير القوى العاملة</h3></div>
<p style="margin:0;font-size:13px;color:#6B7280;">توزيع الموظفين حسب الأقسام والنوع ونوع التوظيف</p>
......@@ -34,6 +35,7 @@
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px;"><i data-lucide="file-warning" style="width:24px;height:24px;color:#BE123C;"></i><h3 style="margin:0;font-size:16px;">العقود المنتهية</h3></div>
<p style="margin:0;font-size:13px;color:#6B7280;">العقود التي ستنتهي قريباً أو انتهت بالفعل</p>
</a>
<?php endif; ?>
</div>
<script>if(typeof lucide!=='undefined')lucide.createIcons();</script>
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>هياكل الرواتب<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.employee.manage_salary')): ?>
<a href="/hr/salary-structures/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إضافة هيكل</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card">
......@@ -27,7 +29,9 @@
</td>
<td>
<a href="/hr/salary-structures/<?= (int) $s['id'] ?>" style="color:#2563EB;margin-inline-end:8px;"><i data-lucide="eye" style="width:16px;height:16px;"></i></a>
<?php if (can('hr.employee.manage_salary')): ?>
<a href="/hr/salary-structures/<?= (int) $s['id'] ?>/edit" style="color:#059669;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>هيكل الرواتب: <?= e($structure->name_ar) ?><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.employee.manage_salary')): ?>
<a href="/hr/salary-structures/<?= (int) $structure->id ?>/edit" class="btn btn-primary">تعديل</a>
<?php endif; ?>
<a href="/hr/salary-structures" class="btn btn-secondary">رجوع</a>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>جداول العمل<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?><a href="/hr/schedules/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إضافة جدول</a><?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.attendance.manage')): ?>
<a href="/hr/schedules/create" class="btn btn-primary" style="display:inline-flex;align-items:center;gap:6px;"><i data-lucide="plus" style="width:16px;height:16px;"></i> إضافة جدول</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<div class="card">
<div class="table-responsive">
......@@ -19,7 +23,7 @@
<td><?= e($s['hours_per_day']) ?></td>
<td><?= e($s['hours_per_week']) ?></td>
<td><?= (int) $s['employee_count'] ?></td>
<td><a href="/hr/schedules/<?= (int) $s['id'] ?>/edit" style="color:#2563EB;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a></td>
<td><?php if (can('hr.attendance.manage')): ?><a href="/hr/schedules/<?= (int) $s['id'] ?>/edit" style="color:#2563EB;"><i data-lucide="edit" style="width:16px;height:16px;"></i></a><?php endif; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
......
......@@ -2,7 +2,9 @@
<?php $__template->section('title'); ?>الورديات<?php $__template->endSection(); ?>
<?php $__template->section('page_actions'); ?>
<?php if (can('hr.attendance.manage')): ?>
<a href="/hr/shifts/create" class="btn btn-primary"><i data-lucide="plus" style="width:16px;height:16px;vertical-align:middle;margin-left:4px;"></i> وردية جديدة</a>
<?php endif; ?>
<?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
......@@ -44,11 +46,13 @@
<?php endif; ?>
</td>
<td>
<?php if (can('hr.attendance.manage')): ?>
<div style="display:flex;gap:4px;">
<a href="/hr/shifts/<?= (int) $shift['id'] ?>/edit" class="btn btn-sm btn-outline" style="font-size:12px;padding:4px 10px;">
<i data-lucide="edit" style="width:13px;height:13px;vertical-align:middle;"></i> تعديل
</a>
</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
......
......@@ -7,6 +7,7 @@
<a href="/installments/<?= (int) $existingActive['id'] ?>" style="color:#0D7377;margin-right:10px;">عرض الخطة</a>
</div>
<?php endif; ?>
<?php if (can('installment.create_plan')): ?>
<form method="POST" action="/installments/store/<?= (int) $member['id'] ?>">
<?= csrf_field() ?>
<div class="card" style="padding:20px;margin-bottom:20px;">
......@@ -25,4 +26,5 @@
<button type="submit" class="btn btn-primary">إنشاء خطة التقسيط</button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">إلغاء</a>
</form>
<?php endif; ?>
<?php $__template->endSection(); ?>
\ No newline at end of file
......@@ -66,7 +66,7 @@
<?php endif; ?>
</td>
<td>
<?php if ($s['status'] === 'pending'): ?>
<?php if ($s['status'] === 'pending' && can('installment.record_payment')): ?>
<form method="POST" action="/installments/<?= (int) $plan['id'] ?>/pay/<?= (int) $s['id'] ?>" style="display:flex;gap:5px;">
<?= csrf_field() ?>
<input type="hidden" name="amount" value="<?= e($s['amount']) ?>">
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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