Autosave: 20260216-101855

This commit is contained in:
Flatlogic Bot 2026-02-16 10:18:55 +00:00
parent caf37ba2c8
commit fb988b3f1a
3 changed files with 232 additions and 20 deletions

252
index.php
View File

@ -537,6 +537,60 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$page = $_GET['page'] ?? 'dashboard';
$data = [];
if ($page === 'export') {
$type = $_GET['type'] ?? 'sales';
$filename = $type . "_export_" . date('Y-m-d') . ".csv";
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $filename);
$output = fopen('php://output', 'w');
// Add UTF-8 BOM for Excel
fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
if ($type === 'sales' || $type === 'purchases') {
$invType = ($type === 'sales') ? 'sale' : 'purchase';
$where = ["v.type = ?"];
$params = [$invType];
if (!empty($_GET['search'])) { $where[] = "(v.id LIKE ? OR c.name LIKE ?)"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; }
if (!empty($_GET['customer_id'])) { $where[] = "v.customer_id = ?"; $params[] = $_GET['customer_id']; }
if (!empty($_GET['start_date'])) { $where[] = "v.invoice_date >= ?"; $params[] = $_GET['start_date']; }
if (!empty($_GET['end_date'])) { $where[] = "v.invoice_date <= ?"; $params[] = $_GET['end_date']; }
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT v.id, c.name as customer_name, v.invoice_date, v.payment_type, v.total_amount, v.vat_amount, v.total_with_vat
FROM invoices v LEFT JOIN customers c ON v.customer_id = c.id
WHERE $whereSql ORDER BY v.id DESC");
$stmt->execute($params);
fputcsv($output, ['Invoice ID', 'Customer/Supplier', 'Date', 'Payment', 'Subtotal', 'VAT', 'Total']);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) fputcsv($output, $row);
} elseif ($type === 'customers' || $type === 'suppliers') {
$custType = ($type === 'suppliers') ? 'supplier' : 'customer';
$where = ["type = ?"];
$params = [$custType];
if (!empty($_GET['search'])) { $where[] = "(name LIKE ? OR email LIKE ? OR phone LIKE ?)"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; }
if (!empty($_GET['start_date'])) { $where[] = "DATE(created_at) >= ?"; $params[] = $_GET['start_date']; }
if (!empty($_GET['end_date'])) { $where[] = "DATE(created_at) <= ?"; $params[] = $_GET['end_date']; }
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT id, name, email, phone, balance, created_at FROM customers WHERE $whereSql ORDER BY id DESC");
$stmt->execute($params);
fputcsv($output, ['ID', 'Name', 'Email', 'Phone', 'Balance', 'Created At']);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) fputcsv($output, $row);
} elseif ($type === 'items') {
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) { $where[] = "(i.name_en LIKE ? OR i.name_ar LIKE ? OR i.sku LIKE ?)"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; $params[] = "%{$_GET['search']}%"; }
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT i.sku, i.name_en, i.name_ar, c.name_en as category, i.purchase_price, i.sale_price, i.stock_quantity, i.vat_rate
FROM stock_items i LEFT JOIN stock_categories c ON i.category_id = c.id
WHERE $whereSql ORDER BY i.id DESC");
$stmt->execute($params);
fputcsv($output, ['SKU', 'Name (EN)', 'Name (AR)', 'Category', 'Purchase Price', 'Sale Price', 'Quantity', 'VAT %']);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) fputcsv($output, $row);
}
fclose($output);
exit;
}
// Global data for modals
$data['categories'] = db()->query("SELECT * FROM stock_categories ORDER BY name_en ASC")->fetchAll();
$data['units'] = db()->query("SELECT * FROM stock_units ORDER BY name_en ASC")->fetchAll();
@ -550,10 +604,28 @@ foreach ($settings_raw as $s) {
switch ($page) {
case 'suppliers':
$data['customers'] = db()->query("SELECT * FROM customers WHERE type = 'supplier' ORDER BY id DESC")->fetchAll();
break;
case 'customers':
$data['customers'] = db()->query("SELECT * FROM customers WHERE type = 'customer' ORDER BY id DESC")->fetchAll();
$type = ($page === 'suppliers') ? 'supplier' : 'customer';
$where = ["type = ?"];
$params = [$type];
if (!empty($_GET['search'])) {
$where[] = "(name LIKE ? OR email LIKE ? OR phone LIKE ?)";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
}
if (!empty($_GET['start_date'])) {
$where[] = "DATE(created_at) >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "DATE(created_at) <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT * FROM customers WHERE $whereSql ORDER BY id DESC");
$stmt->execute($params);
$data['customers'] = $stmt->fetchAll();
break;
case 'categories':
// Already fetched globally
@ -562,12 +634,24 @@ switch ($page) {
// Already fetched globally
break;
case 'items':
$data['items'] = db()->query("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar, u.short_name_en as unit_en, u.short_name_ar as unit_ar, s.name as supplier_name
$where = ["1=1"];
$params = [];
if (!empty($_GET['search'])) {
$where[] = "(i.name_en LIKE ? OR i.name_ar LIKE ? OR i.sku LIKE ?)";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT i.*, c.name_en as cat_en, c.name_ar as cat_ar, u.short_name_en as unit_en, u.short_name_ar as unit_ar, s.name as supplier_name
FROM stock_items i
LEFT JOIN stock_categories c ON i.category_id = c.id
LEFT JOIN stock_units u ON i.unit_id = u.id
LEFT JOIN customers s ON i.supplier_id = s.id
ORDER BY i.id DESC")->fetchAll();
WHERE $whereSql
ORDER BY i.id DESC");
$stmt->execute($params);
$data['items'] = $stmt->fetchAll();
break;
case 'payment_methods':
$data['payment_methods'] = db()->query("SELECT * FROM payment_methods ORDER BY id DESC")->fetchAll();
@ -578,11 +662,40 @@ switch ($page) {
case 'sales':
case 'purchases':
$type = ($page === 'sales') ? 'sale' : 'purchase';
$data['invoices'] = db()->query("SELECT v.*, c.name as customer_name
FROM invoices v
LEFT JOIN customers c ON v.customer_id = c.id
WHERE v.type = '$type'
ORDER BY v.id DESC")->fetchAll();
$where = ["v.type = ?"];
$params = [$type];
if (!empty($_GET['search'])) {
$where[] = "(v.id LIKE ? OR c.name LIKE ?)";
$params[] = "%{$_GET['search']}%";
$params[] = "%{$_GET['search']}%";
}
if (!empty($_GET['customer_id'])) {
$where[] = "v.customer_id = ?";
$params[] = $_GET['customer_id'];
}
if (!empty($_GET['start_date'])) {
$where[] = "v.invoice_date >= ?";
$params[] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "v.invoice_date <= ?";
$params[] = $_GET['end_date'];
}
$whereSql = implode(" AND ", $where);
$stmt = db()->prepare("SELECT v.*, c.name as customer_name
FROM invoices v
LEFT JOIN customers c ON v.customer_id = c.id
WHERE $whereSql
ORDER BY v.id DESC");
$stmt->execute($params);
$data['invoices'] = $stmt->fetchAll();
$data['items_list'] = db()->query("SELECT id, name_en, name_ar, sale_price, purchase_price, stock_quantity, vat_rate FROM stock_items ORDER BY name_en ASC")->fetchAll();
$data['customers_list'] = db()->query("SELECT id, name FROM customers WHERE type = '" . ($type === 'sale' ? 'customer' : 'supplier') . "' ORDER BY name ASC")->fetchAll();
break;
@ -845,6 +958,38 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</button>
</div>
</div>
<!-- Search & Filter Bar -->
<div class="bg-light p-3 rounded mb-4">
<form method="GET" class="row g-2 align-items-end">
<input type="hidden" name="page" value="<?= $page ?>">
<div class="col-md-4">
<label class="form-label small" data-en="Search" data-ar="بحث">Search</label>
<input type="text" name="search" class="form-control" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>" placeholder="Name, email, or phone..." data-en="Name, email, or phone..." data-ar="الاسم، البريد، أو الهاتف...">
</div>
<div class="col-md-2">
<label class="form-label small" data-en="From Date" data-ar="من تاريخ">From Date</label>
<input type="date" name="start_date" class="form-control" value="<?= htmlspecialchars($_GET['start_date'] ?? '') ?>">
</div>
<div class="col-md-2">
<label class="form-label small" data-en="To Date" data-ar="إلى تاريخ">To Date</label>
<input type="date" name="end_date" class="form-control" value="<?= htmlspecialchars($_GET['end_date'] ?? '') ?>">
</div>
<div class="col-md-4 d-flex gap-1">
<button type="submit" class="btn btn-primary flex-grow-1">
<i class="bi bi-filter"></i> <span data-en="Filter" data-ar="تصفية">Filter</span>
</button>
<a href="index.php?page=export&type=<?= $page ?>&<?= http_build_query($_GET) ?>" class="btn btn-outline-success">
<i class="bi bi-download"></i> <span data-en="Export" data-ar="تصدير">Export</span>
</a>
<?php if (!empty($_GET['search']) || !empty($_GET['start_date']) || !empty($_GET['end_date'])): ?>
<a href="index.php?page=<?= $page ?>" class="btn btn-outline-secondary">
<i class="bi bi-x-lg"></i>
</a>
<?php endif; ?>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
@ -1003,6 +1148,30 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</button>
</div>
</div>
<!-- Search Bar -->
<div class="bg-light p-3 rounded mb-4">
<form method="GET" class="row g-2 align-items-end">
<input type="hidden" name="page" value="items">
<div class="col-md-7">
<label class="form-label small" data-en="Search" data-ar="بحث">Search</label>
<input type="text" name="search" class="form-control" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>" placeholder="Name or SKU..." data-en="Name or SKU..." data-ar="الاسم أو الباركود...">
</div>
<div class="col-md-5 d-flex gap-1">
<button type="submit" class="btn btn-primary flex-grow-1">
<i class="bi bi-search"></i> <span data-en="Search" data-ar="بحث">Search</span>
</button>
<a href="index.php?page=export&type=items&<?= http_build_query($_GET) ?>" class="btn btn-outline-success">
<i class="bi bi-download"></i> <span data-en="Export" data-ar="تصدير">Export</span>
</a>
<?php if (!empty($_GET['search'])): ?>
<a href="index.php?page=items" class="btn btn-outline-secondary">
<i class="bi bi-x-lg"></i>
</a>
<?php endif; ?>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
@ -1203,6 +1372,45 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<i class="bi bi-plus-lg"></i> <span data-en="Create New Invoice" data-ar="إنشاء فاتورة جديدة">Create New Invoice</span>
</button>
</div>
<!-- Filters Section -->
<div class="bg-light p-3 rounded mb-4">
<form method="GET" class="row g-3">
<input type="hidden" name="page" value="<?= $page ?>">
<div class="col-md-3">
<label class="form-label small fw-bold" data-en="Search" data-ar="بحث">Search</label>
<input type="text" name="search" class="form-control form-control-sm" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>" placeholder="Inv # or Name...">
</div>
<div class="col-md-3">
<label class="form-label small fw-bold" data-en="<?= $page === 'sales' ? 'Customer' : 'Supplier' ?>" data-ar="<?= $page === 'sales' ? 'العميل' : 'المورد' ?>"><?= $page === 'sales' ? 'Customer' : 'Supplier' ?></label>
<select name="customer_id" class="form-select form-select-sm">
<option value="" data-en="All" data-ar="الكل">All</option>
<?php foreach ($data['customers_list'] as $c): ?>
<option value="<?= $c['id'] ?>" <?= (($_GET['customer_id'] ?? '') == $c['id']) ? 'selected' : '' ?>><?= htmlspecialchars($c['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold" data-en="Start Date" data-ar="من تاريخ">Start Date</label>
<input type="date" name="start_date" class="form-control form-control-sm" value="<?= htmlspecialchars($_GET['start_date'] ?? '') ?>">
</div>
<div class="col-md-2">
<label class="form-label small fw-bold" data-en="End Date" data-ar="إلى تاريخ">End Date</label>
<input type="date" name="end_date" class="form-control form-control-sm" value="<?= htmlspecialchars($_GET['end_date'] ?? '') ?>">
</div>
<div class="col-md-2 d-flex align-items-end gap-1">
<button type="submit" class="btn btn-primary btn-sm flex-grow-1">
<i class="bi bi-filter"></i> <span data-en="Filter" data-ar="تصفية">Filter</span>
</button>
<a href="index.php?page=export&type=<?= $page ?>&<?= http_build_query($_GET) ?>" class="btn btn-success btn-sm">
<i class="bi bi-download"></i> <span data-en="Export" data-ar="تصدير">Export</span>
</a>
<a href="index.php?page=<?= $page ?>" class="btn btn-outline-secondary btn-sm flex-grow-1">
<i class="bi bi-x-circle"></i> <span data-en="Clear" data-ar="مسح">Clear</span>
</a>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
@ -1305,6 +1513,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<th style="width: 40%;" data-en="Item" data-ar="الصنف">Item</th>
<th data-en="Quantity" data-ar="الكمية">Quantity</th>
<th data-en="Unit Price" data-ar="سعر الوحدة">Unit Price</th>
<th data-en="VAT %" data-ar="الضريبة %">VAT %</th>
<th data-en="Total" data-ar="الإجمالي">Total</th>
<th style="width: 50px;"></th>
</tr>
@ -1314,17 +1523,17 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي">Subtotal</td>
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي">Subtotal</td>
<td class="fw-bold" id="subtotal">$0.00</td>
<td></td>
</tr>
<tr>
<td colspan="3" class="text-end fw-bold text-primary" data-en="VAT Amount" data-ar="مبلغ الضريبة">VAT Amount</td>
<td colspan="4" class="text-end fw-bold text-primary" data-en="VAT Amount" data-ar="مبلغ الضريبة">VAT Amount</td>
<td class="fw-bold text-primary" id="totalVat">$0.00</td>
<td></td>
</tr>
<tr class="table-dark">
<td colspan="3" class="text-end fw-bold" data-en="Grand Total (Inc. VAT)" data-ar="الإجمالي الكلي (شامل الضريبة)">Grand Total (Inc. VAT)</td>
<td colspan="4" class="text-end fw-bold" data-en="Grand Total (Inc. VAT)" data-ar="الإجمالي الكلي (شامل الضريبة)">Grand Total (Inc. VAT)</td>
<td class="fw-bold" id="grandTotal">$0.00</td>
<td></td>
</tr>
@ -1391,6 +1600,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<th style="width: 40%;" data-en="Item" data-ar="الصنف">Item</th>
<th data-en="Quantity" data-ar="الكمية">Quantity</th>
<th data-en="Unit Price" data-ar="سعر الوحدة">Unit Price</th>
<th data-en="VAT %" data-ar="الضريبة %">VAT %</th>
<th data-en="Total" data-ar="الإجمالي">Total</th>
<th style="width: 50px;"></th>
</tr>
@ -1400,17 +1610,17 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي">Subtotal</td>
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي">Subtotal</td>
<td class="fw-bold" id="edit_subtotal">$0.00</td>
<td></td>
</tr>
<tr>
<td colspan="3" class="text-end fw-bold text-primary" data-en="VAT Amount" data-ar="مبلغ الضريبة">VAT Amount</td>
<td colspan="4" class="text-end fw-bold text-primary" data-en="VAT Amount" data-ar="مبلغ الضريبة">VAT Amount</td>
<td class="fw-bold text-primary" id="edit_totalVat">$0.00</td>
<td></td>
</tr>
<tr class="table-dark">
<td colspan="3" class="text-end fw-bold" data-en="Grand Total (Inc. VAT)" data-ar="الإجمالي الكلي (شامل الضريبة)">Grand Total (Inc. VAT)</td>
<td colspan="4" class="text-end fw-bold" data-en="Grand Total (Inc. VAT)" data-ar="الإجمالي الكلي (شامل الضريبة)">Grand Total (Inc. VAT)</td>
<td class="fw-bold" id="edit_grandTotal">$0.00</td>
<td></td>
</tr>
@ -1481,6 +1691,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<th data-en="Description" data-ar="الوصف">Description</th>
<th class="text-center" data-en="Qty" data-ar="الكمية">Qty</th>
<th class="text-end" data-en="Price" data-ar="السعر">Price</th>
<th class="text-end" data-en="VAT %" data-ar="الضريبة %">VAT %</th>
<th class="text-end" data-en="Total" data-ar="الإجمالي">Total</th>
</tr>
</thead>
@ -1489,15 +1700,15 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي">Subtotal</td>
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي">Subtotal</td>
<td class="text-end fw-bold" id="invSubtotal"></td>
</tr>
<tr>
<td colspan="3" class="text-end fw-bold" data-en="VAT Amount" data-ar="مبلغ الضريبة">VAT Amount</td>
<td colspan="4" class="text-end fw-bold" data-en="VAT Amount" data-ar="مبلغ الضريبة">VAT Amount</td>
<td class="text-end fw-bold" id="invVatAmount"></td>
</tr>
<tr class="table-light">
<td colspan="3" class="text-end fw-bold" data-en="Total (Inc. VAT)" data-ar="الإجمالي (شامل الضريبة)">Total (Inc. VAT)</td>
<td colspan="4" class="text-end fw-bold" data-en="Total (Inc. VAT)" data-ar="الإجمالي (شامل الضريبة)">Total (Inc. VAT)</td>
<td class="text-end fw-bold" id="invGrandTotal"></td>
</tr>
</tfoot>
@ -2223,10 +2434,10 @@ document.addEventListener('DOMContentLoaded', function() {
<input type="hidden" class="item-vat-rate" value="${vatRate}">
<div><strong>${item.name_en}</strong></div>
<div class="small text-muted">${item.name_ar} (${item.sku})</div>
<div class="small text-primary">VAT: ${vatRate}%</div>
</td>
<td><input type="number" step="0.01" name="quantities[]" class="form-control item-qty" value="${qty}" required></td>
<td><input type="number" step="0.01" name="prices[]" class="form-control item-price" value="${price}" required></td>
<td><input type="text" class="form-control bg-light" value="${vatRate}%" readonly></td>
<td><input type="number" step="0.01" class="form-control item-total" value="${(qty * price).toFixed(2)}" readonly></td>
<td><button type="button" class="btn btn-outline-danger btn-sm remove-row"><i class="bi bi-trash"></i></button></td>
`;
@ -2335,6 +2546,7 @@ document.addEventListener('DOMContentLoaded', function() {
<td>${item.name_en} / ${item.name_ar}</td>
<td class="text-center">${item.quantity}</td>
<td class="text-end">$${parseFloat(item.unit_price).toFixed(2)}</td>
<td class="text-end">${parseFloat(item.vat_rate || 0).toFixed(2)}%</td>
<td class="text-end">$${parseFloat(item.total_price).toFixed(2)}</td>
`;
body.appendChild(tr);

BIN
uploads/favicon.pdf Normal file

Binary file not shown.

BIN
uploads/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB