Autosave: 20260311-175406
This commit is contained in:
parent
36d1266f5e
commit
6ede6271b7
146
accounting.php
Normal file
146
accounting.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
require_once 'includes/accounting_functions.php';
|
||||||
|
|
||||||
|
// Check permission
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
$stmt = db()->prepare("SELECT * FROM user_permissions WHERE user_id = ? AND page = 'accounting' AND can_view = 1");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
echo "<div class='container mt-4' dir='rtl'>لا تملك صلاحية الوصول لهذه الصفحة.</div>";
|
||||||
|
require_once 'includes/footer.php';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle form submission
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_entry'])) {
|
||||||
|
$date = $_POST['date'];
|
||||||
|
$description = $_POST['description'];
|
||||||
|
$reference = $_POST['reference'];
|
||||||
|
$account = $_POST['account'];
|
||||||
|
$debit = (float)$_POST['debit'];
|
||||||
|
$credit = (float)$_POST['credit'];
|
||||||
|
|
||||||
|
$entries = [['account' => $account, 'debit' => $debit, 'credit' => $credit]];
|
||||||
|
|
||||||
|
if (add_journal_entry($date, $description, $reference, $entries)) {
|
||||||
|
$message = "تم إضافة القيد بنجاح.";
|
||||||
|
} else {
|
||||||
|
$error = "حدث خطأ أثناء إضافة القيد.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ledger = get_full_ledger();
|
||||||
|
$trial_balance = get_trial_balance();
|
||||||
|
$balance_sheet = get_balance_sheet();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container mt-4" dir="rtl">
|
||||||
|
<h2 class="text-right">المحاسبة (Accounting)</h2>
|
||||||
|
|
||||||
|
<?php if (isset($message)) echo "<div class='alert alert-success'>$message</div>"; ?>
|
||||||
|
<?php if (isset($error)) echo "<div class='alert alert-danger'>$error</div>"; ?>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card bg-primary text-white text-center mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5>الأصول</h5>
|
||||||
|
<h3><?= number_format($balance_sheet['Assets'], 2) ?></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card bg-danger text-white text-center mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5>الخصوم</h5>
|
||||||
|
<h3><?= number_format($balance_sheet['Liabilities'], 2) ?></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card bg-success text-white text-center mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5>حقوق الملكية</h5>
|
||||||
|
<h3><?= number_format($balance_sheet['Equity'], 2) ?></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card bg-info text-white text-center mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5>صافي الربح/الخسارة</h5>
|
||||||
|
<h3><?= number_format($balance_sheet['Revenue'] - $balance_sheet['Expenses'], 2) ?></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">إضافة قيد محاسبي جديد</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="add_entry" value="1">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label>التاريخ</label>
|
||||||
|
<input type="date" name="date" class="form-control" required value="<?= date('Y-m-d') ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label>الوصف</label>
|
||||||
|
<input type="text" name="description" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label>المرجع</label>
|
||||||
|
<input type="text" name="reference" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label>الحساب</label>
|
||||||
|
<select name="account" class="form-control" required>
|
||||||
|
<?php foreach (get_all_accounts() as $acc): ?>
|
||||||
|
<option value="<?= htmlspecialchars($acc['name']) ?>"><?= htmlspecialchars($acc['name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1">
|
||||||
|
<label>مدين</label>
|
||||||
|
<input type="number" step="0.01" name="debit" class="form-control" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1">
|
||||||
|
<label>دائن</label>
|
||||||
|
<input type="number" step="0.01" name="credit" class="form-control" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1 d-flex align-items-end">
|
||||||
|
<button type="submit" class="btn btn-primary">حفظ</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h3 class="text-right">دفتر الأستاذ (General Ledger)</h3>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered text-right">
|
||||||
|
<thead><tr><th>التاريخ</th><th>الوصف</th><th>المرجع</th><th>الحساب</th><th>مدين</th><th>دائن</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($ledger as $row): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($row['date']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($row['description']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($row['reference']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($row['account_name']) ?></td>
|
||||||
|
<td><?= number_format($row['debit'], 2) ?></td>
|
||||||
|
<td><?= number_format($row['credit'], 2) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
74
accounts.php
Normal file
74
accounts.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
require_once 'includes/accounting_functions.php';
|
||||||
|
|
||||||
|
// Check permission
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
$stmt = db()->prepare("SELECT * FROM user_permissions WHERE user_id = ? AND page = 'accounting' AND can_view = 1");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
echo "<div class='container mt-4' dir='rtl'>لا تملك صلاحية الوصول لهذه الصفحة.</div>";
|
||||||
|
require_once 'includes/footer.php';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle form submission
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_account'])) {
|
||||||
|
$name = $_POST['name'];
|
||||||
|
$type = $_POST['type']; // Assets, Liabilities, Equity, Revenue, Expenses
|
||||||
|
$stmt = db()->prepare("INSERT INTO accounting_accounts (name, type) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$name, $type]);
|
||||||
|
$message = "تم إضافة الحساب بنجاح.";
|
||||||
|
}
|
||||||
|
|
||||||
|
$accounts = db()->query("SELECT * FROM accounting_accounts ORDER BY type, name")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container mt-4" dir="rtl">
|
||||||
|
<h2 class="text-right">دليل الحسابات (Chart of Accounts)</h2>
|
||||||
|
|
||||||
|
<?php if (isset($message)) echo "<div class='alert alert-success'>$message</div>"; ?>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header">إضافة حساب جديد</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="add_account" value="1">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-5">
|
||||||
|
<label>اسم الحساب</label>
|
||||||
|
<input type="text" name="name" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<label>نوع الحساب</label>
|
||||||
|
<select name="type" class="form-control" required>
|
||||||
|
<option value="Assets">أصول (Assets)</option>
|
||||||
|
<option value="Liabilities">خصوم (Liabilities)</option>
|
||||||
|
<option value="Equity">حقوق ملكية (Equity)</option>
|
||||||
|
<option value="Revenue">إيرادات (Revenue)</option>
|
||||||
|
<option value="Expenses">مصروفات (Expenses)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-2 d-flex align-items-end">
|
||||||
|
<button type="submit" class="btn btn-primary">إضافة</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-bordered text-right">
|
||||||
|
<thead><tr><th>الاسم</th><th>النوع</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($accounts as $account): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($account['name']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($account['type']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
26
db/migrations/018_add_accounting_module.sql
Normal file
26
db/migrations/018_add_accounting_module.sql
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
-- Accounting module tables
|
||||||
|
CREATE TABLE IF NOT EXISTS accounting_journal (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
date DATE NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
reference VARCHAR(255),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS accounting_entries (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
journal_id INT NOT NULL,
|
||||||
|
account_name VARCHAR(255) NOT NULL,
|
||||||
|
debit DECIMAL(15,2) DEFAULT 0.00,
|
||||||
|
credit DECIMAL(15,2) DEFAULT 0.00,
|
||||||
|
FOREIGN KEY (journal_id) REFERENCES accounting_journal(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Register accounting module in user permissions
|
||||||
|
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
|
||||||
|
SELECT id, 'accounting',
|
||||||
|
IF(role = 'admin', 1, 0),
|
||||||
|
IF(role = 'admin', 1, 0),
|
||||||
|
IF(role = 'admin', 1, 0),
|
||||||
|
IF(role = 'admin', 1, 0)
|
||||||
|
FROM users;
|
||||||
5
db/migrations/019_add_accounts_table.sql
Normal file
5
db/migrations/019_add_accounts_table.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS accounting_accounts (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
type ENUM('Assets', 'Liabilities', 'Equity', 'Revenue', 'Expenses') NOT NULL
|
||||||
|
);
|
||||||
67
includes/accounting_functions.php
Normal file
67
includes/accounting_functions.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
function get_journal_entries() {
|
||||||
|
$db = db();
|
||||||
|
$stmt = $db->query("SELECT j.*, SUM(e.debit) as total_debit, SUM(e.credit) as total_credit
|
||||||
|
FROM accounting_journal j
|
||||||
|
LEFT JOIN accounting_entries e ON j.id = e.journal_id
|
||||||
|
GROUP BY j.id ORDER BY j.date DESC");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_full_ledger() {
|
||||||
|
$db = db();
|
||||||
|
$stmt = $db->query("SELECT j.date, j.description, j.reference, e.account_name, e.debit, e.credit
|
||||||
|
FROM accounting_journal j
|
||||||
|
JOIN accounting_entries e ON j.id = e.journal_id
|
||||||
|
ORDER BY j.date DESC, j.id DESC");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_trial_balance() {
|
||||||
|
$db = db();
|
||||||
|
$stmt = $db->query("SELECT account_name, SUM(debit) as total_debit, SUM(credit) as total_credit
|
||||||
|
FROM accounting_entries
|
||||||
|
GROUP BY account_name");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_balance_sheet() {
|
||||||
|
$db = db();
|
||||||
|
$stmt = $db->query("SELECT a.name, a.type, SUM(e.debit - e.credit) as balance
|
||||||
|
FROM accounting_accounts a
|
||||||
|
LEFT JOIN accounting_entries e ON a.name = e.account_name
|
||||||
|
GROUP BY a.name, a.type");
|
||||||
|
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$sheet = ['Assets' => 0, 'Liabilities' => 0, 'Equity' => 0, 'Revenue' => 0, 'Expenses' => 0];
|
||||||
|
foreach($data as $row) {
|
||||||
|
$sheet[$row['type']] += $row['balance'];
|
||||||
|
}
|
||||||
|
return $sheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_all_accounts() {
|
||||||
|
return db()->query("SELECT * FROM accounting_accounts ORDER BY type, name")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_journal_entry($date, $description, $reference, $entries) {
|
||||||
|
$db = db();
|
||||||
|
$db->beginTransaction();
|
||||||
|
try {
|
||||||
|
$stmt = $db->prepare("INSERT INTO accounting_journal (date, description, reference) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$date, $description, $reference]);
|
||||||
|
$journal_id = $db->lastInsertId();
|
||||||
|
|
||||||
|
$stmt = $db->prepare("INSERT INTO accounting_entries (journal_id, account_name, debit, credit) VALUES (?, ?, ?, ?)");
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$stmt->execute([$journal_id, $entry['account'], $entry['debit'], $entry['credit']]);
|
||||||
|
}
|
||||||
|
$db->commit();
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@ -342,6 +342,20 @@ if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename(
|
|||||||
</li>
|
</li>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?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']) == 'accounts.php' ? 'active' : '' ?>" href="accounts.php">
|
||||||
|
<i class="fas fa-list-ul me-2"></i> دليل الحسابات
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (canView('reports')): ?>
|
<?php if (canView('reports')): ?>
|
||||||
<div class="sidebar-heading">التقارير</div>
|
<div class="sidebar-heading">التقارير</div>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|||||||
32
scripts/seed_accounts.php
Normal file
32
scripts/seed_accounts.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$common_accounts = [
|
||||||
|
['Cash', 'Assets'],
|
||||||
|
['Accounts Receivable', 'Assets'],
|
||||||
|
['Inventory', 'Assets'],
|
||||||
|
['Fixed Assets', 'Assets'],
|
||||||
|
['Accounts Payable', 'Liabilities'],
|
||||||
|
['Short-term Loans', 'Liabilities'],
|
||||||
|
['Long-term Loans', 'Liabilities'],
|
||||||
|
['Capital', 'Equity'],
|
||||||
|
['Retained Earnings', 'Equity'],
|
||||||
|
['Sales Revenue', 'Revenue'],
|
||||||
|
['Other Revenue', 'Revenue'],
|
||||||
|
['Cost of Goods Sold', 'Expenses'],
|
||||||
|
['Salaries Expense', 'Expenses'],
|
||||||
|
['Rent Expense', 'Expenses'],
|
||||||
|
['Utilities Expense', 'Expenses'],
|
||||||
|
['Marketing Expense', 'Expenses']
|
||||||
|
];
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT IGNORE INTO accounting_accounts (name, type) VALUES (?, ?)");
|
||||||
|
|
||||||
|
foreach ($common_accounts as $account) {
|
||||||
|
$stmt->execute($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Common accounts have been added.";
|
||||||
|
?>
|
||||||
Loading…
x
Reference in New Issue
Block a user