stock module
This commit is contained in:
parent
24ab40453b
commit
d38b70650f
@ -460,3 +460,63 @@ body {
|
||||
}
|
||||
.table-compact tr td:first-child { border-radius: 4px 0 0 4px; }
|
||||
.table-compact tr td:last-child { border-radius: 0 4px 4px 0; }
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
NEW SIDEBAR STYLES (Collapsible Groups)
|
||||
--------------------------------------------------------- */
|
||||
.sidebar-group-btn {
|
||||
width: 100%;
|
||||
text-align: right; /* RTL */
|
||||
padding: 12px 20px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.7);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: all 0.2s;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.sidebar-group-btn:hover {
|
||||
color: #fff;
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
}
|
||||
.sidebar-group-btn[aria-expanded="true"] {
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
color: #fff;
|
||||
}
|
||||
.sidebar-group-btn .group-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.sidebar-group-btn .arrow-icon {
|
||||
font-size: 0.8em;
|
||||
transition: transform 0.3s;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.sidebar-group-btn[aria-expanded="true"] .arrow-icon {
|
||||
transform: rotate(-180deg); /* Adjust for RTL if needed, usually just flips vertically */
|
||||
}
|
||||
|
||||
/* Colors for groups (Text & Icon) */
|
||||
.group-mail { color: #ffca28 !important; } /* Amber */
|
||||
.group-acct { color: #42a5f5 !important; } /* Blue */
|
||||
.group-hr { color: #66bb6a !important; } /* Green */
|
||||
.group-admin { color: #ef5350 !important; } /* Red */
|
||||
.group-reports { color: #ab47bc !important; }/* Purple */
|
||||
|
||||
/* Submenu indentation */
|
||||
.sidebar .collapse .nav-link {
|
||||
padding-right: 45px; /* RTL indent */
|
||||
padding-left: 20px;
|
||||
font-size: 0.9em;
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
.sidebar .collapse .nav-link:hover,
|
||||
.sidebar .collapse .nav-link.active {
|
||||
color: #fff;
|
||||
}
|
||||
.group-stock { color: #fd7e14 !important; }
|
||||
|
||||
134
db/migrations/020_add_hr_module.sql
Normal file
134
db/migrations/020_add_hr_module.sql
Normal file
@ -0,0 +1,134 @@
|
||||
-- Migration: Add HR Module
|
||||
-- 1. Departments
|
||||
CREATE TABLE IF NOT EXISTS hr_departments (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 2. Employees (Linked to users optionally)
|
||||
CREATE TABLE IF NOT EXISTS hr_employees (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT UNIQUE DEFAULT NULL, -- Link to system login if applicable
|
||||
department_id INT,
|
||||
first_name VARCHAR(50) NOT NULL,
|
||||
last_name VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
phone VARCHAR(20),
|
||||
gender ENUM('male', 'female') DEFAULT 'male',
|
||||
birth_date DATE,
|
||||
join_date DATE NOT NULL,
|
||||
job_title VARCHAR(100),
|
||||
basic_salary DECIMAL(10, 2) DEFAULT 0.00,
|
||||
status ENUM('active', 'terminated', 'resigned', 'on_leave') DEFAULT 'active',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (department_id) REFERENCES hr_departments(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 3. Attendance
|
||||
CREATE TABLE IF NOT EXISTS hr_attendance (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
employee_id INT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
check_in TIME,
|
||||
check_out TIME,
|
||||
status ENUM('present', 'absent', 'late', 'excused', 'holiday') DEFAULT 'absent',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY emp_date (employee_id, date),
|
||||
FOREIGN KEY (employee_id) REFERENCES hr_employees(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 4. Leaves
|
||||
CREATE TABLE IF NOT EXISTS hr_leaves (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
employee_id INT NOT NULL,
|
||||
leave_type ENUM('annual', 'sick', 'unpaid', 'maternity', 'emergency', 'other') NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
days_count INT DEFAULT 1,
|
||||
reason TEXT,
|
||||
status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
|
||||
approved_by INT DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (employee_id) REFERENCES hr_employees(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 5. Holidays
|
||||
CREATE TABLE IF NOT EXISTS hr_holidays (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
date_from DATE NOT NULL,
|
||||
date_to DATE NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- 6. Payroll
|
||||
CREATE TABLE IF NOT EXISTS hr_payroll (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
employee_id INT NOT NULL,
|
||||
month INT NOT NULL,
|
||||
year INT NOT NULL,
|
||||
basic_salary DECIMAL(10, 2) NOT NULL,
|
||||
bonuses DECIMAL(10, 2) DEFAULT 0.00,
|
||||
deductions DECIMAL(10, 2) DEFAULT 0.00,
|
||||
net_salary DECIMAL(10, 2) NOT NULL,
|
||||
status ENUM('pending', 'paid') DEFAULT 'pending',
|
||||
payment_date DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY emp_period (employee_id, month, year),
|
||||
FOREIGN KEY (employee_id) REFERENCES hr_employees(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Permissions for HR module (Assign to Admin by default)
|
||||
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||
SELECT id, 'hr_dashboard',
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0)
|
||||
FROM users;
|
||||
|
||||
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||
SELECT id, 'hr_employees',
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0)
|
||||
FROM users;
|
||||
|
||||
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||
SELECT id, 'hr_attendance',
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0)
|
||||
FROM users;
|
||||
|
||||
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||
SELECT id, 'hr_leaves',
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0)
|
||||
FROM users;
|
||||
|
||||
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||
SELECT id, 'hr_payroll',
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0)
|
||||
FROM users;
|
||||
|
||||
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||
SELECT id, 'hr_reports',
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0),
|
||||
IF(role = 'admin', 1, 0)
|
||||
FROM users;
|
||||
76
db/migrations/021_add_stock_module.sql
Normal file
76
db/migrations/021_add_stock_module.sql
Normal file
@ -0,0 +1,76 @@
|
||||
-- 021_add_stock_module.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_stores (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
location VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_categories (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_items (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
category_id INT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
sku VARCHAR(100),
|
||||
description TEXT,
|
||||
min_quantity INT DEFAULT 0,
|
||||
unit VARCHAR(50) DEFAULT 'piece',
|
||||
image_path VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (category_id) REFERENCES stock_categories(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_quantities (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
store_id INT NOT NULL,
|
||||
item_id INT NOT NULL,
|
||||
quantity DECIMAL(10,2) DEFAULT 0,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY unique_item_store (store_id, item_id),
|
||||
FOREIGN KEY (store_id) REFERENCES stock_stores(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (item_id) REFERENCES stock_items(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_transactions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
transaction_type ENUM('in', 'out', 'transfer', 'damage', 'lend', 'return') NOT NULL,
|
||||
store_id INT NOT NULL,
|
||||
item_id INT NOT NULL,
|
||||
quantity DECIMAL(10,2) NOT NULL,
|
||||
user_id INT,
|
||||
reference VARCHAR(255),
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (store_id) REFERENCES stock_stores(id),
|
||||
FOREIGN KEY (item_id) REFERENCES stock_items(id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS stock_lending (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
transaction_id INT NOT NULL,
|
||||
borrower_name VARCHAR(255) NOT NULL,
|
||||
borrower_phone VARCHAR(50),
|
||||
expected_return_date DATE,
|
||||
return_transaction_id INT,
|
||||
status ENUM('active', 'returned', 'overdue') DEFAULT 'active',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (transaction_id) REFERENCES stock_transactions(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Add default store if none exists
|
||||
INSERT INTO stock_stores (name, location)
|
||||
SELECT 'المستودع الرئيسي', 'المقر الرئيسي'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM stock_stores);
|
||||
|
||||
-- Add default category
|
||||
INSERT INTO stock_categories (name)
|
||||
SELECT 'عام'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM stock_categories);
|
||||
227
hr_attendance.php
Normal file
227
hr_attendance.php
Normal file
@ -0,0 +1,227 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
if (!canView('hr_attendance')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$date = $_GET['date'] ?? date('Y-m-d');
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
// Handle Attendance Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_attendance'])) {
|
||||
if (!canAdd('hr_attendance') && !canEdit('hr_attendance')) {
|
||||
$error = "لا تملك صلاحية التعديل.";
|
||||
} else {
|
||||
$emp_id = $_POST['employee_id'];
|
||||
$att_date = $_POST['date'];
|
||||
$status = $_POST['status'];
|
||||
$check_in = !empty($_POST['check_in']) ? $_POST['check_in'] : null;
|
||||
$check_out = !empty($_POST['check_out']) ? $_POST['check_out'] : null;
|
||||
$notes = $_POST['notes'];
|
||||
|
||||
try {
|
||||
// Check if exists
|
||||
$stmt = db()->prepare("SELECT id FROM hr_attendance WHERE employee_id = ? AND date = ?");
|
||||
$stmt->execute([$emp_id, $att_date]);
|
||||
$exists = $stmt->fetch();
|
||||
|
||||
if ($exists) {
|
||||
$stmt = db()->prepare("UPDATE hr_attendance SET status = ?, check_in = ?, check_out = ?, notes = ? WHERE id = ?");
|
||||
$stmt->execute([$status, $check_in, $check_out, $notes, $exists['id']]);
|
||||
$success = "تم تحديث الحضور بنجاح.";
|
||||
} else {
|
||||
$stmt = db()->prepare("INSERT INTO hr_attendance (employee_id, date, status, check_in, check_out, notes) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$emp_id, $att_date, $status, $check_in, $check_out, $notes]);
|
||||
$success = "تم تسجيل الحضور بنجاح.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error = "خطأ: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Employees and their attendance for the selected date
|
||||
$sql = "SELECT e.id, e.first_name, e.last_name, e.job_title,
|
||||
a.id as att_id, a.status, a.check_in, a.check_out, a.notes
|
||||
FROM hr_employees e
|
||||
LEFT JOIN hr_attendance a ON e.id = a.employee_id AND a.date = ?
|
||||
WHERE e.status = 'active'
|
||||
ORDER BY e.first_name";
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute([$date]);
|
||||
$records = $stmt->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">سجل الحضور والانصراف</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<form class="d-flex gap-2 align-items-center" method="get">
|
||||
<label class="col-form-label">التاريخ:</label>
|
||||
<input type="date" name="date" class="form-control" value="<?= $date ?>" onchange="this.form.submit()">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>الموظف</th>
|
||||
<th>الوظيفة</th>
|
||||
<th>الحالة</th>
|
||||
<th>وقت الحضور</th>
|
||||
<th>وقت الانصراف</th>
|
||||
<th>ملاحظات</th>
|
||||
<th>إجراء</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($records as $row): ?>
|
||||
<tr class="<?= !$row['att_id'] ? 'table-light text-muted' : '' ?>">
|
||||
<td class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></td>
|
||||
<td class="small"><?= htmlspecialchars($row['job_title']) ?></td>
|
||||
<td>
|
||||
<?php if ($row['att_id']): ?>
|
||||
<?php
|
||||
$badge = match($row['status']) {
|
||||
'present' => 'success',
|
||||
'absent' => 'danger',
|
||||
'late' => 'warning',
|
||||
'excused' => 'info',
|
||||
'holiday' => 'primary',
|
||||
default => 'secondary'
|
||||
};
|
||||
$status_text = match($row['status']) {
|
||||
'present' => 'حاضر',
|
||||
'absent' => 'غائب',
|
||||
'late' => 'تأخير',
|
||||
'excused' => 'مأذون',
|
||||
'holiday' => 'عطلة',
|
||||
default => $row['status']
|
||||
};
|
||||
?>
|
||||
<span class="badge bg-<?= $badge ?>"><?= $status_text ?></span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-light text-dark border">غير مسجل</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= $row['check_in'] ? date('h:i A', strtotime($row['check_in'])) : '-' ?></td>
|
||||
<td><?= $row['check_out'] ? date('h:i A', strtotime($row['check_out'])) : '-' ?></td>
|
||||
<td class="text-truncate" style="max-width: 150px;"><?= htmlspecialchars($row['notes'] ?? '') ?></td>
|
||||
<td>
|
||||
<?php if (canEdit('hr_attendance')): ?>
|
||||
<?php if ($row['att_id']): ?>
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
title="تعديل"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#attModal"
|
||||
data-id="<?= $row['id'] ?>"
|
||||
data-name="<?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?>"
|
||||
data-status="<?= $row['status'] ?? 'present' ?>"
|
||||
data-in="<?= $row['check_in'] ?? '' ?>"
|
||||
data-out="<?= $row['check_out'] ?? '' ?>"
|
||||
data-notes="<?= htmlspecialchars($row['notes'] ?? '') ?>">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-sm btn-success"
|
||||
title="تسجيل حضور"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#attModal"
|
||||
data-id="<?= $row['id'] ?>"
|
||||
data-name="<?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?>"
|
||||
data-status="present"
|
||||
data-in="<?= date('09:00') ?>"
|
||||
data-out=""
|
||||
data-notes="">
|
||||
<i class="fas fa-check"></i> تسجيل
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attendance Modal -->
|
||||
<div class="modal fade" id="attModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">تسجيل/تعديل الحضور: <span id="modalEmpName" class="text-primary"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="employee_id" id="modalEmpId">
|
||||
<input type="hidden" name="date" value="<?= $date ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">الحالة</label>
|
||||
<select name="status" id="modalStatus" class="form-select" required>
|
||||
<option value="present">حاضر</option>
|
||||
<option value="late">تأخير</option>
|
||||
<option value="excused">مأذون/إذن</option>
|
||||
<option value="absent">غائب</option>
|
||||
<option value="holiday">عطلة</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col">
|
||||
<label class="form-label">وقت الحضور</label>
|
||||
<input type="time" name="check_in" id="modalIn" class="form-control">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">وقت الانصراف</label>
|
||||
<input type="time" name="check_out" id="modalOut" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">ملاحظات</label>
|
||||
<textarea name="notes" id="modalNotes" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" name="save_attendance" class="btn btn-primary">حفظ</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const attModal = document.getElementById('attModal');
|
||||
attModal.addEventListener('show.bs.modal', event => {
|
||||
const button = event.relatedTarget;
|
||||
|
||||
document.getElementById('modalEmpId').value = button.getAttribute('data-id');
|
||||
document.getElementById('modalEmpName').textContent = button.getAttribute('data-name');
|
||||
document.getElementById('modalStatus').value = button.getAttribute('data-status');
|
||||
document.getElementById('modalIn').value = button.getAttribute('data-in');
|
||||
document.getElementById('modalOut').value = button.getAttribute('data-out');
|
||||
document.getElementById('modalNotes').value = button.getAttribute('data-notes');
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
153
hr_dashboard.php
Normal file
153
hr_dashboard.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
if (!canView('hr_dashboard')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch Stats
|
||||
$total_employees = db()->query("SELECT COUNT(*) FROM hr_employees WHERE status = 'active'")->fetchColumn();
|
||||
$employees_present = db()->query("SELECT COUNT(*) FROM hr_attendance WHERE date = CURDATE() AND status = 'present'")->fetchColumn();
|
||||
$on_leave = db()->query("SELECT COUNT(*) FROM hr_leaves WHERE CURDATE() BETWEEN start_date AND end_date AND status = 'approved'")->fetchColumn();
|
||||
$pending_leaves = db()->query("SELECT COUNT(*) FROM hr_leaves WHERE status = 'pending'")->fetchColumn();
|
||||
|
||||
// Recent Employees
|
||||
$recent_employees = db()->query("SELECT * FROM hr_employees ORDER BY join_date DESC LIMIT 5")->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">لوحة الموارد البشرية</h1>
|
||||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-primary shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title mb-0">إجمالي الموظفين</h6>
|
||||
<h2 class="mt-2 mb-0"><?= number_format($total_employees) ?></h2>
|
||||
</div>
|
||||
<i class="fas fa-users fa-2x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title mb-0">حضور اليوم</h6>
|
||||
<h2 class="mt-2 mb-0"><?= number_format($employees_present) ?></h2>
|
||||
</div>
|
||||
<i class="fas fa-user-check fa-2x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-warning shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title mb-0">في إجازة</h6>
|
||||
<h2 class="mt-2 mb-0"><?= number_format($on_leave) ?></h2>
|
||||
</div>
|
||||
<i class="fas fa-plane-departure fa-2x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-danger shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title mb-0">طلبات إجازة معلقة</h6>
|
||||
<h2 class="mt-2 mb-0"><?= number_format($pending_leaves) ?></h2>
|
||||
</div>
|
||||
<i class="fas fa-clipboard-list fa-2x opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Recent Employees -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0"><i class="fas fa-user-plus me-2 text-primary"></i> أحدث الموظفين</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>الاسم</th>
|
||||
<th>الوظيفة</th>
|
||||
<th>تاريخ التعيين</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($recent_employees)): ?>
|
||||
<tr><td colspan="3" class="text-center py-3 text-muted">لا يوجد موظفين مسجلين حالياً</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($recent_employees as $emp): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($emp['first_name'] . ' ' . $emp['last_name']) ?></td>
|
||||
<td><?= htmlspecialchars($emp['job_title']) ?></td>
|
||||
<td><?= date('Y-m-d', strtotime($emp['join_date'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white text-center">
|
||||
<a href="hr_employees.php" class="text-decoration-none small fw-bold">عرض كل الموظفين <i class="fas fa-arrow-left ms-1"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0"><i class="fas fa-bolt me-2 text-warning"></i> إجراءات سريعة</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-3">
|
||||
<?php if (canAdd('hr_employees')): ?>
|
||||
<a href="hr_employees.php?action=add" class="btn btn-outline-primary text-start">
|
||||
<i class="fas fa-plus-circle me-2"></i> إضافة موظف جديد
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (canAdd('hr_attendance')): ?>
|
||||
<a href="hr_attendance.php?action=mark" class="btn btn-outline-success text-start">
|
||||
<i class="fas fa-clock me-2"></i> تسجيل حضور وانصراف
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (canAdd('hr_leaves')): ?>
|
||||
<a href="hr_leaves.php?action=request" class="btn btn-outline-warning text-start">
|
||||
<i class="fas fa-calendar-plus me-2"></i> تقديم طلب إجازة
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (canAdd('hr_payroll')): ?>
|
||||
<a href="hr_payroll.php?action=generate" class="btn btn-outline-info text-start">
|
||||
<i class="fas fa-file-invoice-dollar me-2"></i> إصدار مسير رواتب
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
336
hr_employees.php
Normal file
336
hr_employees.php
Normal file
@ -0,0 +1,336 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
// Check Permission
|
||||
if (!canView('hr_employees')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
// Handle Form Submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['save_employee'])) {
|
||||
if (!canAdd('hr_employees') && !canEdit('hr_employees')) {
|
||||
$error = "لا تملك صلاحية التعديل.";
|
||||
} else {
|
||||
$id = !empty($_POST['id']) ? $_POST['id'] : null;
|
||||
$first_name = trim($_POST['first_name']);
|
||||
$last_name = trim($_POST['last_name']);
|
||||
$email = trim($_POST['email']);
|
||||
$phone = trim($_POST['phone']);
|
||||
$department_id = !empty($_POST['department_id']) ? $_POST['department_id'] : null;
|
||||
$job_title = trim($_POST['job_title']);
|
||||
$basic_salary = floatval($_POST['basic_salary']);
|
||||
$join_date = $_POST['join_date'];
|
||||
$status = $_POST['status'];
|
||||
$gender = $_POST['gender'];
|
||||
$birth_date = !empty($_POST['birth_date']) ? $_POST['birth_date'] : null;
|
||||
|
||||
if (empty($first_name) || empty($last_name) || empty($join_date)) {
|
||||
$error = "يرجى تعبئة الحقول الإلزامية.";
|
||||
} else {
|
||||
try {
|
||||
if ($id) {
|
||||
// Update
|
||||
$stmt = db()->prepare("UPDATE hr_employees SET first_name=?, last_name=?, email=?, phone=?, department_id=?, job_title=?, basic_salary=?, join_date=?, status=?, gender=?, birth_date=? WHERE id=?");
|
||||
$stmt->execute([$first_name, $last_name, $email, $phone, $department_id, $job_title, $basic_salary, $join_date, $status, $gender, $birth_date, $id]);
|
||||
$success = "تم تحديث بيانات الموظف بنجاح.";
|
||||
} else {
|
||||
// Insert
|
||||
$stmt = db()->prepare("INSERT INTO hr_employees (first_name, last_name, email, phone, department_id, job_title, basic_salary, join_date, status, gender, birth_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$first_name, $last_name, $email, $phone, $department_id, $job_title, $basic_salary, $join_date, $status, $gender, $birth_date]);
|
||||
$success = "تم إضافة الموظف بنجاح.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error = "خطأ في قاعدة البيانات: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['delete_employee'])) {
|
||||
if (!canDelete('hr_employees')) {
|
||||
$error = "لا تملك صلاحية الحذف.";
|
||||
} else {
|
||||
$id = $_POST['id'];
|
||||
try {
|
||||
$stmt = db()->prepare("DELETE FROM hr_employees WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$success = "تم حذف الموظف بنجاح.";
|
||||
} catch (PDOException $e) {
|
||||
$error = "لا يمكن حذف الموظف لوجود سجلات مرتبطة به.";
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['save_department'])) {
|
||||
$dept_name = trim($_POST['name']);
|
||||
if (!empty($dept_name)) {
|
||||
$stmt = db()->prepare("INSERT INTO hr_departments (name) VALUES (?)");
|
||||
$stmt->execute([$dept_name]);
|
||||
$success = "تم إضافة القسم بنجاح.";
|
||||
}
|
||||
} elseif (isset($_POST['delete_department'])) {
|
||||
$dept_id = $_POST['id'];
|
||||
try {
|
||||
$stmt = db()->prepare("DELETE FROM hr_departments WHERE id = ?");
|
||||
$stmt->execute([$dept_id]);
|
||||
$success = "تم حذف القسم.";
|
||||
} catch (PDOException $e) {
|
||||
$error = "لا يمكن حذف القسم لأنه مرتبط بموظفين.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Departments for Dropdown
|
||||
$departments = db()->query("SELECT * FROM hr_departments ORDER BY name")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">إدارة الموظفين</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<?php if (canAdd('hr_employees')): ?>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary me-2" data-bs-toggle="modal" data-bs-target="#deptModal">
|
||||
<i class="fas fa-building"></i> الأقسام
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#employeeModal" onclick="resetEmployeeForm()">
|
||||
<i class="fas fa-plus"></i> إضافة موظف
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Departments Modal -->
|
||||
<div class="modal fade" id="deptModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">إدارة الأقسام</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="post" class="mb-3 d-flex gap-2">
|
||||
<input type="text" name="name" class="form-control" placeholder="اسم القسم الجديد" required>
|
||||
<button type="submit" name="save_department" class="btn btn-primary">إضافة</button>
|
||||
</form>
|
||||
<ul class="list-group">
|
||||
<?php foreach ($departments as $dept): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?= htmlspecialchars($dept['name']) ?>
|
||||
<form method="post" onsubmit="return confirm('هل أنت متأكد؟');">
|
||||
<input type="hidden" name="id" value="<?= $dept['id'] ?>">
|
||||
<button type="submit" name="delete_department" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Employee Modal (Add/Edit) -->
|
||||
<div class="modal fade" id="employeeModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="employeeModalLabel">إضافة موظف جديد</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" id="employeeForm">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="id" id="empId">
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">الاسم الأول <span class="text-danger">*</span></label>
|
||||
<input type="text" name="first_name" id="empFirstName" class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">اسم العائلة <span class="text-danger">*</span></label>
|
||||
<input type="text" name="last_name" id="empLastName" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">البريد الإلكتروني</label>
|
||||
<input type="email" name="email" id="empEmail" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">رقم الهاتف</label>
|
||||
<input type="text" name="phone" id="empPhone" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">الجنس</label>
|
||||
<select name="gender" id="empGender" class="form-select">
|
||||
<option value="male">ذكر</option>
|
||||
<option value="female">أنثى</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">تاريخ الميلاد</label>
|
||||
<input type="date" name="birth_date" id="empBirthDate" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">القسم</label>
|
||||
<select name="department_id" id="empDept" class="form-select">
|
||||
<option value="">-- اختر القسم --</option>
|
||||
<?php foreach ($departments as $dept): ?>
|
||||
<option value="<?= $dept['id'] ?>"><?= htmlspecialchars($dept['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">المسمى الوظيفي</label>
|
||||
<input type="text" name="job_title" id="empJobTitle" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">تاريخ التعيين <span class="text-danger">*</span></label>
|
||||
<input type="date" name="join_date" id="empJoinDate" class="form-control" value="<?= date('Y-m-d') ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">الراتب الأساسي</label>
|
||||
<input type="number" step="0.01" name="basic_salary" id="empSalary" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">الحالة</label>
|
||||
<select name="status" id="empStatus" class="form-select">
|
||||
<option value="active">نشط</option>
|
||||
<option value="on_leave">في إجازة</option>
|
||||
<option value="resigned">مستقيل</option>
|
||||
<option value="terminated">منهي خدماته</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" name="save_employee" class="btn btn-primary">حفظ البيانات</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- List View -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>الاسم</th>
|
||||
<th>القسم</th>
|
||||
<th>المسمى الوظيفي</th>
|
||||
<th>تاريخ التعيين</th>
|
||||
<th>الحالة</th>
|
||||
<th>الإجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$sql = "SELECT e.*, d.name as dept_name FROM hr_employees e LEFT JOIN hr_departments d ON e.department_id = d.id ORDER BY e.first_name";
|
||||
$stmt = db()->query($sql);
|
||||
while ($row = $stmt->fetch()):
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 35px; height: 35px; font-size: 0.9rem;">
|
||||
<?= mb_substr($row['first_name'], 0, 1) . mb_substr($row['last_name'], 0, 1) ?>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></div>
|
||||
<div class="small text-muted"><?= htmlspecialchars($row['email']) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge bg-secondary"><?= htmlspecialchars($row['dept_name'] ?? '-') ?></span></td>
|
||||
<td><?= htmlspecialchars($row['job_title']) ?></td>
|
||||
<td><?= $row['join_date'] ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$status_cls = match($row['status']) {
|
||||
'active' => 'success',
|
||||
'terminated' => 'danger',
|
||||
'resigned' => 'warning',
|
||||
'on_leave' => 'info',
|
||||
default => 'secondary'
|
||||
};
|
||||
?>
|
||||
<span class="badge bg-<?= $status_cls ?>"><?= htmlspecialchars($row['status']) ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<?php if (canEdit('hr_employees')): ?>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#employeeModal"
|
||||
data-id="<?= $row['id'] ?>"
|
||||
data-fname="<?= htmlspecialchars($row['first_name']) ?>"
|
||||
data-lname="<?= htmlspecialchars($row['last_name']) ?>"
|
||||
data-email="<?= htmlspecialchars($row['email']) ?>"
|
||||
data-phone="<?= htmlspecialchars($row['phone']) ?>"
|
||||
data-gender="<?= $row['gender'] ?>"
|
||||
data-bdate="<?= $row['birth_date'] ?>"
|
||||
data-dept="<?= $row['department_id'] ?>"
|
||||
data-job="<?= htmlspecialchars($row['job_title']) ?>"
|
||||
data-join="<?= $row['join_date'] ?>"
|
||||
data-salary="<?= $row['basic_salary'] ?>"
|
||||
data-status="<?= $row['status'] ?>"
|
||||
onclick="editEmployee(this)">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if (canDelete('hr_employees')): ?>
|
||||
<form method="post" onsubmit="return confirm('هل أنت متأكد من حذف هذا الموظف؟');" class="d-inline">
|
||||
<input type="hidden" name="id" value="<?= $row['id'] ?>">
|
||||
<button type="submit" name="delete_employee" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endwhile; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function resetEmployeeForm() {
|
||||
document.getElementById('employeeForm').reset();
|
||||
document.getElementById('empId').value = '';
|
||||
document.getElementById('employeeModalLabel').textContent = 'إضافة موظف جديد';
|
||||
}
|
||||
|
||||
function editEmployee(btn) {
|
||||
document.getElementById('employeeModalLabel').textContent = 'تعديل بيانات موظف';
|
||||
document.getElementById('empId').value = btn.dataset.id;
|
||||
document.getElementById('empFirstName').value = btn.dataset.fname;
|
||||
document.getElementById('empLastName').value = btn.dataset.lname;
|
||||
document.getElementById('empEmail').value = btn.dataset.email;
|
||||
document.getElementById('empPhone').value = btn.dataset.phone;
|
||||
document.getElementById('empGender').value = btn.dataset.gender;
|
||||
document.getElementById('empBirthDate').value = btn.dataset.bdate;
|
||||
document.getElementById('empDept').value = btn.dataset.dept;
|
||||
document.getElementById('empJobTitle').value = btn.dataset.job;
|
||||
document.getElementById('empJoinDate').value = btn.dataset.join;
|
||||
document.getElementById('empSalary').value = btn.dataset.salary;
|
||||
document.getElementById('empStatus').value = btn.dataset.status;
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
183
hr_holidays.php
Normal file
183
hr_holidays.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
if (!canView('hr_attendance')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['save_holiday'])) {
|
||||
if (!canAdd('hr_attendance') && !canEdit('hr_attendance')) {
|
||||
$error = "لا تملك صلاحية التعديل.";
|
||||
} else {
|
||||
$id = $_POST['id'] ?? null;
|
||||
$name = trim($_POST['name']);
|
||||
$from = $_POST['date_from'];
|
||||
$to = $_POST['date_to'];
|
||||
|
||||
if (!empty($name) && !empty($from) && !empty($to)) {
|
||||
if ($id) {
|
||||
$stmt = db()->prepare("UPDATE hr_holidays SET name=?, date_from=?, date_to=? WHERE id=?");
|
||||
$stmt->execute([$name, $from, $to, $id]);
|
||||
$success = "تم تحديث العطلة بنجاح.";
|
||||
} else {
|
||||
$stmt = db()->prepare("INSERT INTO hr_holidays (name, date_from, date_to) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$name, $from, $to]);
|
||||
$success = "تم إضافة العطلة بنجاح.";
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['delete_holiday'])) {
|
||||
if (!canDelete('hr_attendance')) {
|
||||
$error = "لا تملك صلاحية الحذف.";
|
||||
} else {
|
||||
$id = $_POST['id'];
|
||||
$stmt = db()->prepare("DELETE FROM hr_holidays WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$success = "تم حذف العطلة.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$holidays = db()->query("SELECT * FROM hr_holidays ORDER BY date_from DESC")->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">العطلات الرسمية</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<?php if (canAdd('hr_attendance')): ?>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#holidayModal" onclick="resetHolidayForm()">
|
||||
<i class="fas fa-plus"></i> إضافة عطلة
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>اسم العطلة</th>
|
||||
<th>من تاريخ</th>
|
||||
<th>إلى تاريخ</th>
|
||||
<th>الحالة</th>
|
||||
<th>إجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($holidays)): ?>
|
||||
<tr><td colspan="5" class="text-center py-4 text-muted">لا توجد عطلات مسجلة.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($holidays as $row):
|
||||
$today = date('Y-m-d');
|
||||
$status_cls = 'secondary';
|
||||
$status_txt = 'منتهية';
|
||||
|
||||
if ($today >= $row['date_from'] && $today <= $row['date_to']) {
|
||||
$status_cls = 'success';
|
||||
$status_txt = 'جارية';
|
||||
} elseif ($today < $row['date_from']) {
|
||||
$status_cls = 'primary';
|
||||
$status_txt = 'قادمة';
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td class="fw-bold"><?= htmlspecialchars($row['name']) ?></td>
|
||||
<td><?= $row['date_from'] ?></td>
|
||||
<td><?= $row['date_to'] ?></td>
|
||||
<td><span class="badge bg-<?= $status_cls ?>"><?= $status_txt ?></span></td>
|
||||
<td>
|
||||
<?php if (canEdit('hr_attendance')): ?>
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#holidayModal"
|
||||
data-id="<?= $row['id'] ?>"
|
||||
data-name="<?= htmlspecialchars($row['name']) ?>"
|
||||
data-from="<?= $row['date_from'] ?>"
|
||||
data-to="<?= $row['date_to'] ?>"
|
||||
onclick="editHoliday(this)">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if (canDelete('hr_attendance')): ?>
|
||||
<form method="post" onsubmit="return confirm('هل أنت متأكد؟');" class="d-inline">
|
||||
<input type="hidden" name="id" value="<?= $row['id'] ?>">
|
||||
<button type="submit" name="delete_holiday" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Holiday Modal -->
|
||||
<div class="modal fade" id="holidayModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="holidayModalTitle">إضافة عطلة جديدة</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" id="holidayForm">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="id" id="holidayId">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">اسم العطلة</label>
|
||||
<input type="text" name="name" id="holidayName" class="form-control" required placeholder="مثال: عيد الفطر">
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<label class="form-label">من تاريخ</label>
|
||||
<input type="date" name="date_from" id="holidayFrom" class="form-control" required>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">إلى تاريخ</label>
|
||||
<input type="date" name="date_to" id="holidayTo" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" name="save_holiday" class="btn btn-primary">حفظ</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function resetHolidayForm() {
|
||||
document.getElementById('holidayForm').reset();
|
||||
document.getElementById('holidayId').value = '';
|
||||
document.getElementById('holidayModalTitle').textContent = 'إضافة عطلة جديدة';
|
||||
}
|
||||
|
||||
function editHoliday(btn) {
|
||||
document.getElementById('holidayModalTitle').textContent = 'تعديل عطلة';
|
||||
document.getElementById('holidayId').value = btn.getAttribute('data-id');
|
||||
document.getElementById('holidayName').value = btn.getAttribute('data-name');
|
||||
document.getElementById('holidayFrom').value = btn.getAttribute('data-from');
|
||||
document.getElementById('holidayTo').value = btn.getAttribute('data-to');
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
275
hr_leaves.php
Normal file
275
hr_leaves.php
Normal file
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
if (!canView('hr_leaves')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$tab = $_GET['tab'] ?? 'pending';
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
// Handle Form Submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['request_leave'])) {
|
||||
if (!canAdd('hr_leaves')) {
|
||||
$error = "لا تملك صلاحية الإضافة.";
|
||||
} else {
|
||||
$id = $_POST['id'] ?? null; // For edit
|
||||
$emp_id = $_POST['employee_id'];
|
||||
$type = $_POST['leave_type'];
|
||||
$start = $_POST['start_date'];
|
||||
$end = $_POST['end_date'];
|
||||
$reason = trim($_POST['reason']);
|
||||
|
||||
$start_dt = new DateTime($start);
|
||||
$end_dt = new DateTime($end);
|
||||
$days = $end_dt->diff($start_dt)->days + 1;
|
||||
|
||||
if ($days <= 0) {
|
||||
$error = "تاريخ النهاية يجب أن يكون بعد تاريخ البداية.";
|
||||
} else {
|
||||
try {
|
||||
if ($id) {
|
||||
// Update existing request
|
||||
$stmt = db()->prepare("UPDATE hr_leaves SET employee_id=?, leave_type=?, start_date=?, end_date=?, days_count=?, reason=? WHERE id=? AND status='pending'");
|
||||
$stmt->execute([$emp_id, $type, $start, $end, $days, $reason, $id]);
|
||||
$success = "تم تحديث طلب الإجازة بنجاح.";
|
||||
} else {
|
||||
// New request
|
||||
$stmt = db()->prepare("INSERT INTO hr_leaves (employee_id, leave_type, start_date, end_date, days_count, reason, status) VALUES (?, ?, ?, ?, ?, ?, 'pending')");
|
||||
$stmt->execute([$emp_id, $type, $start, $end, $days, $reason]);
|
||||
$success = "تم تقديم طلب الإجازة بنجاح.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$error = "خطأ: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['update_status'])) {
|
||||
if (!canEdit('hr_leaves')) {
|
||||
$error = "لا تملك صلاحية الاعتماد.";
|
||||
} else {
|
||||
$id = $_POST['id'];
|
||||
$status = $_POST['status'];
|
||||
$stmt = db()->prepare("UPDATE hr_leaves SET status = ?, approved_by = ? WHERE id = ?");
|
||||
$stmt->execute([$status, $_SESSION['user_id'], $id]);
|
||||
$success = "تم تحديث حالة الطلب.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Employees for Dropdown
|
||||
$employees = db()->query("SELECT id, first_name, last_name FROM hr_employees WHERE status = 'active' ORDER BY first_name")->fetchAll();
|
||||
|
||||
// Fetch Leaves based on Tab
|
||||
$where_clause = $tab === 'pending' ? "WHERE l.status = 'pending'" : "WHERE 1=1";
|
||||
$sql = "SELECT l.*, e.first_name, e.last_name, u.full_name as approver_name
|
||||
FROM hr_leaves l
|
||||
JOIN hr_employees e ON l.employee_id = e.id
|
||||
LEFT JOIN users u ON l.approved_by = u.id
|
||||
$where_clause
|
||||
ORDER BY l.created_at DESC";
|
||||
$requests = db()->query($sql)->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">إدارة الإجازات</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<?php if (canAdd('hr_leaves')): ?>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#leaveModal" onclick="resetLeaveForm()">
|
||||
<i class="fas fa-plus"></i> طلب إجازة جديد
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<ul class="nav nav-tabs mb-4">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $tab === 'pending' ? 'active' : '' ?>" href="?tab=pending">الطلبات المعلقة</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $tab === 'all' ? 'active' : '' ?>" href="?tab=all">سجل الإجازات</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>الموظف</th>
|
||||
<th>نوع الإجازة</th>
|
||||
<th>الفترة</th>
|
||||
<th>المدة</th>
|
||||
<th>السبب</th>
|
||||
<th>الحالة</th>
|
||||
<?php if ($tab === 'all'): ?><th>المعتمد</th><?php endif; ?>
|
||||
<th>إجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($requests)): ?>
|
||||
<tr><td colspan="8" class="text-center py-4 text-muted">لا توجد طلبات.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($requests as $req): ?>
|
||||
<tr>
|
||||
<td class="fw-bold"><?= htmlspecialchars($req['first_name'] . ' ' . $req['last_name']) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$type_map = [
|
||||
'annual' => 'سنوية',
|
||||
'sick' => 'مرضية',
|
||||
'unpaid' => 'بدون راتب',
|
||||
'maternity' => 'أمومة',
|
||||
'emergency' => 'طارئة',
|
||||
'other' => 'أخرى'
|
||||
];
|
||||
echo $type_map[$req['leave_type']] ?? $req['leave_type'];
|
||||
?>
|
||||
</td>
|
||||
<td class="small">
|
||||
من <?= $req['start_date'] ?><br>إلى <?= $req['end_date'] ?>
|
||||
</td>
|
||||
<td><?= $req['days_count'] ?> يوم</td>
|
||||
<td class="text-truncate" style="max-width: 150px;"><?= htmlspecialchars($req['reason']) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$status_cls = match($req['status']) {
|
||||
'approved' => 'success',
|
||||
'rejected' => 'danger',
|
||||
default => 'warning'
|
||||
};
|
||||
$status_txt = match($req['status']) {
|
||||
'approved' => 'مقبولة',
|
||||
'rejected' => 'مرفوضة',
|
||||
default => 'معلقة'
|
||||
};
|
||||
?>
|
||||
<span class="badge bg-<?= $status_cls ?>"><?= $status_txt ?></span>
|
||||
</td>
|
||||
<?php if ($tab === 'all'): ?>
|
||||
<td class="small"><?= htmlspecialchars($req['approver_name'] ?? '-') ?></td>
|
||||
<?php endif; ?>
|
||||
<td>
|
||||
<?php if ($req['status'] === 'pending' && canEdit('hr_leaves')): ?>
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
title="تعديل الطلب"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#leaveModal"
|
||||
data-id="<?= $req['id'] ?>"
|
||||
data-emp="<?= $req['employee_id'] ?>"
|
||||
data-type="<?= $req['leave_type'] ?>"
|
||||
data-start="<?= $req['start_date'] ?>"
|
||||
data-end="<?= $req['end_date'] ?>"
|
||||
data-reason="<?= htmlspecialchars($req['reason']) ?>"
|
||||
onclick="editLeave(this)">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="id" value="<?= $req['id'] ?>">
|
||||
<input type="hidden" name="status" value="approved">
|
||||
<button type="submit" name="update_status" class="btn btn-sm btn-success" title="قبول"><i class="fas fa-check"></i></button>
|
||||
</form>
|
||||
<form method="post" class="d-inline">
|
||||
<input type="hidden" name="id" value="<?= $req['id'] ?>">
|
||||
<input type="hidden" name="status" value="rejected">
|
||||
<button type="submit" name="update_status" class="btn btn-sm btn-danger" title="رفض" onclick="return confirm('هل أنت متأكد من الرفض؟')"><i class="fas fa-times"></i></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave Request Modal -->
|
||||
<div class="modal fade" id="leaveModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="leaveModalTitle">تقديم طلب إجازة</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" id="leaveForm">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="id" id="leaveId">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">الموظف</label>
|
||||
<select name="employee_id" id="leaveEmp" class="form-select" required>
|
||||
<option value="">-- اختر الموظف --</option>
|
||||
<?php foreach ($employees as $emp): ?>
|
||||
<option value="<?= $emp['id'] ?>"><?= htmlspecialchars($emp['first_name'] . ' ' . $emp['last_name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">نوع الإجازة</label>
|
||||
<select name="leave_type" id="leaveType" class="form-select" required>
|
||||
<option value="annual">سنوية</option>
|
||||
<option value="sick">مرضية</option>
|
||||
<option value="emergency">طارئة</option>
|
||||
<option value="unpaid">بدون راتب</option>
|
||||
<option value="maternity">أمومة</option>
|
||||
<option value="other">أخرى</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col">
|
||||
<label class="form-label">من تاريخ</label>
|
||||
<input type="date" name="start_date" id="leaveStart" class="form-control" required>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="form-label">إلى تاريخ</label>
|
||||
<input type="date" name="end_date" id="leaveEnd" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">السبب</label>
|
||||
<textarea name="reason" id="leaveReason" class="form-control" rows="3" placeholder="سبب الإجازة..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" name="request_leave" class="btn btn-primary">حفظ الطلب</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function resetLeaveForm() {
|
||||
document.getElementById('leaveForm').reset();
|
||||
document.getElementById('leaveId').value = '';
|
||||
document.getElementById('leaveModalTitle').textContent = 'تقديم طلب إجازة';
|
||||
}
|
||||
|
||||
function editLeave(btn) {
|
||||
document.getElementById('leaveModalTitle').textContent = 'تعديل طلب إجازة';
|
||||
document.getElementById('leaveId').value = btn.getAttribute('data-id');
|
||||
document.getElementById('leaveEmp').value = btn.getAttribute('data-emp');
|
||||
document.getElementById('leaveType').value = btn.getAttribute('data-type');
|
||||
document.getElementById('leaveStart').value = btn.getAttribute('data-start');
|
||||
document.getElementById('leaveEnd').value = btn.getAttribute('data-end');
|
||||
document.getElementById('leaveReason').value = btn.getAttribute('data-reason');
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
263
hr_payroll.php
Normal file
263
hr_payroll.php
Normal file
@ -0,0 +1,263 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
if (!canView('hr_payroll')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$month = $_GET['month'] ?? date('m');
|
||||
$year = $_GET['year'] ?? date('Y');
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
// Handle Payroll Actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['generate_payroll'])) {
|
||||
if (!canAdd('hr_payroll')) {
|
||||
$error = "لا تملك صلاحية التوليد.";
|
||||
} else {
|
||||
$gen_month = $_POST['month'];
|
||||
$gen_year = $_POST['year'];
|
||||
|
||||
// Get all active employees
|
||||
$employees = db()->query("SELECT id, basic_salary FROM hr_employees WHERE status = 'active'")->fetchAll();
|
||||
$count = 0;
|
||||
|
||||
foreach ($employees as $emp) {
|
||||
// Check if already exists
|
||||
$stmt = db()->prepare("SELECT id FROM hr_payroll WHERE employee_id = ? AND month = ? AND year = ?");
|
||||
$stmt->execute([$emp['id'], $gen_month, $gen_year]);
|
||||
if ($stmt->fetch()) continue; // Skip if exists
|
||||
|
||||
// Calculate Absent Deductions
|
||||
$stmt = db()->prepare("SELECT COUNT(*) FROM hr_attendance WHERE employee_id = ? AND status = 'absent' AND MONTH(date) = ? AND YEAR(date) = ?");
|
||||
$stmt->execute([$emp['id'], $gen_month, $gen_year]);
|
||||
$absent_days = $stmt->fetchColumn();
|
||||
|
||||
$daily_rate = $emp['basic_salary'] / 30;
|
||||
$deductions = round($absent_days * $daily_rate, 2);
|
||||
$net = $emp['basic_salary'] - $deductions;
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO hr_payroll (employee_id, month, year, basic_salary, deductions, net_salary, status) VALUES (?, ?, ?, ?, ?, ?, 'pending')");
|
||||
$stmt->execute([$emp['id'], $gen_month, $gen_year, $emp['basic_salary'], $deductions, $net]);
|
||||
$count++;
|
||||
}
|
||||
$success = "تم توليد الرواتب لـ $count موظف.";
|
||||
}
|
||||
} elseif (isset($_POST['update_payroll'])) {
|
||||
if (!canEdit('hr_payroll')) {
|
||||
$error = "لا تملك صلاحية التعديل.";
|
||||
} else {
|
||||
$id = $_POST['id'];
|
||||
$bonuses = floatval($_POST['bonuses']);
|
||||
$deductions = floatval($_POST['deductions']);
|
||||
$status = $_POST['status'];
|
||||
|
||||
// Recalculate Net
|
||||
$stmt = db()->prepare("SELECT basic_salary FROM hr_payroll WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$current = $stmt->fetch();
|
||||
|
||||
if ($current) {
|
||||
$net = $current['basic_salary'] + $bonuses - $deductions;
|
||||
$payment_date = ($status == 'paid') ? date('Y-m-d') : null;
|
||||
|
||||
$stmt = db()->prepare("UPDATE hr_payroll SET bonuses = ?, deductions = ?, net_salary = ?, status = ?, payment_date = ? WHERE id = ?");
|
||||
$stmt->execute([$bonuses, $deductions, $net, $status, $payment_date, $id]);
|
||||
$success = "تم تحديث الراتب.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Payroll Records
|
||||
$sql = "SELECT p.*, e.first_name, e.last_name, e.job_title
|
||||
FROM hr_payroll p
|
||||
JOIN hr_employees e ON p.employee_id = e.id
|
||||
WHERE p.month = ? AND p.year = ?
|
||||
ORDER BY e.first_name";
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute([$month, $year]);
|
||||
$payrolls = $stmt->fetchAll();
|
||||
|
||||
// Calculate Totals
|
||||
$total_salaries = 0;
|
||||
foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">مسير الرواتب</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<form class="d-flex gap-2 align-items-center" method="get">
|
||||
<select name="month" class="form-select form-select-sm">
|
||||
<?php for($m=1; $m<=12; $m++): ?>
|
||||
<option value="<?= $m ?>" <?= $m == $month ? 'selected' : '' ?>><?= date('F', mktime(0, 0, 0, $m, 1)) ?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
<select name="year" class="form-select form-select-sm">
|
||||
<?php for($y=date('Y')-1; $y<=date('Y')+1; $y++): ?>
|
||||
<option value="<?= $y ?>" <?= $y == $year ? 'selected' : '' ?>><?= $y ?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary">عرض</button>
|
||||
</form>
|
||||
|
||||
<?php if (canAdd('hr_payroll')): ?>
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#generateModal">
|
||||
<i class="fas fa-cog"></i> توليد الرواتب
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body py-2 text-center">
|
||||
<h6 class="mb-0 text-muted">إجمالي الرواتب للشهر</h6>
|
||||
<h4 class="mb-0 text-primary fw-bold"><?= number_format($total_salaries, 2) ?></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>الموظف</th>
|
||||
<th>الراتب الأساسي</th>
|
||||
<th>إضافي</th>
|
||||
<th>خصومات</th>
|
||||
<th>الصافي</th>
|
||||
<th>الحالة</th>
|
||||
<th>إجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($payrolls)): ?>
|
||||
<tr><td colspan="7" class="text-center py-4 text-muted">لا توجد بيانات لهذا الشهر. اضغط على "توليد الرواتب" للبدء.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($payrolls as $row): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></div>
|
||||
<div class="small text-muted"><?= htmlspecialchars($row['job_title']) ?></div>
|
||||
</td>
|
||||
<td><?= number_format($row['basic_salary'], 2) ?></td>
|
||||
<td class="text-success"><?= number_format($row['bonuses'], 2) ?></td>
|
||||
<td class="text-danger"><?= number_format($row['deductions'], 2) ?></td>
|
||||
<td class="fw-bold"><?= number_format($row['net_salary'], 2) ?></td>
|
||||
<td>
|
||||
<span class="badge bg-<?= $row['status'] == 'paid' ? 'success' : 'warning' ?>">
|
||||
<?= $row['status'] == 'paid' ? 'مدفوع' : 'معلق' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (canEdit('hr_payroll')): ?>
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editPayModal"
|
||||
data-id="<?= $row['id'] ?>"
|
||||
data-name="<?= htmlspecialchars($row['first_name']) ?>"
|
||||
data-bonus="<?= $row['bonuses'] ?>"
|
||||
data-deduct="<?= $row['deductions'] ?>"
|
||||
data-status="<?= $row['status'] ?>">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate Modal -->
|
||||
<div class="modal fade" id="generateModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">توليد الرواتب</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post">
|
||||
<div class="modal-body">
|
||||
<p>سيتم حساب الرواتب لجميع الموظفين النشطين لشهر: <strong><?= $month ?> / <?= $year ?></strong></p>
|
||||
<p class="text-muted small">سيتم احتساب الخصومات تلقائياً بناءً على أيام الغياب المسجلة.</p>
|
||||
<input type="hidden" name="month" value="<?= $month ?>">
|
||||
<input type="hidden" name="year" value="<?= $year ?>">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" name="generate_payroll" class="btn btn-primary">تأكيد التوليد</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Payroll Modal -->
|
||||
<div class="modal fade" id="editPayModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">تعديل الراتب: <span id="payName"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="id" id="payId">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">مكافآت / إضافي</label>
|
||||
<input type="number" step="0.01" name="bonuses" id="payBonus" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">خصومات</label>
|
||||
<input type="number" step="0.01" name="deductions" id="payDeduct" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">الحالة</label>
|
||||
<select name="status" id="payStatus" class="form-select">
|
||||
<option value="pending">معلق</option>
|
||||
<option value="paid">مدفوع</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" name="update_payroll" class="btn btn-primary">حفظ التغييرات</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const editPayModal = document.getElementById('editPayModal');
|
||||
editPayModal.addEventListener('show.bs.modal', event => {
|
||||
const button = event.relatedTarget;
|
||||
document.getElementById('payId').value = button.getAttribute('data-id');
|
||||
document.getElementById('payName').textContent = button.getAttribute('data-name');
|
||||
document.getElementById('payBonus').value = button.getAttribute('data-bonus');
|
||||
document.getElementById('payDeduct').value = button.getAttribute('data-deduct');
|
||||
document.getElementById('payStatus').value = button.getAttribute('data-status');
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
170
hr_reports.php
Normal file
170
hr_reports.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
|
||||
if (!canView('hr_reports')) {
|
||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||
require_once 'includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$report_type = $_GET['type'] ?? 'attendance_summary';
|
||||
$month = $_GET['month'] ?? date('m');
|
||||
$year = $_GET['year'] ?? date('Y');
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">تقارير الموارد البشرية</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0 d-print-none">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="window.print()">
|
||||
<i class="fas fa-print"></i> طباعة
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row d-print-none mb-4">
|
||||
<div class="col-md-12">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $report_type == 'attendance_summary' ? 'active' : '' ?>" href="?type=attendance_summary">ملخص الحضور الشهري</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $report_type == 'employee_list' ? 'active' : '' ?>" href="?type=employee_list">قائمة الموظفين الشاملة</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<?php if ($report_type == 'attendance_summary'): ?>
|
||||
<div class="card shadow-sm mb-4 d-print-none">
|
||||
<div class="card-body">
|
||||
<form class="row g-3 align-items-end" method="get">
|
||||
<input type="hidden" name="type" value="attendance_summary">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">الشهر</label>
|
||||
<select name="month" class="form-select">
|
||||
<?php for($m=1; $m<=12; $m++): ?>
|
||||
<option value="<?= $m ?>" <?= $m == $month ? 'selected' : '' ?>><?= date('F', mktime(0, 0, 0, $m, 1)) ?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">السنة</label>
|
||||
<select name="year" class="form-select">
|
||||
<?php for($y=date('Y')-1; $y<=date('Y')+1; $y++): ?>
|
||||
<option value="<?= $y ?>" <?= $y == $year ? 'selected' : '' ?>><?= $y ?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100">عرض التقرير</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="mb-0">تقرير الحضور لشهر <?= $month ?> / <?= $year ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-start">الموظف</th>
|
||||
<th>أيام الحضور</th>
|
||||
<th>أيام الغياب</th>
|
||||
<th>التأخير</th>
|
||||
<th>إجازات</th>
|
||||
<th>نسبة الحضور</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$sql = "SELECT e.id, e.first_name, e.last_name,
|
||||
COUNT(CASE WHEN a.status = 'present' THEN 1 END) as present_days,
|
||||
COUNT(CASE WHEN a.status = 'absent' THEN 1 END) as absent_days,
|
||||
COUNT(CASE WHEN a.status = 'late' THEN 1 END) as late_days,
|
||||
COUNT(CASE WHEN a.status = 'excused' OR a.status = 'holiday' THEN 1 END) as leave_days
|
||||
FROM hr_employees e
|
||||
LEFT JOIN hr_attendance a ON e.id = a.employee_id
|
||||
AND MONTH(a.date) = ? AND YEAR(a.date) = ?
|
||||
WHERE e.status = 'active'
|
||||
GROUP BY e.id
|
||||
ORDER BY e.first_name";
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute([$month, $year]);
|
||||
$report_data = $stmt->fetchAll();
|
||||
|
||||
foreach ($report_data as $row):
|
||||
$total_days_recorded = $row['present_days'] + $row['absent_days'] + $row['late_days'] + $row['leave_days'];
|
||||
$attendance_rate = $total_days_recorded > 0
|
||||
? round((($row['present_days'] + $row['late_days']) / $total_days_recorded) * 100, 1)
|
||||
: 0;
|
||||
?>
|
||||
<tr>
|
||||
<td class="text-start fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></td>
|
||||
<td class="text-success"><?= $row['present_days'] ?></td>
|
||||
<td class="text-danger"><?= $row['absent_days'] ?></td>
|
||||
<td class="text-warning"><?= $row['late_days'] ?></td>
|
||||
<td class="text-info"><?= $row['leave_days'] ?></td>
|
||||
<td class="fw-bold"><?= $attendance_rate ?>%</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($report_type == 'employee_list'): ?>
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="mb-0">سجل الموظفين الشامل</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>الرقم الوظيفي</th>
|
||||
<th>الاسم</th>
|
||||
<th>القسم</th>
|
||||
<th>الوظيفة</th>
|
||||
<th>تاريخ التعيين</th>
|
||||
<th>الراتب الأساسي</th>
|
||||
<th>رقم الهاتف</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$employees = db()->query("SELECT e.*, d.name as dept_name FROM hr_employees e LEFT JOIN hr_departments d ON e.department_id = d.id WHERE e.status = 'active' ORDER BY e.id")->fetchAll();
|
||||
foreach ($employees as $emp):
|
||||
?>
|
||||
<tr>
|
||||
<td>#<?= $emp['id'] ?></td>
|
||||
<td><?= htmlspecialchars($emp['first_name'] . ' ' . $emp['last_name']) ?></td>
|
||||
<td><?= htmlspecialchars($emp['dept_name'] ?? '-') ?></td>
|
||||
<td><?= htmlspecialchars($emp['job_title']) ?></td>
|
||||
<td><?= $emp['join_date'] ?></td>
|
||||
<td><?= number_format($emp['basic_salary'], 2) ?></td>
|
||||
<td><?= htmlspecialchars($emp['phone']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<style>
|
||||
@media print {
|
||||
.d-print-none { display: none !important; }
|
||||
.sidebar, .top-navbar { display: none !important; }
|
||||
.main-content { margin: 0 !important; padding: 0 !important; }
|
||||
.card { border: none !important; shadow: none !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
@ -114,6 +114,27 @@ if (isLoggedIn()) {
|
||||
if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename($_SERVER['PHP_SELF']) !== 'forgot_password.php' && basename($_SERVER['PHP_SELF']) !== 'install.php') {
|
||||
redirect('login.php');
|
||||
}
|
||||
|
||||
// Determine active groups
|
||||
$cp = basename($_SERVER['PHP_SELF']);
|
||||
|
||||
$mail_pages = ['inbound.php', 'outbound.php', 'internal_inbox.php', 'internal_outbox.php'];
|
||||
$is_mail_open = in_array($cp, $mail_pages);
|
||||
|
||||
$acct_pages = ['accounting.php', 'trial_balance.php', 'balance_sheet.php', 'accounts.php'];
|
||||
$is_acct_open = in_array($cp, $acct_pages);
|
||||
|
||||
$hr_pages = ['hr_dashboard.php', 'hr_employees.php', 'hr_attendance.php', 'hr_leaves.php', 'hr_holidays.php', 'hr_payroll.php', 'hr_reports.php'];
|
||||
$is_hr_open = in_array($cp, $hr_pages);
|
||||
|
||||
$stock_pages = ['stock_dashboard.php', 'stock_items.php', 'stock_in.php', 'stock_out.php', 'stock_lending.php', 'stock_reports.php', 'stock_settings.php'];
|
||||
$is_stock_open = in_array($cp, $stock_pages);
|
||||
|
||||
$report_pages = ['overdue_report.php'];
|
||||
$is_report_open = in_array($cp, $report_pages);
|
||||
|
||||
$admin_pages = ['index.php', 'users.php', 'charity-settings.php'];
|
||||
$is_admin_open = in_array($cp, $admin_pages);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl" data-bs-theme="<?= $user_theme ?>">
|
||||
@ -244,104 +265,280 @@ if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename(
|
||||
</div>
|
||||
|
||||
<ul class="nav flex-column mt-3 mb-4">
|
||||
<!-- Dashboard -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'user_dashboard.php') ? 'active' : '' ?>" href="user_dashboard.php">
|
||||
<a class="nav-link <?= ($cp == 'user_dashboard.php') ? 'active' : '' ?>" href="user_dashboard.php">
|
||||
<i class="fas fa-tachometer-alt me-2"></i> لوحة التحكم
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php if (canView('inbound') || canView('outbound')): ?>
|
||||
<div class="sidebar-heading">البريد الخارجي</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('inbound')): ?>
|
||||
<!-- Mail Group -->
|
||||
<?php if (canView('inbound') || canView('outbound') || canView('internal')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'inbound.php' && !isset($_GET['my_tasks'])) ? 'active' : '' ?>" href="inbound.php">
|
||||
<i class="fas fa-download me-2"></i> البريد الوارد
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('outbound')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'outbound.php' ? 'active' : '' ?>" href="outbound.php">
|
||||
<i class="fas fa-upload me-2"></i> البريد الصادر
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('internal')): ?>
|
||||
<div class="sidebar-heading">البريد الداخلي</div>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'internal_inbox.php' ? 'active' : '' ?>" href="internal_inbox.php">
|
||||
<i class="fas fa-inbox me-2"></i> الوارد الداخلي
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'internal_outbox.php' ? 'active' : '' ?>" href="internal_outbox.php">
|
||||
<i class="fas fa-paper-plane me-2"></i> الصادر الداخلي
|
||||
</a>
|
||||
<button class="sidebar-group-btn <?= $is_mail_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-mail" aria-expanded="<?= $is_mail_open ? 'true' : 'false' ?>">
|
||||
<span class="group-content group-mail">
|
||||
<i class="fas fa-envelope"></i> البريد
|
||||
</span>
|
||||
<i class="fas fa-chevron-down arrow-icon"></i>
|
||||
</button>
|
||||
<div class="collapse <?= $is_mail_open ? 'show' : '' ?>" id="menu-mail">
|
||||
<ul class="nav flex-column">
|
||||
<?php if (canView('inbound')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= ($cp == 'inbound.php' && !isset($_GET['my_tasks'])) ? 'active' : '' ?>" href="inbound.php">
|
||||
البريد الوارد
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('outbound')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'outbound.php' ? 'active' : '' ?>" href="outbound.php">
|
||||
البريد الصادر
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('internal')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'internal_inbox.php' ? 'active' : '' ?>" href="internal_inbox.php">
|
||||
الوارد الداخلي
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'internal_outbox.php' ? 'active' : '' ?>" href="internal_outbox.php">
|
||||
الصادر الداخلي
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Accounting Group -->
|
||||
<?php if (canView('accounting')): ?>
|
||||
<div class="sidebar-heading">نظام المحاسبة</div>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'accounting.php' ? 'active' : '' ?>" href="accounting.php">
|
||||
<i class="fas fa-calculator me-2"></i> المحاسبة
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'trial_balance.php' ? 'active' : '' ?>" href="trial_balance.php">
|
||||
<i class="fas fa-balance-scale me-2"></i> ميزان المراجعة
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'balance_sheet.php' ? 'active' : '' ?>" href="balance_sheet.php">
|
||||
<i class="fas fa-file-invoice-dollar me-2"></i> الميزانية العمومية
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'accounts.php' ? 'active' : '' ?>" href="accounts.php">
|
||||
<i class="fas fa-list-ul me-2"></i> دليل الحسابات
|
||||
</a>
|
||||
<button class="sidebar-group-btn <?= $is_acct_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-acct" aria-expanded="<?= $is_acct_open ? 'true' : 'false' ?>">
|
||||
<span class="group-content group-acct">
|
||||
<i class="fas fa-calculator"></i> المحاسبة
|
||||
</span>
|
||||
<i class="fas fa-chevron-down arrow-icon"></i>
|
||||
</button>
|
||||
<div class="collapse <?= $is_acct_open ? 'show' : '' ?>" id="menu-acct">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'accounting.php' ? 'active' : '' ?>" href="accounting.php">
|
||||
المحاسبة العامة
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'trial_balance.php' ? 'active' : '' ?>" href="trial_balance.php">
|
||||
ميزان المراجعة
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'balance_sheet.php' ? 'active' : '' ?>" href="balance_sheet.php">
|
||||
الميزانية العمومية
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'accounts.php' ? 'active' : '' ?>" href="accounts.php">
|
||||
دليل الحسابات
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- HR Group -->
|
||||
<?php if (canView('hr_employees') || canView('hr_dashboard')): ?>
|
||||
<li class="nav-item">
|
||||
<button class="sidebar-group-btn <?= $is_hr_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-hr" aria-expanded="<?= $is_hr_open ? 'true' : 'false' ?>">
|
||||
<span class="group-content group-hr">
|
||||
<i class="fas fa-users-cog"></i> الموارد البشرية
|
||||
</span>
|
||||
<i class="fas fa-chevron-down arrow-icon"></i>
|
||||
</button>
|
||||
<div class="collapse <?= $is_hr_open ? 'show' : '' ?>" id="menu-hr">
|
||||
<ul class="nav flex-column">
|
||||
<?php if (canView('hr_dashboard')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_dashboard.php' ? 'active' : '' ?>" href="hr_dashboard.php">
|
||||
لوحة HR
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('hr_employees')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_employees.php' ? 'active' : '' ?>" href="hr_employees.php">
|
||||
الموظفين
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('hr_attendance')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_attendance.php' ? 'active' : '' ?>" href="hr_attendance.php">
|
||||
الحضور والانصراف
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('hr_leaves')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_leaves.php' ? 'active' : '' ?>" href="hr_leaves.php">
|
||||
الإجازات
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('hr_attendance')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_holidays.php' ? 'active' : '' ?>" href="hr_holidays.php">
|
||||
العطلات
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('hr_payroll')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_payroll.php' ? 'active' : '' ?>" href="hr_payroll.php">
|
||||
الرواتب
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('hr_reports')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'hr_reports.php' ? 'active' : '' ?>" href="hr_reports.php">
|
||||
التقارير
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Stock Group -->
|
||||
<?php if (canView('stock_dashboard') || canView('stock_items') || canView('stock_settings')): ?>
|
||||
<li class="nav-item">
|
||||
<button class="sidebar-group-btn <?= $is_stock_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-stock" aria-expanded="<?= $is_stock_open ? 'true' : 'false' ?>">
|
||||
<span class="group-content group-stock">
|
||||
<i class="fas fa-boxes"></i> إدارة المخزون
|
||||
</span>
|
||||
<i class="fas fa-chevron-down arrow-icon"></i>
|
||||
</button>
|
||||
<div class="collapse <?= $is_stock_open ? 'show' : '' ?>" id="menu-stock">
|
||||
<ul class="nav flex-column">
|
||||
<?php if (canView('stock_dashboard')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_dashboard.php' ? 'active' : '' ?>" href="stock_dashboard.php">
|
||||
لوحة التحكم
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_items')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_items.php' ? 'active' : '' ?>" href="stock_items.php">
|
||||
الأصناف والمخزون
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_in')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_in.php' ? 'active' : '' ?>" href="stock_in.php">
|
||||
توريد (وارد)
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_out')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_out.php' ? 'active' : '' ?>" href="stock_out.php">
|
||||
صرف (صادر)
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_lending')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_lending.php' ? 'active' : '' ?>" href="stock_lending.php">
|
||||
الإعارة
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_reports')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_reports.php' ? 'active' : '' ?>" href="stock_reports.php">
|
||||
التقارير
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_settings')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'stock_settings.php' ? 'active' : '' ?>" href="stock_settings.php">
|
||||
إعدادات المخزون
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Reports Group -->
|
||||
<?php if (canView('reports')): ?>
|
||||
<div class="sidebar-heading">التقارير</div>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'overdue_report.php' ? 'active' : '' ?>" href="overdue_report.php">
|
||||
<i class="fas fa-exclamation-circle me-2"></i> تقرير التأخير
|
||||
</a>
|
||||
<button class="sidebar-group-btn <?= $is_report_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-reports" aria-expanded="<?= $is_report_open ? 'true' : 'false' ?>">
|
||||
<span class="group-content group-reports">
|
||||
<i class="fas fa-chart-pie"></i> التقارير
|
||||
</span>
|
||||
<i class="fas fa-chevron-down arrow-icon"></i>
|
||||
</button>
|
||||
<div class="collapse <?= $is_report_open ? 'show' : '' ?>" id="menu-reports">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'overdue_report.php' ? 'active' : '' ?>" href="overdue_report.php">
|
||||
تقرير التأخير
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Admin Group -->
|
||||
<?php if (canView('users') || canView('settings') || isAdmin()): ?>
|
||||
<div class="sidebar-heading">الإدارة</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isAdmin()): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'index.php' ? 'active' : '' ?>" href="index.php">
|
||||
<i class="fas fa-chart-line me-2"></i> إحصائيات النظام
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<button class="sidebar-group-btn <?= $is_admin_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-admin" aria-expanded="<?= $is_admin_open ? 'true' : 'false' ?>">
|
||||
<span class="group-content group-admin">
|
||||
<i class="fas fa-cogs"></i> الإدارة
|
||||
</span>
|
||||
<i class="fas fa-chevron-down arrow-icon"></i>
|
||||
</button>
|
||||
<div class="collapse <?= $is_admin_open ? 'show' : '' ?>" id="menu-admin">
|
||||
<ul class="nav flex-column">
|
||||
<?php if (isAdmin()): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'index.php' ? 'active' : '' ?>" href="index.php">
|
||||
إحصائيات النظام
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('users')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'users.php' ? 'active' : '' ?>" href="users.php">
|
||||
<i class="fas fa-users me-2"></i> إدارة المستخدمين
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('users')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'users.php' ? 'active' : '' ?>" href="users.php">
|
||||
إدارة المستخدمين
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (canView('settings')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'charity-settings.php' ? 'active' : '' ?>" href="charity-settings.php">
|
||||
<i class="fas fa-cog me-2"></i> إعدادات النظام
|
||||
</a>
|
||||
<?php if (canView('settings')): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $cp == 'charity-settings.php' ? 'active' : '' ?>" href="charity-settings.php">
|
||||
إعدادات النظام
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
149
stock_dashboard.php
Normal file
149
stock_dashboard.php
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_dashboard')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Stats
|
||||
$total_items = db()->query("SELECT COUNT(*) FROM stock_items")->fetchColumn();
|
||||
$total_stores = db()->query("SELECT COUNT(*) FROM stock_stores")->fetchColumn();
|
||||
$low_stock_count = db()->query("
|
||||
SELECT COUNT(*) FROM stock_quantities q
|
||||
JOIN stock_items i ON q.item_id = i.id
|
||||
WHERE q.quantity <= i.min_quantity
|
||||
")->fetchColumn();
|
||||
|
||||
// Recent Transactions
|
||||
$stmt = db()->query("
|
||||
SELECT t.*, i.name as item_name, s.name as store_name, u.full_name as user_name
|
||||
FROM stock_transactions t
|
||||
JOIN stock_items i ON t.item_id = i.id
|
||||
JOIN stock_stores s ON t.store_id = s.id
|
||||
LEFT JOIN users u ON t.user_id = u.id
|
||||
ORDER BY t.created_at DESC LIMIT 10
|
||||
");
|
||||
$recent_transactions = $stmt->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">لوحة تحكم المخزون</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<?php if (canView('stock_in')): ?>
|
||||
<a href="stock_in.php" class="btn btn-sm btn-success me-2">
|
||||
<i class="fas fa-plus"></i> توريد جديد
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if (canView('stock_out')): ?>
|
||||
<a href="stock_out.php" class="btn btn-sm btn-danger">
|
||||
<i class="fas fa-minus"></i> صرف جديد
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card shadow-sm border-0 h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0 bg-primary bg-opacity-10 p-3 rounded">
|
||||
<i class="fas fa-box fa-2x text-primary"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
<h6 class="text-muted mb-1">إجمالي الأصناف</h6>
|
||||
<h3 class="mb-0 fw-bold"><?= number_format($total_items) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card shadow-sm border-0 h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0 bg-success bg-opacity-10 p-3 rounded">
|
||||
<i class="fas fa-warehouse fa-2x text-success"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
<h6 class="text-muted mb-1">المستودعات</h6>
|
||||
<h3 class="mb-0 fw-bold"><?= number_format($total_stores) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card shadow-sm border-0 h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-shrink-0 bg-warning bg-opacity-10 p-3 rounded">
|
||||
<i class="fas fa-exclamation-triangle fa-2x text-warning"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1 ms-3">
|
||||
<h6 class="text-muted mb-1">تنبيهات المخزون المنخفض</h6>
|
||||
<h3 class="mb-0 fw-bold"><?= number_format($low_stock_count) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold"><i class="fas fa-history me-2 text-secondary"></i> أحدث الحركات</h5>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>النوع</th>
|
||||
<th>الصنف</th>
|
||||
<th>المستودع</th>
|
||||
<th>الكمية</th>
|
||||
<th>بواسطة</th>
|
||||
<th>التاريخ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($recent_transactions)): ?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-4 text-muted">لا توجد حركات حديثة</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($recent_transactions as $t): ?>
|
||||
<tr>
|
||||
<td><?= $t['id'] ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$badges = [
|
||||
'in' => ['bg-success', 'توريد'],
|
||||
'out' => ['bg-danger', 'صرف'],
|
||||
'damage' => ['bg-dark', 'تالف'],
|
||||
'lend' => ['bg-info text-dark', 'إعارة'],
|
||||
'return' => ['bg-primary', 'إرجاع'],
|
||||
'transfer' => ['bg-secondary', 'نقل']
|
||||
];
|
||||
$b = $badges[$t['transaction_type']] ?? ['bg-secondary', $t['transaction_type']];
|
||||
?>
|
||||
<span class="badge <?= $b[0] ?>"><?= $b[1] ?></span>
|
||||
</td>
|
||||
<td class="fw-bold"><?= htmlspecialchars($t['item_name']) ?></td>
|
||||
<td><?= htmlspecialchars($t['store_name']) ?></td>
|
||||
<td dir="ltr" class="text-end fw-bold"><?= number_format($t['quantity'], 2) ?></td>
|
||||
<td><?= htmlspecialchars($t['user_name'] ?? '-') ?></td>
|
||||
<td><?= date('Y-m-d H:i', strtotime($t['created_at'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
136
stock_in.php
Normal file
136
stock_in.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_in')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// Handle Form Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$store_id = $_POST['store_id'] ?? null;
|
||||
$item_id = $_POST['item_id'] ?? null;
|
||||
$quantity = $_POST['quantity'] ?? 0;
|
||||
$reference = $_POST['reference'] ?? '';
|
||||
$notes = $_POST['notes'] ?? '';
|
||||
|
||||
if ($store_id && $item_id && $quantity > 0) {
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Create Transaction
|
||||
$stmt = $pdo->prepare("INSERT INTO stock_transactions (transaction_type, store_id, item_id, quantity, user_id, reference, notes) VALUES ('in', ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$store_id, $item_id, $quantity, $_SESSION['user_id'], $reference, $notes]);
|
||||
|
||||
// 2. Update Quantity
|
||||
// Check if record exists
|
||||
$check = $pdo->prepare("SELECT id, quantity FROM stock_quantities WHERE store_id = ? AND item_id = ?");
|
||||
$check->execute([$store_id, $item_id]);
|
||||
$exists = $check->fetch();
|
||||
|
||||
if ($exists) {
|
||||
$new_qty = $exists['quantity'] + $quantity;
|
||||
$update = $pdo->prepare("UPDATE stock_quantities SET quantity = ? WHERE id = ?");
|
||||
$update->execute([$new_qty, $exists['id']]);
|
||||
} else {
|
||||
$insert = $pdo->prepare("INSERT INTO stock_quantities (store_id, item_id, quantity) VALUES (?, ?, ?)");
|
||||
$insert->execute([$store_id, $item_id, $quantity]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
$success = 'تم تسجيل عملية التوريد بنجاح';
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
$error = 'حدث خطأ: ' . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = 'يرجى تعبئة جميع الحقول المطلوبة';
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Data for Dropdowns
|
||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">توريد مخزون (وارد)</h1>
|
||||
<a href="stock_dashboard.php" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-right"></i> عودة للوحة التحكم
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= $success ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= $error ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-4">
|
||||
<form method="POST">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">المستودع <span class="text-danger">*</span></label>
|
||||
<select name="store_id" class="form-select" required>
|
||||
<option value="">-- اختر المستودع --</option>
|
||||
<?php foreach ($stores as $store): ?>
|
||||
<option value="<?= $store['id'] ?>"><?= htmlspecialchars($store['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">الصنف <span class="text-danger">*</span></label>
|
||||
<select name="item_id" class="form-select" required>
|
||||
<option value="">-- اختر الصنف --</option>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<option value="<?= $item['id'] ?>"><?= htmlspecialchars($item['name']) ?> (<?= htmlspecialchars($item['sku'] ?: '-') ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">الكمية <span class="text-danger">*</span></label>
|
||||
<input type="number" step="0.01" name="quantity" class="form-control" required min="0.01">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">رقم المرجع / الفاتورة</label>
|
||||
<input type="text" name="reference" class="form-control" placeholder="مثال: فاتورة رقم 123">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">ملاحظات</label>
|
||||
<textarea name="notes" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-success btn-lg">
|
||||
<i class="fas fa-save me-2"></i> حفظ عملية التوريد
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
242
stock_items.php
Normal file
242
stock_items.php
Normal file
@ -0,0 +1,242 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_items')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// Handle Actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
$id = $_POST['id'] ?? 0;
|
||||
$name = $_POST['name'] ?? '';
|
||||
$sku = $_POST['sku'] ?? '';
|
||||
$category_id = $_POST['category_id'] ?? null;
|
||||
$min_quantity = $_POST['min_quantity'] ?? 0;
|
||||
$unit = $_POST['unit'] ?? 'piece';
|
||||
$description = $_POST['description'] ?? '';
|
||||
|
||||
if ($action === 'add' && canAdd('stock_items')) {
|
||||
if ($name) {
|
||||
$stmt = db()->prepare("INSERT INTO stock_items (name, sku, category_id, min_quantity, unit, description) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
if ($stmt->execute([$name, $sku, $category_id, $min_quantity, $unit, $description])) {
|
||||
$success = 'تم إضافة الصنف بنجاح';
|
||||
} else {
|
||||
$error = 'حدث خطأ أثناء الإضافة';
|
||||
}
|
||||
}
|
||||
} elseif ($action === 'edit' && canEdit('stock_items')) {
|
||||
if ($name && $id) {
|
||||
$stmt = db()->prepare("UPDATE stock_items SET name=?, sku=?, category_id=?, min_quantity=?, unit=?, description=? WHERE id=?");
|
||||
if ($stmt->execute([$name, $sku, $category_id, $min_quantity, $unit, $description, $id])) {
|
||||
$success = 'تم تحديث الصنف بنجاح';
|
||||
} else {
|
||||
$error = 'حدث خطأ أثناء التحديث';
|
||||
}
|
||||
}
|
||||
} elseif ($action === 'delete' && canDelete('stock_items')) {
|
||||
if ($id) {
|
||||
$stmt = db()->prepare("DELETE FROM stock_items WHERE id=?");
|
||||
if ($stmt->execute([$id])) {
|
||||
$success = 'تم حذف الصنف بنجاح';
|
||||
} else {
|
||||
$error = 'لا يمكن حذف الصنف لوجود حركات مرتبطة به';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Items with Category Name and Total Quantity
|
||||
$query = "
|
||||
SELECT i.*, c.name as category_name,
|
||||
(SELECT SUM(quantity) FROM stock_quantities q WHERE q.item_id = i.id) as total_quantity
|
||||
FROM stock_items i
|
||||
LEFT JOIN stock_categories c ON i.category_id = c.id
|
||||
ORDER BY i.name ASC
|
||||
";
|
||||
$items = db()->query($query)->fetchAll();
|
||||
|
||||
// Fetch Categories for Dropdown
|
||||
$categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">إدارة الأصناف</h1>
|
||||
<?php if (canAdd('stock_items')): ?>
|
||||
<button type="button" class="btn btn-primary" onclick="openItemModal('add')">
|
||||
<i class="fas fa-plus"></i> إضافة صنف جديد
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= $success ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= $error ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">اسم الصنف</th>
|
||||
<th>الرمز (SKU)</th>
|
||||
<th>التصنيف</th>
|
||||
<th>الكمية الحالية</th>
|
||||
<th>الحد الأدنى</th>
|
||||
<th>الوحدة</th>
|
||||
<th class="text-center">الإجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td class="ps-4 fw-bold"><?= htmlspecialchars($item['name']) ?></td>
|
||||
<td><?= htmlspecialchars($item['sku'] ?? '-') ?></td>
|
||||
<td><span class="badge bg-secondary"><?= htmlspecialchars($item['category_name'] ?? 'عام') ?></span></td>
|
||||
<td>
|
||||
<?php
|
||||
$qty = $item['total_quantity'] ?: 0;
|
||||
$cls = ($qty <= $item['min_quantity']) ? 'text-danger fw-bold' : 'text-success';
|
||||
?>
|
||||
<span class="<?= $cls ?>"><?= number_format($qty, 2) ?></span>
|
||||
</td>
|
||||
<td><?= number_format($item['min_quantity']) ?></td>
|
||||
<td><?= htmlspecialchars($item['unit']) ?></td>
|
||||
<td class="text-center">
|
||||
<?php if (canEdit('stock_items')): ?>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick='openItemModal("edit", <?= json_encode($item) ?>)'>
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if (canDelete('stock_items')): ?>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="confirmDelete(<?= $item['id'] ?>)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Item Modal -->
|
||||
<div class="modal fade" id="itemModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title" id="itemModalLabel">إضافة صنف</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="action" id="modalAction" value="add">
|
||||
<input type="hidden" name="id" id="modalId" value="">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">اسم الصنف <span class="text-danger">*</span></label>
|
||||
<input type="text" name="name" id="modalName" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">الرمز (SKU)</label>
|
||||
<input type="text" name="sku" id="modalSku" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">التصنيف</label>
|
||||
<select name="category_id" id="modalCategory" class="form-select">
|
||||
<option value="">-- اختر --</option>
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<option value="<?= $cat['id'] ?>"><?= htmlspecialchars($cat['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">الحد الأدنى للكمية</label>
|
||||
<input type="number" name="min_quantity" id="modalMinQty" class="form-control" value="0">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">الوحدة</label>
|
||||
<input type="text" name="unit" id="modalUnit" class="form-control" value="قطعة">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">الوصف</label>
|
||||
<textarea name="description" id="modalDesc" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" class="btn btn-primary">حفظ</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let itemModal;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
itemModal = new bootstrap.Modal(document.getElementById('itemModal'));
|
||||
});
|
||||
|
||||
function openItemModal(action, data = null) {
|
||||
document.getElementById('modalAction').value = action;
|
||||
document.getElementById('itemModalLabel').textContent = action === 'add' ? 'إضافة صنف جديد' : 'تعديل الصنف';
|
||||
|
||||
if (action === 'edit' && data) {
|
||||
document.getElementById('modalId').value = data.id;
|
||||
document.getElementById('modalName').value = data.name;
|
||||
document.getElementById('modalSku').value = data.sku || '';
|
||||
document.getElementById('modalCategory').value = data.category_id || '';
|
||||
document.getElementById('modalMinQty').value = data.min_quantity;
|
||||
document.getElementById('modalUnit').value = data.unit;
|
||||
document.getElementById('modalDesc').value = data.description || '';
|
||||
} else {
|
||||
document.getElementById('modalId').value = '';
|
||||
document.getElementById('modalName').value = '';
|
||||
document.getElementById('modalSku').value = '';
|
||||
document.getElementById('modalCategory').value = '';
|
||||
document.getElementById('modalMinQty').value = '0';
|
||||
document.getElementById('modalUnit').value = 'قطعة';
|
||||
document.getElementById('modalDesc').value = '';
|
||||
}
|
||||
itemModal.show();
|
||||
}
|
||||
|
||||
function confirmDelete(id) {
|
||||
if(confirm('هل أنت متأكد من حذف هذا الصنف؟')) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.innerHTML = `<input type="hidden" name="action" value="delete"><input type="hidden" name="id" value="${id}">`;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
251
stock_lending.php
Normal file
251
stock_lending.php
Normal file
@ -0,0 +1,251 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_lending')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// Handle Actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'lend') {
|
||||
$store_id = $_POST['store_id'];
|
||||
$item_id = $_POST['item_id'];
|
||||
$quantity = $_POST['quantity'];
|
||||
$borrower = $_POST['borrower_name'];
|
||||
$phone = $_POST['borrower_phone'];
|
||||
$date = $_POST['expected_return_date'];
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Check stock
|
||||
$check = $pdo->prepare("SELECT id, quantity FROM stock_quantities WHERE store_id = ? AND item_id = ? FOR UPDATE");
|
||||
$check->execute([$store_id, $item_id]);
|
||||
$stock = $check->fetch();
|
||||
|
||||
if (!$stock || $stock['quantity'] < $quantity) {
|
||||
throw new Exception("الكمية غير متوفرة للإعارة");
|
||||
}
|
||||
|
||||
// Deduct Stock
|
||||
$pdo->prepare("UPDATE stock_quantities SET quantity = quantity - ? WHERE id = ?")->execute([$quantity, $stock['id']]);
|
||||
|
||||
// Create Transaction
|
||||
$stmt = $pdo->prepare("INSERT INTO stock_transactions (transaction_type, store_id, item_id, quantity, user_id, reference) VALUES ('lend', ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$store_id, $item_id, $quantity, $_SESSION['user_id'], "إعارة: $borrower"]);
|
||||
$trans_id = $pdo->lastInsertId();
|
||||
|
||||
// Create Lending Record
|
||||
$stmt = $pdo->prepare("INSERT INTO stock_lending (transaction_id, borrower_name, borrower_phone, expected_return_date, status) VALUES (?, ?, ?, ?, 'active')");
|
||||
$stmt->execute([$trans_id, $borrower, $phone, $date]);
|
||||
|
||||
$pdo->commit();
|
||||
$success = 'تم تسجيل الإعارة بنجاح';
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
} elseif ($action === 'return') {
|
||||
$lending_id = $_POST['lending_id'];
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Get Lending Info
|
||||
$lend = $pdo->query("SELECT l.*, t.store_id, t.item_id, t.quantity FROM stock_lending l JOIN stock_transactions t ON l.transaction_id = t.id WHERE l.id = $lending_id")->fetch();
|
||||
|
||||
if ($lend && $lend['status'] === 'active') {
|
||||
// Add Stock Back
|
||||
$check = $pdo->prepare("SELECT id FROM stock_quantities WHERE store_id = ? AND item_id = ?");
|
||||
$check->execute([$lend['store_id'], $lend['item_id']]);
|
||||
$s_id = $check->fetchColumn();
|
||||
|
||||
if ($s_id) {
|
||||
$pdo->prepare("UPDATE stock_quantities SET quantity = quantity + ? WHERE id = ?")->execute([$lend['quantity'], $s_id]);
|
||||
} else {
|
||||
$pdo->prepare("INSERT INTO stock_quantities (store_id, item_id, quantity) VALUES (?, ?, ?)")->execute([$lend['store_id'], $lend['item_id'], $lend['quantity']]);
|
||||
}
|
||||
|
||||
// Create Return Transaction
|
||||
$stmt = $pdo->prepare("INSERT INTO stock_transactions (transaction_type, store_id, item_id, quantity, user_id, reference) VALUES ('return', ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$lend['store_id'], $lend['item_id'], $lend['quantity'], $_SESSION['user_id'], "إرجاع إعارة: " . $lend['borrower_name']]);
|
||||
$ret_id = $pdo->lastInsertId();
|
||||
|
||||
// Update Lending Record
|
||||
$pdo->prepare("UPDATE stock_lending SET status = 'returned', return_transaction_id = ? WHERE id = ?")->execute([$ret_id, $lending_id]);
|
||||
|
||||
$pdo->commit();
|
||||
$success = 'تم إرجاع المواد بنجاح';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Active Loans
|
||||
$loans = db()->query("
|
||||
SELECT l.*, i.name as item_name, s.name as store_name, t.quantity, t.created_at as lend_date
|
||||
FROM stock_lending l
|
||||
JOIN stock_transactions t ON l.transaction_id = t.id
|
||||
JOIN stock_items i ON t.item_id = i.id
|
||||
JOIN stock_stores s ON t.store_id = s.id
|
||||
WHERE l.status = 'active'
|
||||
ORDER BY l.expected_return_date ASC
|
||||
")->fetchAll();
|
||||
|
||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">إعارة المواد</h1>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#lendModal">
|
||||
<i class="fas fa-hand-holding me-2"></i> إعارة جديدة
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= $success ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= $error ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold"><i class="fas fa-clock me-2 text-warning"></i> الإعارات النشطة</h5>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>المستعير</th>
|
||||
<th>المادة</th>
|
||||
<th>الكمية</th>
|
||||
<th>تاريخ الإعارة</th>
|
||||
<th>تاريخ الإرجاع المتوقع</th>
|
||||
<th>الحالة</th>
|
||||
<th>الإجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($loans)): ?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-4 text-muted">لا توجد إعارات نشطة حالياً</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($loans as $loan): ?>
|
||||
<tr>
|
||||
<td class="fw-bold">
|
||||
<?= htmlspecialchars($loan['borrower_name']) ?>
|
||||
<br><small class="text-muted"><?= htmlspecialchars($loan['borrower_phone']) ?></small>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($loan['item_name']) ?></td>
|
||||
<td class="fw-bold"><?= number_format($loan['quantity'], 2) ?></td>
|
||||
<td><?= date('Y-m-d', strtotime($loan['lend_date'])) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$due = strtotime($loan['expected_return_date']);
|
||||
$cls = ($due < time()) ? 'text-danger fw-bold' : '';
|
||||
?>
|
||||
<span class="<?= $cls ?>"><?= date('Y-m-d', $due) ?></span>
|
||||
</td>
|
||||
<td><span class="badge bg-warning text-dark">نشط</span></td>
|
||||
<td>
|
||||
<form method="POST" onsubmit="return confirm('هل استلمت المواد المرجعة؟ سيتم إضافتها للمخزون.')">
|
||||
<input type="hidden" name="action" value="return">
|
||||
<input type="hidden" name="lending_id" value="<?= $loan['id'] ?>">
|
||||
<button type="submit" class="btn btn-sm btn-success">
|
||||
<i class="fas fa-check me-1"></i> تسجيل إرجاع
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lend Modal -->
|
||||
<div class="modal fade" id="lendModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title">تسجيل إعارة جديدة</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="action" value="lend">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">المستودع</label>
|
||||
<select name="store_id" class="form-select" required>
|
||||
<?php foreach ($stores as $store): ?>
|
||||
<option value="<?= $store['id'] ?>"><?= htmlspecialchars($store['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">المادة</label>
|
||||
<select name="item_id" class="form-select" required>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<option value="<?= $item['id'] ?>"><?= htmlspecialchars($item['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">الكمية</label>
|
||||
<input type="number" step="0.01" name="quantity" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">اسم المستعير</label>
|
||||
<input type="text" name="borrower_name" class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">رقم الهاتف</label>
|
||||
<input type="text" name="borrower_phone" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">تاريخ الإرجاع المتوقع</label>
|
||||
<input type="date" name="expected_return_date" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" class="btn btn-primary">حفظ</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
144
stock_out.php
Normal file
144
stock_out.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_out')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// Handle Form Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$store_id = $_POST['store_id'] ?? null;
|
||||
$item_id = $_POST['item_id'] ?? null;
|
||||
$quantity = $_POST['quantity'] ?? 0;
|
||||
$type = $_POST['type'] ?? 'out'; // 'out' or 'damage'
|
||||
$reference = $_POST['reference'] ?? '';
|
||||
$notes = $_POST['notes'] ?? '';
|
||||
|
||||
if ($store_id && $item_id && $quantity > 0) {
|
||||
try {
|
||||
$pdo = db();
|
||||
// Check availability first
|
||||
$check = $pdo->prepare("SELECT id, quantity FROM stock_quantities WHERE store_id = ? AND item_id = ?");
|
||||
$check->execute([$store_id, $item_id]);
|
||||
$stock = $check->fetch();
|
||||
|
||||
if (!$stock || $stock['quantity'] < $quantity) {
|
||||
$error = 'الكمية غير متوفرة في المستودع المحدد. الكمية الحالية: ' . ($stock['quantity'] ?? 0);
|
||||
} else {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Create Transaction
|
||||
$stmt = $pdo->prepare("INSERT INTO stock_transactions (transaction_type, store_id, item_id, quantity, user_id, reference, notes) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$type, $store_id, $item_id, $quantity, $_SESSION['user_id'], $reference, $notes]);
|
||||
|
||||
// 2. Update Quantity
|
||||
$new_qty = $stock['quantity'] - $quantity;
|
||||
$update = $pdo->prepare("UPDATE stock_quantities SET quantity = ? WHERE id = ?");
|
||||
$update->execute([$new_qty, $stock['id']]);
|
||||
|
||||
$pdo->commit();
|
||||
$success = 'تم تسجيل عملية الصرف بنجاح';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
$error = 'حدث خطأ: ' . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = 'يرجى تعبئة جميع الحقول المطلوبة';
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Data for Dropdowns
|
||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">صرف مخزون (صادر)</h1>
|
||||
<a href="stock_dashboard.php" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-right"></i> عودة للوحة التحكم
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= $success ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= $error ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-4">
|
||||
<form method="POST">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">المستودع <span class="text-danger">*</span></label>
|
||||
<select name="store_id" class="form-select" required>
|
||||
<option value="">-- اختر المستودع --</option>
|
||||
<?php foreach ($stores as $store): ?>
|
||||
<option value="<?= $store['id'] ?>"><?= htmlspecialchars($store['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">الصنف <span class="text-danger">*</span></label>
|
||||
<select name="item_id" class="form-select" required>
|
||||
<option value="">-- اختر الصنف --</option>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<option value="<?= $item['id'] ?>"><?= htmlspecialchars($item['name']) ?> (<?= htmlspecialchars($item['sku'] ?: '-') ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">الكمية <span class="text-danger">*</span></label>
|
||||
<input type="number" step="0.01" name="quantity" class="form-control" required min="0.01">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">نوع العملية</label>
|
||||
<select name="type" class="form-select">
|
||||
<option value="out">صرف (استهلاك/بيع)</option>
|
||||
<option value="damage">تالف / منتهي الصلاحية</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">رقم المرجع / للمستلم</label>
|
||||
<input type="text" name="reference" class="form-control" placeholder="مثال: إذن صرف رقم 55">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">ملاحظات</label>
|
||||
<textarea name="notes" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-danger btn-lg">
|
||||
<i class="fas fa-sign-out-alt me-2"></i> تنفيذ الصرف
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
173
stock_reports.php
Normal file
173
stock_reports.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_reports')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$start_date = $_GET['start_date'] ?? date('Y-m-01');
|
||||
$end_date = $_GET['end_date'] ?? date('Y-m-d');
|
||||
$store_id = $_GET['store_id'] ?? '';
|
||||
$item_id = $_GET['item_id'] ?? '';
|
||||
$type = $_GET['type'] ?? '';
|
||||
|
||||
// Build Query
|
||||
$sql = "
|
||||
SELECT t.*, i.name as item_name, s.name as store_name, u.full_name as user_name
|
||||
FROM stock_transactions t
|
||||
JOIN stock_items i ON t.item_id = i.id
|
||||
JOIN stock_stores s ON t.store_id = s.id
|
||||
LEFT JOIN users u ON t.user_id = u.id
|
||||
WHERE DATE(t.created_at) BETWEEN ? AND ?
|
||||
";
|
||||
$params = [$start_date, $end_date];
|
||||
|
||||
if ($store_id) {
|
||||
$sql .= " AND t.store_id = ?";
|
||||
$params[] = $store_id;
|
||||
}
|
||||
if ($item_id) {
|
||||
$sql .= " AND t.item_id = ?";
|
||||
$params[] = $item_id;
|
||||
}
|
||||
if ($type) {
|
||||
$sql .= " AND t.transaction_type = ?";
|
||||
$params[] = $type;
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY t.created_at DESC";
|
||||
|
||||
$stmt = db()->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$transactions = $stmt->fetchAll();
|
||||
|
||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom d-print-none">
|
||||
<h1 class="h2">تقارير حركة المخزون</h1>
|
||||
<button onclick="window.print()" class="btn btn-secondary">
|
||||
<i class="fas fa-print me-2"></i> طباعة التقرير
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4 d-print-none">
|
||||
<div class="card-body bg-light">
|
||||
<form method="GET" class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold">من تاريخ</label>
|
||||
<input type="date" name="start_date" class="form-control" value="<?= $start_date ?>">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold">إلى تاريخ</label>
|
||||
<input type="date" name="end_date" class="form-control" value="<?= $end_date ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label fw-bold">المستودع</label>
|
||||
<select name="store_id" class="form-select">
|
||||
<option value="">الكل</option>
|
||||
<?php foreach ($stores as $s): ?>
|
||||
<option value="<?= $s['id'] ?>" <?= $store_id == $s['id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label fw-bold">الصنف</label>
|
||||
<select name="item_id" class="form-select">
|
||||
<option value="">الكل</option>
|
||||
<?php foreach ($items as $i): ?>
|
||||
<option value="<?= $i['id'] ?>" <?= $item_id == $i['id'] ? 'selected' : '' ?>><?= htmlspecialchars($i['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-filter me-2"></i> عرض
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-none d-print-block text-center mb-4">
|
||||
<h3>تقرير حركة المخزون</h3>
|
||||
<p>من <?= $start_date ?> إلى <?= $end_date ?></p>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>التاريخ</th>
|
||||
<th>النوع</th>
|
||||
<th>المستودع</th>
|
||||
<th>الصنف</th>
|
||||
<th>الكمية</th>
|
||||
<th>المستخدم</th>
|
||||
<th>ملاحظات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($transactions)): ?>
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-muted">لا توجد بيانات للعرض</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($transactions as $t): ?>
|
||||
<tr>
|
||||
<td><?= $t['id'] ?></td>
|
||||
<td><?= date('Y-m-d H:i', strtotime($t['created_at'])) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$types = [
|
||||
'in' => 'توريد',
|
||||
'out' => 'صرف',
|
||||
'damage' => 'تالف',
|
||||
'lend' => 'إعارة',
|
||||
'return' => 'إرجاع',
|
||||
'transfer' => 'نقل'
|
||||
];
|
||||
echo $types[$t['transaction_type']] ?? $t['transaction_type'];
|
||||
?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($t['store_name']) ?></td>
|
||||
<td class="fw-bold"><?= htmlspecialchars($t['item_name']) ?></td>
|
||||
<td dir="ltr" class="text-end fw-bold">
|
||||
<?php if ($t['transaction_type'] == 'in' || $t['transaction_type'] == 'return'): ?>
|
||||
<span class="text-success">+<?= number_format($t['quantity'], 2) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="text-danger">-<?= number_format($t['quantity'], 2) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($t['user_name'] ?? '-') ?></td>
|
||||
<td class="small text-muted">
|
||||
<?php if ($t['reference']): ?>
|
||||
<strong>المرجع:</strong> <?= htmlspecialchars($t['reference']) ?><br>
|
||||
<?php endif; ?>
|
||||
<?= htmlspecialchars($t['notes'] ?? '') ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@media print {
|
||||
.btn, .sidebar, .top-navbar, form { display: none !important; }
|
||||
.main-content { margin: 0 !important; padding: 0 !important; }
|
||||
.card { border: none !important; box-shadow: none !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
265
stock_settings.php
Normal file
265
stock_settings.php
Normal file
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
if (!canView('stock_settings')) {
|
||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||
require_once __DIR__ . '/includes/footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// Handle Actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
$type = $_POST['type'] ?? ''; // 'store' or 'category'
|
||||
$id = $_POST['id'] ?? 0;
|
||||
$name = $_POST['name'] ?? '';
|
||||
$desc = $_POST['description'] ?? ''; // location or description
|
||||
|
||||
if ($action === 'add' && canAdd('stock_settings')) {
|
||||
if ($name) {
|
||||
if ($type === 'store') {
|
||||
$stmt = db()->prepare("INSERT INTO stock_stores (name, location) VALUES (?, ?)");
|
||||
$stmt->execute([$name, $desc]);
|
||||
} else {
|
||||
$stmt = db()->prepare("INSERT INTO stock_categories (name, description) VALUES (?, ?)");
|
||||
$stmt->execute([$name, $desc]);
|
||||
}
|
||||
$success = 'تم الإضافة بنجاح';
|
||||
}
|
||||
} elseif ($action === 'edit' && canEdit('stock_settings')) {
|
||||
if ($name && $id) {
|
||||
if ($type === 'store') {
|
||||
$stmt = db()->prepare("UPDATE stock_stores SET name=?, location=? WHERE id=?");
|
||||
$stmt->execute([$name, $desc, $id]);
|
||||
} else {
|
||||
$stmt = db()->prepare("UPDATE stock_categories SET name=?, description=? WHERE id=?");
|
||||
$stmt->execute([$name, $desc, $id]);
|
||||
}
|
||||
$success = 'تم التحديث بنجاح';
|
||||
}
|
||||
} elseif ($action === 'delete' && canDelete('stock_settings')) {
|
||||
if ($id) {
|
||||
try {
|
||||
if ($type === 'store') {
|
||||
$stmt = db()->prepare("DELETE FROM stock_stores WHERE id=?");
|
||||
$stmt->execute([$id]);
|
||||
} else {
|
||||
$stmt = db()->prepare("DELETE FROM stock_categories WHERE id=?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
$success = 'تم الحذف بنجاح';
|
||||
} catch (PDOException $e) {
|
||||
$error = 'لا يمكن الحذف لوجود بيانات مرتبطة';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||
$categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">إعدادات المخزون</h1>
|
||||
</div>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= $success ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= $error ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<ul class="nav nav-tabs mb-4" id="stockSettingsTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="stores-tab" data-bs-toggle="tab" data-bs-target="#stores" type="button" role="tab">المستودعات</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="categories-tab" data-bs-toggle="tab" data-bs-target="#categories" type="button" role="tab">التصنيفات</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="stockSettingsContent">
|
||||
<!-- Stores Tab -->
|
||||
<div class="tab-pane fade show active" id="stores" role="tabpanel">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h5 class="fw-bold">قائمة المستودعات</h5>
|
||||
<?php if (canAdd('stock_settings')): ?>
|
||||
<button class="btn btn-primary btn-sm" onclick="openModal('store', 'add')">
|
||||
<i class="fas fa-plus"></i> إضافة مستودع
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>اسم المستودع</th>
|
||||
<th>الموقع / العنوان</th>
|
||||
<th>تاريخ الإضافة</th>
|
||||
<th>الإجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stores as $s): ?>
|
||||
<tr>
|
||||
<td><?= $s['id'] ?></td>
|
||||
<td class="fw-bold"><?= htmlspecialchars($s['name']) ?></td>
|
||||
<td><?= htmlspecialchars($s['location'] ?? '-') ?></td>
|
||||
<td><?= date('Y-m-d', strtotime($s['created_at'])) ?></td>
|
||||
<td>
|
||||
<?php if (canEdit('stock_settings')): ?>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick='openModal("store", "edit", <?= json_encode($s) ?>)'>
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if (canDelete('stock_settings')): ?>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="confirmDelete('store', <?= $s['id'] ?>)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Categories Tab -->
|
||||
<div class="tab-pane fade" id="categories" role="tabpanel">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h5 class="fw-bold">تصنيفات المواد</h5>
|
||||
<?php if (canAdd('stock_settings')): ?>
|
||||
<button class="btn btn-primary btn-sm" onclick="openModal('category', 'add')">
|
||||
<i class="fas fa-plus"></i> إضافة تصنيف
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>اسم التصنيف</th>
|
||||
<th>الوصف</th>
|
||||
<th>تاريخ الإضافة</th>
|
||||
<th>الإجراءات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($categories as $c): ?>
|
||||
<tr>
|
||||
<td><?= $c['id'] ?></td>
|
||||
<td class="fw-bold"><?= htmlspecialchars($c['name']) ?></td>
|
||||
<td><?= htmlspecialchars($c['description'] ?? '-') ?></td>
|
||||
<td><?= date('Y-m-d', strtotime($c['created_at'])) ?></td>
|
||||
<td>
|
||||
<?php if (canEdit('stock_settings')): ?>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick='openModal("category", "edit", <?= json_encode($c) ?>)'>
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php if (canDelete('stock_settings')): ?>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="confirmDelete('category', <?= $c['id'] ?>)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generic Modal -->
|
||||
<div class="modal fade" id="settingsModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title" id="modalTitle">إضافة</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="action" id="modalAction" value="add">
|
||||
<input type="hidden" name="type" id="modalType" value="">
|
||||
<input type="hidden" name="id" id="modalId" value="">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">الاسم <span class="text-danger">*</span></label>
|
||||
<input type="text" name="name" id="modalName" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold" id="descLabel">الوصف / الموقع</label>
|
||||
<textarea name="description" id="modalDesc" class="form-control" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||
<button type="submit" class="btn btn-primary">حفظ</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let settingsModal;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
settingsModal = new bootstrap.Modal(document.getElementById('settingsModal'));
|
||||
});
|
||||
|
||||
function openModal(type, action, data = null) {
|
||||
document.getElementById('modalType').value = type;
|
||||
document.getElementById('modalAction').value = action;
|
||||
|
||||
const typeName = type === 'store' ? 'مستودع' : 'تصنيف';
|
||||
document.getElementById('modalTitle').textContent = (action === 'add' ? 'إضافة ' : 'تعديل ') + typeName;
|
||||
document.getElementById('descLabel').textContent = type === 'store' ? 'الموقع / العنوان' : 'الوصف';
|
||||
|
||||
if (action === 'edit' && data) {
|
||||
document.getElementById('modalId').value = data.id;
|
||||
document.getElementById('modalName').value = data.name;
|
||||
document.getElementById('modalDesc').value = (type === 'store' ? data.location : data.description) || '';
|
||||
} else {
|
||||
document.getElementById('modalId').value = '';
|
||||
document.getElementById('modalName').value = '';
|
||||
document.getElementById('modalDesc').value = '';
|
||||
}
|
||||
settingsModal.show();
|
||||
}
|
||||
|
||||
function confirmDelete(type, id) {
|
||||
if(confirm('هل أنت متأكد من الحذف؟')) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.innerHTML = `<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="type" value="${type}">
|
||||
<input type="hidden" name="id" value="${id}">`;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
17
users.php
17
users.php
@ -15,7 +15,20 @@ $modules = [
|
||||
'users' => 'إدارة المستخدمين',
|
||||
'settings' => 'الإعدادات',
|
||||
'reports' => 'التقارير',
|
||||
'accounting' => 'المحاسبة'
|
||||
'accounting' => 'المحاسبة',
|
||||
'hr_dashboard' => 'HR - لوحة التحكم',
|
||||
'hr_employees' => 'HR - الموظفين',
|
||||
'hr_attendance' => 'HR - الحضور والعطلات',
|
||||
'hr_leaves' => 'HR - الإجازات',
|
||||
'hr_payroll' => 'HR - الرواتب',
|
||||
'hr_reports' => 'HR - التقارير',
|
||||
'stock_dashboard' => 'المخزون - لوحة التحكم',
|
||||
'stock_items' => 'المخزون - الأصناف',
|
||||
'stock_in' => 'المخزون - توريد (وارد)',
|
||||
'stock_out' => 'المخزون - صرف (صادر)',
|
||||
'stock_lending' => 'المخزون - الإعارة',
|
||||
'stock_reports' => 'المخزون - التقارير',
|
||||
'stock_settings' => 'المخزون - الإعدادات'
|
||||
];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
@ -384,7 +397,7 @@ function applyRolePresets(role) {
|
||||
view.checked = add.checked = edit.checked = del.checked = false;
|
||||
} else {
|
||||
view.checked = add.checked = edit.checked = true;
|
||||
del.checked = (m === 'reports' ? false : false);
|
||||
del.checked = false; // Clerk can't delete by default
|
||||
}
|
||||
} else {
|
||||
if (m === 'inbound' || m === 'outbound' || m === 'internal') {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user