216 lines
9.4 KiB
PHP
216 lines
9.4 KiB
PHP
<?php
|
|
require_once 'includes/app.php';
|
|
$user = require_auth();
|
|
|
|
$activeNav = 'debts';
|
|
$pageTitle = tr('الديون والفواتير الآجلة', 'Debts & Unpaid Bills');
|
|
$pdo = db();
|
|
$debtsLoadError = '';
|
|
$unpaidSales = [];
|
|
$debtsByCustomer = [];
|
|
|
|
// Handle Mark as Paid
|
|
if (isset($_GET['mark_paid'])) {
|
|
$id = (int)$_GET['mark_paid'];
|
|
try {
|
|
$pdo->prepare("UPDATE sales_orders SET payment_status = 'paid', status = 'completed' WHERE id = ?")->execute([$id]);
|
|
set_flash('success', tr('تم استلام المبلغ بنجاح.', 'Payment received successfully.'));
|
|
} catch (Throwable $e) {
|
|
set_flash('danger', tr('خطأ أثناء التحديث.', 'Error updating.'));
|
|
}
|
|
redirect_to('debts.php');
|
|
}
|
|
|
|
try {
|
|
$salesColumns = [];
|
|
foreach ($pdo->query('SHOW COLUMNS FROM sales_orders')->fetchAll(PDO::FETCH_ASSOC) as $column) {
|
|
$salesColumns[$column['Field']] = true;
|
|
}
|
|
|
|
$hasCustomersTable = false;
|
|
$customerColumns = [];
|
|
$customersTable = $pdo->query("SHOW TABLES LIKE 'customers'");
|
|
if ($customersTable && $customersTable->fetchColumn()) {
|
|
$hasCustomersTable = true;
|
|
foreach ($pdo->query('SHOW COLUMNS FROM customers')->fetchAll(PDO::FETCH_ASSOC) as $column) {
|
|
$customerColumns[$column['Field']] = true;
|
|
}
|
|
}
|
|
|
|
$selectParts = [
|
|
's.id',
|
|
's.receipt_no',
|
|
's.sale_date',
|
|
's.total_amount',
|
|
isset($salesColumns['customer_id']) ? 's.customer_id' : 'NULL AS customer_id',
|
|
isset($salesColumns['customer_name']) ? 's.customer_name' : 'NULL AS customer_name',
|
|
'NULL AS c_name',
|
|
'NULL AS c_phone',
|
|
];
|
|
|
|
$joinSql = '';
|
|
if ($hasCustomersTable && isset($salesColumns['customer_id']) && isset($customerColumns['id'])) {
|
|
$joinSql = ' LEFT JOIN customers c ON s.customer_id = c.id ';
|
|
if (isset($customerColumns['name'])) {
|
|
$selectParts[6] = 'c.name AS c_name';
|
|
}
|
|
if (isset($customerColumns['phone'])) {
|
|
$selectParts[7] = 'c.phone AS c_phone';
|
|
}
|
|
}
|
|
|
|
if (isset($salesColumns['payment_status'])) {
|
|
$whereSql = " WHERE s.payment_status = 'unpaid'";
|
|
} elseif (isset($salesColumns['payment_method'])) {
|
|
$whereSql = " WHERE s.payment_method = 'pay_later'";
|
|
} else {
|
|
$whereSql = ' WHERE 1 = 0';
|
|
}
|
|
|
|
$sqlUnpaid = 'SELECT ' . implode(', ', $selectParts)
|
|
. ' FROM sales_orders s'
|
|
. $joinSql
|
|
. $whereSql
|
|
. ' ORDER BY s.sale_date DESC';
|
|
$stmtUnpaid = $pdo->query($sqlUnpaid);
|
|
$unpaidSales = $stmtUnpaid ? $stmtUnpaid->fetchAll(PDO::FETCH_ASSOC) : [];
|
|
} catch (Throwable $e) {
|
|
$debtsLoadError = tr(
|
|
'تعذر تحميل صفحة الديون بسبب اختلاف في هيكل قاعدة البيانات. احفظ التعديلات ثم أنشئ نسخة جديدة وأعد التحديث.',
|
|
'The debts page could not load because the deployed database schema is older than the current code. Save these changes, create a new version, then refresh the page.'
|
|
);
|
|
}
|
|
|
|
// Aggregate by customer
|
|
foreach ($unpaidSales as $sale) {
|
|
$cId = $sale['customer_id'] ?? 'unknown';
|
|
if (!isset($debtsByCustomer[$cId])) {
|
|
$debtsByCustomer[$cId] = [
|
|
'name' => $sale['c_name'] ?: $sale['customer_name'] ?: tr('عميل غير معروف', 'Unknown Customer'),
|
|
'phone' => $sale['c_phone'] ?: '',
|
|
'total' => 0.0,
|
|
'count' => 0
|
|
];
|
|
}
|
|
$debtsByCustomer[$cId]['total'] += (float)$sale['total_amount'];
|
|
$debtsByCustomer[$cId]['count'] += 1;
|
|
}
|
|
|
|
// Sort by highest debt
|
|
uasort($debtsByCustomer, fn($a, $b) => $b['total'] <=> $a['total']);
|
|
|
|
require_once 'includes/header.php';
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h3 mb-0 text-gray-800"><?= h($pageTitle) ?></h1>
|
|
</div>
|
|
|
|
<?php if ($debtsLoadError !== ''): ?>
|
|
<div class="alert alert-warning" role="alert">
|
|
<?= h($debtsLoadError) ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="row">
|
|
<!-- Debts by Customer -->
|
|
<div class="col-lg-4 mb-4">
|
|
<div class="card shadow-sm border-0 h-100">
|
|
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
|
|
<h6 class="m-0 font-weight-bold text-primary"><i class="bi bi-people"></i> <?= h(tr('الديون حسب العميل', 'Debts by Customer')) ?></h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<?php if (empty($debtsByCustomer)): ?>
|
|
<div class="text-center text-muted py-4"><?= h(tr('لا توجد ديون مسجلة.', 'No debts recorded.')) ?></div>
|
|
<?php else: ?>
|
|
<ul class="list-group list-group-flush">
|
|
<?php foreach ($debtsByCustomer as $debt): ?>
|
|
<li class="list-group-item d-flex justify-content-between align-items-center px-0">
|
|
<div>
|
|
<strong><?= h($debt['name']) ?></strong>
|
|
<?php if ($debt['phone']): ?>
|
|
<div class="small text-muted"><?= h($debt['phone']) ?></div>
|
|
<?php endif; ?>
|
|
<div class="small text-muted"><?= h($debt['count']) ?> <?= h(tr('فواتير', 'bills')) ?></div>
|
|
</div>
|
|
<span class="badge bg-danger rounded-pill fs-6"><?= h(currency($debt['total'])) ?></span>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Unpaid Invoices -->
|
|
<div class="col-lg-8 mb-4">
|
|
<div class="card shadow-sm border-0 h-100">
|
|
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
|
|
<h6 class="m-0 font-weight-bold text-primary"><i class="bi bi-receipt"></i> <?= h(tr('الفواتير غير المدفوعة', 'Unpaid Bills')) ?></h6>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th><?= h(tr('رقم الفاتورة', 'Receipt No')) ?></th>
|
|
<th><?= h(tr('العميل', 'Customer')) ?></th>
|
|
<th><?= h(tr('التاريخ', 'Date')) ?></th>
|
|
<th><?= h(tr('المبلغ', 'Amount')) ?></th>
|
|
<th><?= h(tr('الإجراء', 'Action')) ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($unpaidSales)): ?>
|
|
<tr>
|
|
<td colspan="5" class="text-center py-4 text-muted"><?= h(tr('لا توجد فواتير غير مدفوعة.', 'No unpaid bills.')) ?></td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($unpaidSales as $sale): ?>
|
|
<tr>
|
|
<td>
|
|
<a href="<?= h(url_for('sale.php', ['id' => $sale['id']])) ?>" class="fw-bold text-decoration-none">
|
|
<?= h($sale['receipt_no']) ?>
|
|
</a>
|
|
</td>
|
|
<td>
|
|
<?= h($sale['c_name'] ?: $sale['customer_name'] ?: '-') ?>
|
|
</td>
|
|
<td><?= h(date('Y-m-d', strtotime((string)$sale['sale_date']))) ?></td>
|
|
<td class="fw-bold text-danger"><?= h(currency((float)$sale['total_amount'])) ?></td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-success rounded-pill px-3" onclick="markPaid(<?= $sale['id'] ?>)">
|
|
<i class="bi bi-cash-coin"></i> <?= h(tr('استلام', 'Receive')) ?>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function markPaid(id) {
|
|
Swal.fire({
|
|
title: "<?= h(tr('تأكيد استلام المبلغ؟', 'Confirm payment receipt?')) ?>",
|
|
text: "<?= h(tr('سيتم تحويل هذه الفاتورة إلى مدفوعة.', 'This bill will be marked as paid.')) ?>",
|
|
icon: "question",
|
|
showCancelButton: true,
|
|
confirmButtonColor: "#198754",
|
|
confirmButtonText: "<?= h(tr('نعم، تم الاستلام', 'Yes, Received')) ?>",
|
|
cancelButtonText: "<?= h(tr('إلغاء', 'Cancel')) ?>"
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
window.location.href = "debts.php?mark_paid=" + id;
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<?php require_once 'includes/footer.php'; ?>
|