341 lines
19 KiB
PHP
341 lines
19 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/app.php';
|
|
require_permission('accounting');
|
|
|
|
$errors = [];
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
verify_csrf();
|
|
$action = (string)($_POST['form_action'] ?? '');
|
|
|
|
if ($action === 'customer_payment') {
|
|
$customerId = (int)($_POST['customer_id'] ?? 0);
|
|
$amount = (float)($_POST['amount'] ?? 0);
|
|
$method = (string)($_POST['payment_method'] ?? 'bank_transfer');
|
|
$notes = trim((string)($_POST['notes'] ?? ''));
|
|
$customer = fetch_record('customer', $customerId);
|
|
|
|
if (!$customer) {
|
|
$errors[] = 'اختر عميلًا صحيحًا لتسجيل المقبوضات.';
|
|
}
|
|
if ($amount <= 0) {
|
|
$errors[] = 'أدخل مبلغ مقبوضات أكبر من صفر.';
|
|
}
|
|
if (!array_key_exists($method, payment_method_options())) {
|
|
$errors[] = 'طريقة السداد غير صحيحة.';
|
|
}
|
|
|
|
if (!$errors && $customer) {
|
|
create_record(
|
|
'customer_payment',
|
|
'مقبوضات من ' . $customer['title'],
|
|
next_code('RCPT', 'customer_payment'),
|
|
[
|
|
'customer_id' => (int)$customer['id'],
|
|
'customer_name' => $customer['title'],
|
|
'amount' => $amount,
|
|
'payment_method' => $method,
|
|
'payment_method_label' => payment_method_label($method),
|
|
'notes' => $notes,
|
|
'created_date' => date('Y-m-d H:i'),
|
|
'created_by' => current_user()['username'] ?? 'system',
|
|
],
|
|
'posted'
|
|
);
|
|
set_flash('success', 'تم ترحيل مقبوضات العميل بنجاح.');
|
|
redirect('accounting.php');
|
|
}
|
|
}
|
|
|
|
if ($action === 'supplier_payment') {
|
|
$supplierId = (int)($_POST['supplier_id'] ?? 0);
|
|
$amount = (float)($_POST['amount'] ?? 0);
|
|
$method = (string)($_POST['payment_method'] ?? 'bank_transfer');
|
|
$notes = trim((string)($_POST['notes'] ?? ''));
|
|
$supplier = fetch_record('supplier', $supplierId);
|
|
|
|
if (!$supplier) {
|
|
$errors[] = 'اختر موردًا صحيحًا لتسجيل الدفعة.';
|
|
}
|
|
if ($amount <= 0) {
|
|
$errors[] = 'أدخل مبلغ دفعة أكبر من صفر.';
|
|
}
|
|
if (!array_key_exists($method, payment_method_options())) {
|
|
$errors[] = 'طريقة السداد غير صحيحة.';
|
|
}
|
|
|
|
if (!$errors && $supplier) {
|
|
create_record(
|
|
'supplier_payment',
|
|
'دفعة إلى ' . $supplier['title'],
|
|
next_code('SPAY', 'supplier_payment'),
|
|
[
|
|
'supplier_id' => (int)$supplier['id'],
|
|
'supplier_name' => $supplier['title'],
|
|
'amount' => $amount,
|
|
'payment_method' => $method,
|
|
'payment_method_label' => payment_method_label($method),
|
|
'notes' => $notes,
|
|
'created_date' => date('Y-m-d H:i'),
|
|
'created_by' => current_user()['username'] ?? 'system',
|
|
],
|
|
'posted'
|
|
);
|
|
set_flash('success', 'تم ترحيل دفعة المورد بنجاح.');
|
|
redirect('accounting.php');
|
|
}
|
|
}
|
|
|
|
if ($action === 'expense_entry') {
|
|
$title = trim((string)($_POST['title'] ?? ''));
|
|
$category = (string)($_POST['category'] ?? 'operations');
|
|
$amount = (float)($_POST['amount'] ?? 0);
|
|
$notes = trim((string)($_POST['notes'] ?? ''));
|
|
|
|
if ($title === '') {
|
|
$errors[] = 'أدخل اسم المصروف.';
|
|
}
|
|
if ($amount <= 0) {
|
|
$errors[] = 'أدخل مبلغ مصروف أكبر من صفر.';
|
|
}
|
|
if (!array_key_exists($category, expense_category_options())) {
|
|
$errors[] = 'تصنيف المصروف غير صحيح.';
|
|
}
|
|
|
|
if (!$errors) {
|
|
create_record(
|
|
'expense_entry',
|
|
$title,
|
|
next_code('EXP', 'expense_entry'),
|
|
[
|
|
'category' => $category,
|
|
'category_label' => expense_category_label($category),
|
|
'amount' => $amount,
|
|
'notes' => $notes,
|
|
'created_date' => date('Y-m-d H:i'),
|
|
'created_by' => current_user()['username'] ?? 'system',
|
|
],
|
|
'posted'
|
|
);
|
|
set_flash('success', 'تم ترحيل المصروف بنجاح.');
|
|
redirect('accounting.php');
|
|
}
|
|
}
|
|
}
|
|
|
|
$counts = fetch_counts();
|
|
$summary = accounting_summary();
|
|
$customerStatements = customer_statement_rows();
|
|
$supplierStatements = supplier_statement_rows();
|
|
$customerPayments = fetch_records('customer_payment');
|
|
$supplierPayments = fetch_records('supplier_payment');
|
|
$expenses = fetch_records('expense_entry');
|
|
$activity = recent_accounting_activity(10);
|
|
$customers = customer_dataset();
|
|
$suppliers = supplier_dataset();
|
|
|
|
render_header('المحاسبة والتقارير المالية', 'إدارة المقبوضات والمدفوعات والمصروفات وكشوف حساب العملاء والموردين مع مؤشرات ربح وتدفق نقدي.', 'accounting');
|
|
?>
|
|
<section class="hero-panel mb-4">
|
|
<div class="row g-4 align-items-center">
|
|
<div class="col-lg-8">
|
|
<span class="eyebrow">Accounting & Statements</span>
|
|
<h1 class="hero-title">محاسبة تشغيلية مرتبطة بالمبيعات والمشتريات.</h1>
|
|
<p class="hero-copy">تسحب هذه الصفحة الذمم المدينة من <strong>فواتير المبيعات</strong>، والذمم الدائنة من <strong>أوامر الشراء المستلمة</strong>، ثم تخصم منها المقبوضات والمدفوعات والمصروفات لتكوين صورة مالية فورية.</p>
|
|
</div>
|
|
<div class="col-lg-4">
|
|
<div class="hero-side-card h-100">
|
|
<div class="metric-label">الربح المتوقع</div>
|
|
<div class="metric-value small-money"><?= e(format_money((float)$summary['expected_profit'])) ?></div>
|
|
<div class="small text-secondary mb-2">صافي التدفق النقدي: <?= e(format_money((float)$summary['net_cashflow'])) ?></div>
|
|
<div class="small text-secondary">المقبوضات اليوم: <?= e(format_money((float)$summary['today_receipts'])) ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row g-3 mb-4">
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">فواتير المبيعات</div><div class="stat-value"><?= e(format_money((float)$summary['sales_invoices'])) ?></div><div class="stat-note"><?= e((string)($counts['sales_invoice'] ?? 0)) ?> فاتورة</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">مشتريات مستلمة</div><div class="stat-value"><?= e(format_money((float)$summary['purchase_commitments'])) ?></div><div class="stat-note">مرتبطة بالموردين</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">مقبوضات العملاء</div><div class="stat-value"><?= e(format_money((float)$summary['customer_receipts'])) ?></div><div class="stat-note"><?= e((string)($counts['customer_payment'] ?? 0)) ?> حركة</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">مدفوعات الموردين</div><div class="stat-value"><?= e(format_money((float)$summary['supplier_payments'])) ?></div><div class="stat-note"><?= e((string)($counts['supplier_payment'] ?? 0)) ?> حركة</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">المصروفات</div><div class="stat-value"><?= e(format_money((float)$summary['expenses'])) ?></div><div class="stat-note"><?= e((string)($counts['expense_entry'] ?? 0)) ?> قيد</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">ذمم العملاء</div><div class="stat-value"><?= e(format_money((float)$summary['receivables'])) ?></div><div class="stat-note">قابلة للتحصيل</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">ذمم الموردين</div><div class="stat-value"><?= e(format_money((float)$summary['payables'])) ?></div><div class="stat-note">واجب سدادها</div></div></div>
|
|
<div class="col-6 col-xl-3"><div class="stat-card"><div class="stat-label">صافي التدفق النقدي</div><div class="stat-value"><?= e(format_money((float)$summary['net_cashflow'])) ?></div><div class="stat-note">مقبوضات - مدفوعات - مصروفات</div></div></div>
|
|
</section>
|
|
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-danger py-2 mb-4"><?php foreach ($errors as $error): ?><div><?= e($error) ?></div><?php endforeach; ?></div>
|
|
<?php endif; ?>
|
|
|
|
<section class="row g-4 mb-4">
|
|
<div class="col-xl-4">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header compact"><div><h2 class="section-title">تسجيل مقبوضات عميل</h2><p class="section-copy">تُخصم تلقائيًا من رصيد العميل المستحق.</p></div></div>
|
|
<form method="post" class="vstack gap-3">
|
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
|
<input type="hidden" name="form_action" value="customer_payment">
|
|
<div>
|
|
<label class="form-label">العميل</label>
|
|
<select class="form-select" name="customer_id" required>
|
|
<option value="">اختر العميل</option>
|
|
<?php foreach ($customers as $customer): ?>
|
|
<option value="<?= (int)$customer['id'] ?>"><?= e($customer['name']) ?> — <?= e($customer['code']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-6"><label class="form-label">المبلغ</label><input type="number" min="0.01" step="0.01" class="form-control" name="amount" required></div>
|
|
<div class="col-md-6"><label class="form-label">طريقة السداد</label><select class="form-select" name="payment_method"><?php foreach (payment_method_options() as $key => $label): ?><option value="<?= e($key) ?>"><?= e($label) ?></option><?php endforeach; ?></select></div>
|
|
</div>
|
|
<div><label class="form-label">ملاحظات</label><textarea class="form-control" name="notes" rows="3"></textarea></div>
|
|
<button class="btn btn-dark" type="submit">ترحيل المقبوضات</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-4">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header compact"><div><h2 class="section-title">تسجيل دفعة مورد</h2><p class="section-copy">تُخصم من الذمم الدائنة على المورد مباشرة.</p></div></div>
|
|
<form method="post" class="vstack gap-3">
|
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
|
<input type="hidden" name="form_action" value="supplier_payment">
|
|
<div>
|
|
<label class="form-label">المورد</label>
|
|
<select class="form-select" name="supplier_id" required>
|
|
<option value="">اختر المورد</option>
|
|
<?php foreach ($suppliers as $supplier): ?>
|
|
<option value="<?= (int)$supplier['id'] ?>"><?= e($supplier['name']) ?> — <?= e($supplier['code']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-6"><label class="form-label">المبلغ</label><input type="number" min="0.01" step="0.01" class="form-control" name="amount" required></div>
|
|
<div class="col-md-6"><label class="form-label">طريقة السداد</label><select class="form-select" name="payment_method"><?php foreach (payment_method_options() as $key => $label): ?><option value="<?= e($key) ?>"><?= e($label) ?></option><?php endforeach; ?></select></div>
|
|
</div>
|
|
<div><label class="form-label">ملاحظات</label><textarea class="form-control" name="notes" rows="3"></textarea></div>
|
|
<button class="btn btn-dark" type="submit">ترحيل الدفعة</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-4">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header compact"><div><h2 class="section-title">إضافة مصروف</h2><p class="section-copy">يؤثر مباشرة على الربح المتوقع والتدفق النقدي.</p></div></div>
|
|
<form method="post" class="vstack gap-3">
|
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
|
<input type="hidden" name="form_action" value="expense_entry">
|
|
<div><label class="form-label">اسم المصروف</label><input type="text" class="form-control" name="title" required></div>
|
|
<div class="row g-3">
|
|
<div class="col-md-6"><label class="form-label">التصنيف</label><select class="form-select" name="category"><?php foreach (expense_category_options() as $key => $label): ?><option value="<?= e($key) ?>"><?= e($label) ?></option><?php endforeach; ?></select></div>
|
|
<div class="col-md-6"><label class="form-label">المبلغ</label><input type="number" min="0.01" step="0.01" class="form-control" name="amount" required></div>
|
|
</div>
|
|
<div><label class="form-label">ملاحظات</label><textarea class="form-control" name="notes" rows="3"></textarea></div>
|
|
<button class="btn btn-dark" type="submit">ترحيل المصروف</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row g-4 mb-4">
|
|
<div class="col-lg-6">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header"><div><h2 class="section-title">كشف حساب العملاء</h2><p class="section-copy">الرصيد = فواتير المبيعات - المقبوضات المسجلة.</p></div></div>
|
|
<?php if ($customerStatements): ?>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle app-table">
|
|
<thead><tr><th>العميل</th><th>الفواتير</th><th>المقبوضات</th><th>الرصيد</th></tr></thead>
|
|
<tbody>
|
|
<?php foreach ($customerStatements as $row): ?>
|
|
<tr>
|
|
<td><?= e($row['name']) ?><div class="small text-secondary"><?= e($row['code']) ?></div></td>
|
|
<td><?= e(format_money((float)$row['invoices'])) ?></td>
|
|
<td><?= e(format_money((float)$row['payments'])) ?></td>
|
|
<td><strong><?= e(format_money((float)$row['balance'])) ?></strong></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="empty-inline">لا توجد بيانات عملاء لعرض كشف الحساب.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header"><div><h2 class="section-title">كشف حساب الموردين</h2><p class="section-copy">الرصيد = مشتريات مستلمة - مدفوعات الموردين.</p></div></div>
|
|
<?php if ($supplierStatements): ?>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle app-table">
|
|
<thead><tr><th>المورد</th><th>المشتريات</th><th>المدفوعات</th><th>الرصيد</th></tr></thead>
|
|
<tbody>
|
|
<?php foreach ($supplierStatements as $row): ?>
|
|
<tr>
|
|
<td><?= e($row['name']) ?><div class="small text-secondary"><?= e($row['code']) ?></div></td>
|
|
<td><?= e(format_money((float)$row['purchases'])) ?></td>
|
|
<td><?= e(format_money((float)$row['payments'])) ?></td>
|
|
<td><strong><?= e(format_money((float)$row['balance'])) ?></strong></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="empty-inline">لا توجد بيانات موردين لعرض كشف الحساب.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row g-4 mb-4">
|
|
<div class="col-lg-5">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header"><div><h2 class="section-title">آخر نشاط محاسبي</h2><p class="section-copy">يشمل المقبوضات والمدفوعات والمصروفات في سجل سريع.</p></div></div>
|
|
<?php if ($activity): ?>
|
|
<div class="vstack gap-2">
|
|
<?php foreach ($activity as $row): $payload = $row['payload_data']; ?>
|
|
<div class="list-row">
|
|
<div>
|
|
<strong><?= e($row['title']) ?></strong>
|
|
<div class="small text-secondary"><?= e($row['code']) ?> — <?= e(substr((string)($row['created_at'] ?? ''), 0, 16)) ?></div>
|
|
</div>
|
|
<div class="text-start">
|
|
<span class="badge <?= e(status_badge_class((string)$row['status'])) ?> mb-1"><?= e(order_status_label((string)$row['status'])) ?></span>
|
|
<div class="small"><?= e(format_money((float)($payload['amount'] ?? 0))) ?></div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="empty-inline">لا توجد قيود محاسبية مرحّلة بعد.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-7">
|
|
<div class="panel-card h-100">
|
|
<div class="section-header"><div><h2 class="section-title">ملخص القيود المرحّلة</h2><p class="section-copy">آخر الحركات المالية المسجلة يدويًا داخل النظام.</p></div></div>
|
|
<?php if ($customerPayments || $supplierPayments || $expenses): ?>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle app-table">
|
|
<thead><tr><th>النوع</th><th>المرجع</th><th>الطرف</th><th>التفصيل</th><th>المبلغ</th></tr></thead>
|
|
<tbody>
|
|
<?php foreach ($activity as $row): $payload = $row['payload_data']; ?>
|
|
<tr>
|
|
<td><?= e(match ((string)$row['record_type']) { 'customer_payment' => 'مقبوضات', 'supplier_payment' => 'مدفوعات', default => 'مصروف' }) ?></td>
|
|
<td><?= e($row['code']) ?></td>
|
|
<td><?= e($payload['customer_name'] ?? $payload['supplier_name'] ?? 'مصروف داخلي') ?></td>
|
|
<td><?= e($payload['payment_method_label'] ?? $payload['category_label'] ?? '') ?><?php if (!empty($payload['notes'])): ?><div class="small text-secondary"><?= e($payload['notes']) ?></div><?php endif; ?></td>
|
|
<td><?= e(format_money((float)($payload['amount'] ?? 0))) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="empty-inline">لم يتم تسجيل مقبوضات أو مدفوعات أو مصروفات بعد.</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<?php render_footer(); ?>
|