adding edit icons to sales list

This commit is contained in:
Flatlogic Bot 2026-02-16 09:58:22 +00:00
parent 4b20014812
commit caf37ba2c8
3 changed files with 695 additions and 116 deletions

View File

@ -0,0 +1,25 @@
-- Add VAT to stock items
ALTER TABLE stock_items ADD COLUMN vat_rate DECIMAL(5,2) DEFAULT 0.00;
-- Add VAT details to invoices
ALTER TABLE invoices ADD COLUMN vat_amount DECIMAL(15,2) DEFAULT 0.00;
ALTER TABLE invoices ADD COLUMN total_with_vat DECIMAL(15,2) DEFAULT 0.00;
-- Create settings table for company profile
CREATE TABLE IF NOT EXISTS settings (
`key` VARCHAR(50) PRIMARY KEY,
`value` TEXT,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Seed initial company profile
INSERT INTO settings (`key`, `value`) VALUES
('company_name', 'My Tech Company'),
('company_logo', ''),
('vat_number', ''),
('cr_number', ''),
('company_address', ''),
('company_phone', ''),
('vat_enabled', '1'),
('default_vat_rate', '15.00')
ON DUPLICATE KEY UPDATE `key`=`key`;

718
index.php
View File

@ -7,7 +7,7 @@ $message = '';
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'search_items') { if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'search_items') {
header('Content-Type: application/json'); header('Content-Type: application/json');
$q = $_GET['q'] ?? ''; $q = $_GET['q'] ?? '';
$stmt = db()->prepare("SELECT id, name_en, name_ar, sku, sale_price, purchase_price, stock_quantity FROM stock_items WHERE name_en LIKE ? OR name_ar LIKE ? OR sku LIKE ? LIMIT 10"); $stmt = db()->prepare("SELECT id, name_en, name_ar, sku, sale_price, purchase_price, stock_quantity, vat_rate FROM stock_items WHERE name_en LIKE ? OR name_ar LIKE ? OR sku LIKE ? LIMIT 10");
$stmt->execute(["%$q%", "%$q%", "%$q%"]); $stmt->execute(["%$q%", "%$q%", "%$q%"]);
echo json_encode($stmt->fetchAll()); echo json_encode($stmt->fetchAll());
exit; exit;
@ -82,6 +82,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$s_price = (float)($_POST['sale_price'] ?? 0); $s_price = (float)($_POST['sale_price'] ?? 0);
$qty = (float)($_POST['stock_quantity'] ?? 0); $qty = (float)($_POST['stock_quantity'] ?? 0);
$min_stock = (float)($_POST['min_stock_level'] ?? 0); $min_stock = (float)($_POST['min_stock_level'] ?? 0);
$vat_rate = (float)($_POST['vat_rate'] ?? 0);
$expiry = $_POST['expiry_date'] ?: null; $expiry = $_POST['expiry_date'] ?: null;
$image_path = null; $image_path = null;
@ -95,8 +96,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
if ($name_en && $name_ar) { if ($name_en && $name_ar) {
$stmt = db()->prepare("INSERT INTO stock_items (category_id, unit_id, supplier_id, name_en, name_ar, sku, purchase_price, sale_price, stock_quantity, min_stock_level, expiry_date, image_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO stock_items (category_id, unit_id, supplier_id, name_en, name_ar, sku, purchase_price, sale_price, stock_quantity, min_stock_level, expiry_date, image_path, vat_rate) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$cat_id, $unit_id, $supplier_id, $name_en, $name_ar, $sku, $p_price, $s_price, $qty, $min_stock, $expiry, $image_path]); $stmt->execute([$cat_id, $unit_id, $supplier_id, $name_en, $name_ar, $sku, $p_price, $s_price, $qty, $min_stock, $expiry, $image_path, $vat_rate]);
$message = "Item added successfully!"; $message = "Item added successfully!";
} }
} }
@ -128,6 +129,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$s_price = (float)($_POST['sale_price'] ?? 0); $s_price = (float)($_POST['sale_price'] ?? 0);
$qty = (float)($_POST['stock_quantity'] ?? 0); $qty = (float)($_POST['stock_quantity'] ?? 0);
$min_stock = (float)($_POST['min_stock_level'] ?? 0); $min_stock = (float)($_POST['min_stock_level'] ?? 0);
$vat_rate = (float)($_POST['vat_rate'] ?? 0);
$expiry = $_POST['expiry_date'] ?: null; $expiry = $_POST['expiry_date'] ?: null;
$stmt = db()->prepare("SELECT image_path FROM stock_items WHERE id = ?"); $stmt = db()->prepare("SELECT image_path FROM stock_items WHERE id = ?");
@ -148,8 +150,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
if ($name_en && $name_ar) { if ($name_en && $name_ar) {
$stmt = db()->prepare("UPDATE stock_items SET category_id = ?, unit_id = ?, supplier_id = ?, name_en = ?, name_ar = ?, sku = ?, purchase_price = ?, sale_price = ?, stock_quantity = ?, min_stock_level = ?, expiry_date = ?, image_path = ? WHERE id = ?"); $stmt = db()->prepare("UPDATE stock_items SET category_id = ?, unit_id = ?, supplier_id = ?, name_en = ?, name_ar = ?, sku = ?, purchase_price = ?, sale_price = ?, stock_quantity = ?, min_stock_level = ?, expiry_date = ?, image_path = ?, vat_rate = ? WHERE id = ?");
$stmt->execute([$cat_id, $unit_id, $supplier_id, $name_en, $name_ar, $sku, $p_price, $s_price, $qty, $min_stock, $expiry, $image_path, $id]); $stmt->execute([$cat_id, $unit_id, $supplier_id, $name_en, $name_ar, $sku, $p_price, $s_price, $qty, $min_stock, $expiry, $image_path, $vat_rate, $id]);
$message = "Item updated successfully!"; $message = "Item updated successfully!";
} }
} }
@ -276,22 +278,42 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$db = db(); $db = db();
$db->beginTransaction(); $db->beginTransaction();
try { try {
$total_amount = 0; $subtotal = 0;
foreach ($item_ids as $index => $item_id) { $total_vat = 0;
$total_amount += (float)$quantities[$index] * (float)$prices[$index];
}
$stmt = $db->prepare("INSERT INTO invoices (customer_id, invoice_date, type, payment_type, total_amount) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$customer_id, $invoice_date, $type, $payment_type, $total_amount]);
$invoice_id = $db->lastInsertId();
$items_data = [];
foreach ($item_ids as $index => $item_id) { foreach ($item_ids as $index => $item_id) {
$qty = (float)$quantities[$index]; $qty = (float)$quantities[$index];
$price = (float)$prices[$index]; $price = (float)$prices[$index];
$total_price = $qty * $price;
// Fetch vat_rate for this item
$stmtVat = $db->prepare("SELECT vat_rate FROM stock_items WHERE id = ?");
$stmtVat->execute([$item_id]);
$vat_rate = (float)$stmtVat->fetchColumn();
$line_total = $qty * $price;
$line_vat = $line_total * ($vat_rate / 100);
$subtotal += $line_total;
$total_vat += $line_vat;
$items_data[] = [
'id' => $item_id,
'qty' => $qty,
'price' => $price,
'total' => $line_total
];
}
$total_with_vat = $subtotal + $total_vat;
$stmt = $db->prepare("INSERT INTO invoices (customer_id, invoice_date, type, payment_type, total_amount, vat_amount, total_with_vat) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$customer_id, $invoice_date, $type, $payment_type, $subtotal, $total_vat, $total_with_vat]);
$invoice_id = $db->lastInsertId();
foreach ($items_data as $item) {
$stmt = $db->prepare("INSERT INTO invoice_items (invoice_id, item_id, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?)"); $stmt = $db->prepare("INSERT INTO invoice_items (invoice_id, item_id, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$invoice_id, $item_id, $qty, $price, $total_price]); $stmt->execute([$invoice_id, $item['id'], $item['qty'], $item['price'], $item['total']]);
// Update stock level // Update stock level
if ($type === 'sale') { if ($type === 'sale') {
@ -299,14 +321,142 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} else { } else {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?"); $stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?");
} }
$stmt->execute([$qty, $item_id]); $stmt->execute([$item['qty'], $item['id']]);
} }
$db->commit(); $db->commit();
$message = ucfirst($type) . " invoice created successfully!"; $message = "Invoice #$invoice_id created successfully!";
} catch (Exception $e) { } catch (Exception $e) {
$db->rollBack(); $db->rollBack();
$message = "Error creating invoice: " . $e->getMessage(); $message = "Error: " . $e->getMessage();
}
}
}
if (isset($_POST['delete_invoice'])) {
$id = (int)$_POST['id'];
if ($id) {
$db = db();
$db->beginTransaction();
try {
// Get invoice details
$stmt = $db->prepare("SELECT type FROM invoices WHERE id = ?");
$stmt->execute([$id]);
$type = $stmt->fetchColumn();
// Get items to restore stock
$stmt = $db->prepare("SELECT item_id, quantity FROM invoice_items WHERE invoice_id = ?");
$stmt->execute([$id]);
$items = $stmt->fetchAll();
foreach ($items as $item) {
if ($type === 'sale') {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?");
} else {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity - ? WHERE id = ?");
}
$stmt->execute([$item['quantity'], $item['item_id']]);
}
$stmt = $db->prepare("DELETE FROM invoice_items WHERE invoice_id = ?");
$stmt->execute([$id]);
$stmt = $db->prepare("DELETE FROM invoices WHERE id = ?");
$stmt->execute([$id]);
$db->commit();
$message = "Invoice deleted successfully and stock restored!";
} catch (Exception $e) {
$db->rollBack();
$message = "Error: " . $e->getMessage();
}
}
}
if (isset($_POST['edit_invoice'])) {
$invoice_id = (int)$_POST['invoice_id'];
$customer_id = $_POST['customer_id'] ?: null;
$invoice_date = $_POST['invoice_date'] ?: date('Y-m-d');
$payment_type = $_POST['payment_type'] ?? 'cash';
$item_ids = $_POST['item_ids'] ?? [];
$quantities = $_POST['quantities'] ?? [];
$prices = $_POST['prices'] ?? [];
if ($invoice_id && !empty($item_ids)) {
$db = db();
$db->beginTransaction();
try {
// Get old invoice type and items to revert stock
$stmt = $db->prepare("SELECT type FROM invoices WHERE id = ?");
$stmt->execute([$invoice_id]);
$type = $stmt->fetchColumn();
$stmt = $db->prepare("SELECT item_id, quantity FROM invoice_items WHERE invoice_id = ?");
$stmt->execute([$invoice_id]);
$old_items = $stmt->fetchAll();
foreach ($old_items as $item) {
if ($type === 'sale') {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?");
} else {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity - ? WHERE id = ?");
}
$stmt->execute([$item['quantity'], $item['item_id']]);
}
// Delete old items
$stmt = $db->prepare("DELETE FROM invoice_items WHERE invoice_id = ?");
$stmt->execute([$invoice_id]);
// Calculate new totals
$subtotal = 0;
$total_vat = 0;
$items_data = [];
foreach ($item_ids as $index => $item_id) {
$qty = (float)$quantities[$index];
$price = (float)$prices[$index];
$stmtVat = $db->prepare("SELECT vat_rate FROM stock_items WHERE id = ?");
$stmtVat->execute([$item_id]);
$vat_rate = (float)$stmtVat->fetchColumn();
$line_total = $qty * $price;
$line_vat = $line_total * ($vat_rate / 100);
$subtotal += $line_total;
$total_vat += $line_vat;
$items_data[] = [
'id' => $item_id,
'qty' => $qty,
'price' => $price,
'total' => $line_total
];
}
$total_with_vat = $subtotal + $total_vat;
// Update invoice
$stmt = $db->prepare("UPDATE invoices SET customer_id = ?, invoice_date = ?, payment_type = ?, total_amount = ?, vat_amount = ?, total_with_vat = ? WHERE id = ?");
$stmt->execute([$customer_id, $invoice_date, $payment_type, $subtotal, $total_vat, $total_with_vat, $invoice_id]);
// Insert new items and update stock
foreach ($items_data as $item) {
$stmt = $db->prepare("INSERT INTO invoice_items (invoice_id, item_id, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$invoice_id, $item['id'], $item['qty'], $item['price'], $item['total']]);
if ($type === 'sale') {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity - ? WHERE id = ?");
} else {
$stmt = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?");
}
$stmt->execute([$item['qty'], $item['id']]);
}
$db->commit();
$message = "Invoice #$invoice_id updated successfully!";
} catch (Exception $e) {
$db->rollBack();
$message = "Error: " . $e->getMessage();
} }
} }
} }
@ -340,6 +490,47 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$message = "Payment method deleted successfully!"; $message = "Payment method deleted successfully!";
} }
} }
if (isset($_POST['update_settings'])) {
foreach ($_POST['settings'] as $key => $value) {
$stmt = db()->prepare("INSERT INTO settings (`key`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = ?");
$stmt->execute([$key, $value, $value]);
}
if (isset($_FILES['company_logo']) && $_FILES['company_logo']['error'] === UPLOAD_ERR_OK) {
$ext = pathinfo($_FILES['company_logo']['name'], PATHINFO_EXTENSION);
$filename = 'logo.' . $ext;
$target = 'uploads/' . $filename;
if (!is_dir('uploads')) mkdir('uploads', 0775, true);
if (move_uploaded_file($_FILES['company_logo']['tmp_name'], $target)) {
$stmt = db()->prepare("INSERT INTO settings (`key`, `value`) VALUES ('company_logo', ?) ON DUPLICATE KEY UPDATE `value` = ?");
$stmt->execute([$target, $target]);
}
}
if (isset($_FILES['favicon']) && $_FILES['favicon']['error'] === UPLOAD_ERR_OK) {
$ext = pathinfo($_FILES['favicon']['name'], PATHINFO_EXTENSION);
$filename = 'favicon.' . $ext;
$target = 'uploads/' . $filename;
if (!is_dir('uploads')) mkdir('uploads', 0775, true);
if (move_uploaded_file($_FILES['favicon']['tmp_name'], $target)) {
$stmt = db()->prepare("INSERT INTO settings (`key`, `value`) VALUES ('favicon', ?) ON DUPLICATE KEY UPDATE `value` = ?");
$stmt->execute([$target, $target]);
}
}
if (isset($_FILES['manager_signature']) && $_FILES['manager_signature']['error'] === UPLOAD_ERR_OK) {
$ext = pathinfo($_FILES['manager_signature']['name'], PATHINFO_EXTENSION);
$filename = 'signature.' . $ext;
$target = 'uploads/' . $filename;
if (!is_dir('uploads')) mkdir('uploads', 0775, true);
if (move_uploaded_file($_FILES['manager_signature']['tmp_name'], $target)) {
$stmt = db()->prepare("INSERT INTO settings (`key`, `value`) VALUES ('manager_signature', ?) ON DUPLICATE KEY UPDATE `value` = ?");
$stmt->execute([$target, $target]);
}
}
$message = "Settings updated successfully!";
}
} }
// Routing & Data Fetching // Routing & Data Fetching
@ -351,6 +542,12 @@ $data['categories'] = db()->query("SELECT * FROM stock_categories ORDER BY name_
$data['units'] = db()->query("SELECT * FROM stock_units ORDER BY name_en ASC")->fetchAll(); $data['units'] = db()->query("SELECT * FROM stock_units ORDER BY name_en ASC")->fetchAll();
$data['suppliers'] = db()->query("SELECT * FROM customers WHERE type = 'supplier' ORDER BY name ASC")->fetchAll(); $data['suppliers'] = db()->query("SELECT * FROM customers WHERE type = 'supplier' ORDER BY name ASC")->fetchAll();
$settings_raw = db()->query("SELECT * FROM settings")->fetchAll();
$data['settings'] = [];
foreach ($settings_raw as $s) {
$data['settings'][$s['key']] = $s['value'];
}
switch ($page) { switch ($page) {
case 'suppliers': case 'suppliers':
$data['customers'] = db()->query("SELECT * FROM customers WHERE type = 'supplier' ORDER BY id DESC")->fetchAll(); $data['customers'] = db()->query("SELECT * FROM customers WHERE type = 'supplier' ORDER BY id DESC")->fetchAll();
@ -375,6 +572,9 @@ switch ($page) {
case 'payment_methods': case 'payment_methods':
$data['payment_methods'] = db()->query("SELECT * FROM payment_methods ORDER BY id DESC")->fetchAll(); $data['payment_methods'] = db()->query("SELECT * FROM payment_methods ORDER BY id DESC")->fetchAll();
break; break;
case 'settings':
// Already fetched globally
break;
case 'sales': case 'sales':
case 'purchases': case 'purchases':
$type = ($page === 'sales') ? 'sale' : 'purchase'; $type = ($page === 'sales') ? 'sale' : 'purchase';
@ -383,7 +583,7 @@ switch ($page) {
LEFT JOIN customers c ON v.customer_id = c.id LEFT JOIN customers c ON v.customer_id = c.id
WHERE v.type = '$type' WHERE v.type = '$type'
ORDER BY v.id DESC")->fetchAll(); ORDER BY v.id DESC")->fetchAll();
$data['items_list'] = db()->query("SELECT id, name_en, name_ar, sale_price, purchase_price, stock_quantity FROM stock_items ORDER BY name_en ASC")->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(); $data['customers_list'] = db()->query("SELECT id, name FROM customers WHERE type = '" . ($type === 'sale' ? 'customer' : 'supplier') . "' ORDER BY name ASC")->fetchAll();
break; break;
default: default:
@ -405,6 +605,9 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title data-en="Accounting Admin" data-ar="لوحة التحكم المحاسبية">Accounting Admin</title> <title data-en="Accounting Admin" data-ar="لوحة التحكم المحاسبية">Accounting Admin</title>
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" /> <meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php if (!empty($data['settings']['favicon'])): ?>
<link rel="icon" href="<?= htmlspecialchars($data['settings']['favicon']) ?>?v=<?= time() ?>">
<?php endif; ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
@ -494,6 +697,9 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<a href="index.php?page=payment_methods" class="nav-link <?= isset($_GET['page']) && $_GET['page'] === 'payment_methods' ? 'active' : '' ?>"> <a href="index.php?page=payment_methods" class="nav-link <?= isset($_GET['page']) && $_GET['page'] === 'payment_methods' ? 'active' : '' ?>">
<i class="bi bi-credit-card"></i> <span data-en="Payment Methods" data-ar="طرق الدفع">Payment Methods</span> <i class="bi bi-credit-card"></i> <span data-en="Payment Methods" data-ar="طرق الدفع">Payment Methods</span>
</a> </a>
<a href="index.php?page=settings" class="nav-link <?= isset($_GET['page']) && $_GET['page'] === 'settings' ? 'active' : '' ?>">
<i class="bi bi-building"></i> <span data-en="Company Profile" data-ar="ملف الشركة">Company Profile</span>
</a>
<a href="#" class="nav-link"> <a href="#" class="nav-link">
<i class="bi bi-person-badge"></i> <span data-en="HR" data-ar="الموارد البشرية">HR</span> <i class="bi bi-person-badge"></i> <span data-en="HR" data-ar="الموارد البشرية">HR</span>
</a> </a>
@ -514,6 +720,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
'payment_methods' => ['en' => 'Payment Methods', 'ar' => 'طرق الدفع'], 'payment_methods' => ['en' => 'Payment Methods', 'ar' => 'طرق الدفع'],
'sales' => ['en' => 'Sales Invoices', 'ar' => 'فواتير المبيعات'], 'sales' => ['en' => 'Sales Invoices', 'ar' => 'فواتير المبيعات'],
'purchases' => ['en' => 'Purchase Invoices', 'ar' => 'فواتير المشتريات'], 'purchases' => ['en' => 'Purchase Invoices', 'ar' => 'فواتير المشتريات'],
'settings' => ['en' => 'Company Profile', 'ar' => 'ملف الشركة'],
]; ];
$currTitle = $titles[$page] ?? $titles['dashboard']; $currTitle = $titles[$page] ?? $titles['dashboard'];
?> ?>
@ -528,7 +735,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<i class="bi bi-person-circle"></i> Admin <i class="bi bi-person-circle"></i> Admin
</button> </button>
<ul class="dropdown-menu dropdown-menu-end"> <ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#" data-en="Profile" data-ar="الملف الشخصي">Profile</a></li> <li><a class="dropdown-item" href="index.php?page=settings" data-en="Company Profile" data-ar="ملف الشركة">Company Profile</a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" data-en="Logout" data-ar="تسجيل الخروج">Logout</a></li> <li><a class="dropdown-item" href="#" data-en="Logout" data-ar="تسجيل الخروج">Logout</a></li>
</ul> </ul>
@ -807,6 +1014,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<th data-en="Supplier" data-ar="المورد">Supplier</th> <th data-en="Supplier" data-ar="المورد">Supplier</th>
<th data-en="Stock Level" data-ar="المخزون">Stock Level</th> <th data-en="Stock Level" data-ar="المخزون">Stock Level</th>
<th data-en="Expiry" data-ar="تاريخ الانتهاء">Expiry</th> <th data-en="Expiry" data-ar="تاريخ الانتهاء">Expiry</th>
<th data-en="VAT" data-ar="الضريبة">VAT</th>
<th data-en="Actions" data-ar="الإجراءات">Actions</th> <th data-en="Actions" data-ar="الإجراءات">Actions</th>
</tr> </tr>
</thead> </thead>
@ -839,6 +1047,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div> </div>
</td> </td>
<td><?= $item['expiry_date'] ?: '---' ?></td> <td><?= $item['expiry_date'] ?: '---' ?></td>
<td><?= number_format((float)$item['vat_rate'], 2) ?>%</td>
<td> <td>
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
<button class="btn btn-outline-info" title="View" data-bs-toggle="modal" data-bs-target="#viewItemModal<?= $item['id'] ?>"><i class="bi bi-eye"></i></button> <button class="btn btn-outline-info" title="View" data-bs-toggle="modal" data-bs-target="#viewItemModal<?= $item['id'] ?>"><i class="bi bi-eye"></i></button>
@ -922,7 +1131,11 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<label class="form-label" data-en="Change Picture" data-ar="تغيير الصورة">Change Picture</label> <label class="form-label" data-en="Change Picture" data-ar="تغيير الصورة">Change Picture</label>
<input type="file" name="image" class="form-control" accept="image/*"> <input type="file" name="image" class="form-control" accept="image/*">
</div> </div>
<div class="col-md-6"> <div class="col-md-4">
<label class="form-label" data-en="VAT Rate (%)" data-ar="ضريبة القيمة المضافة (%)">VAT Rate (%)</label>
<input type="number" step="0.01" name="vat_rate" class="form-control" value="<?= (float)$item['vat_rate'] ?>">
</div>
<div class="col-md-8">
<div class="form-check form-switch mt-4"> <div class="form-check form-switch mt-4">
<input class="form-check-input hasExpiryToggleEdit" type="checkbox" id="hasExpiryToggle<?= $item['id'] ?>" <?= $item['expiry_date'] ? 'checked' : '' ?>> <input class="form-check-input hasExpiryToggleEdit" type="checkbox" id="hasExpiryToggle<?= $item['id'] ?>" <?= $item['expiry_date'] ? 'checked' : '' ?>>
<label class="form-check-label" for="hasExpiryToggle<?= $item['id'] ?>" data-en="Has Expiry Date?" data-ar="هل له تاريخ انتهاء؟">Has Expiry Date?</label> <label class="form-check-label" for="hasExpiryToggle<?= $item['id'] ?>" data-en="Has Expiry Date?" data-ar="هل له تاريخ انتهاء؟">Has Expiry Date?</label>
@ -967,6 +1180,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<div class="col-4"><small class="text-muted d-block">Purchase Price</small> $<?= number_format((float)$item['purchase_price'], 2) ?></div> <div class="col-4"><small class="text-muted d-block">Purchase Price</small> $<?= number_format((float)$item['purchase_price'], 2) ?></div>
<div class="col-4"><small class="text-muted d-block">Sale Price</small> $<?= number_format((float)$item['sale_price'], 2) ?></div> <div class="col-4"><small class="text-muted d-block">Sale Price</small> $<?= number_format((float)$item['sale_price'], 2) ?></div>
<div class="col-4"><small class="text-muted d-block">Stock</small> <?= number_format((float)$item['stock_quantity'], 2) ?></div> <div class="col-4"><small class="text-muted d-block">Stock</small> <?= number_format((float)$item['stock_quantity'], 2) ?></div>
<div class="col-4"><small class="text-muted d-block">VAT Rate</small> <?= number_format((float)$item['vat_rate'], 2) ?>%</div>
<div class="col-6"><small class="text-muted d-block">Min Stock</small> <?= number_format((float)$item['min_stock_level'], 2) ?></div> <div class="col-6"><small class="text-muted d-block">Min Stock</small> <?= number_format((float)$item['min_stock_level'], 2) ?></div>
<div class="col-6"><small class="text-muted d-block">Expiry Date</small> <?= $item['expiry_date'] ?: '---' ?></div> <div class="col-6"><small class="text-muted d-block">Expiry Date</small> <?= $item['expiry_date'] ?: '---' ?></div>
</div> </div>
@ -1004,7 +1218,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<?php <?php
foreach ($data['invoices'] as $inv): foreach ($data['invoices'] as $inv):
// Fetch items for this invoice // Fetch items for this invoice
$items = db()->prepare("SELECT ii.*, i.name_en, i.name_ar $items = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.vat_rate
FROM invoice_items ii FROM invoice_items ii
JOIN stock_items i ON ii.item_id = i.id JOIN stock_items i ON ii.item_id = i.id
WHERE ii.invoice_id = ?"); WHERE ii.invoice_id = ?");
@ -1017,11 +1231,22 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<td><?= htmlspecialchars($inv['customer_name'] ?? '---') ?></td> <td><?= htmlspecialchars($inv['customer_name'] ?? '---') ?></td>
<td class="text-end fw-bold">$<?= number_format((float)$inv['total_amount'], 2) ?></td> <td class="text-end fw-bold">$<?= number_format((float)$inv['total_amount'], 2) ?></td>
<td class="text-end"> <td class="text-end">
<button class="btn btn-sm btn-outline-info view-invoice-btn" <div class="btn-group btn-group-sm">
<button class="btn btn-outline-info view-invoice-btn"
data-json='<?= json_encode($inv, JSON_HEX_APOS | JSON_HEX_QUOT) ?>' data-json='<?= json_encode($inv, JSON_HEX_APOS | JSON_HEX_QUOT) ?>'
data-bs-toggle="modal" data-bs-target="#viewInvoiceModal"> data-bs-toggle="modal" data-bs-target="#viewInvoiceModal">
<i class="bi bi-printer"></i> <span data-en="View & Print" data-ar="عرض وطباعة">View & Print</span> <i class="bi bi-printer"></i>
</button> </button>
<button class="btn btn-outline-primary edit-invoice-btn"
data-json='<?= json_encode($inv, JSON_HEX_APOS | JSON_HEX_QUOT) ?>'
data-bs-toggle="modal" data-bs-target="#editInvoiceModal">
<i class="bi bi-pencil"></i>
</button>
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure? This will restore stock levels.')">
<input type="hidden" name="id" value="<?= $inv['id'] ?>">
<button type="submit" name="delete_invoice" class="btn btn-outline-danger"><i class="bi bi-trash"></i></button>
</form>
</div>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
@ -1089,7 +1314,17 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="text-end fw-bold" data-en="Grand Total" data-ar="الإجمالي الكلي">Grand Total</td> <td colspan="3" 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 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 class="fw-bold" id="grandTotal">$0.00</td> <td class="fw-bold" id="grandTotal">$0.00</td>
<td></td> <td></td>
</tr> </tr>
@ -1106,7 +1341,91 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div> </div>
</div> </div>
<!-- View Invoice Modal (Printable) --> <!-- Edit Invoice Modal -->
<div class="modal fade" id="editInvoiceModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content border-0 shadow">
<div class="modal-header">
<h5 class="modal-title" data-en="Edit Invoice" data-ar="تعديل الفاتورة">Edit Invoice</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST">
<input type="hidden" name="invoice_id" id="edit_invoice_id">
<div class="modal-body">
<div class="row mb-4">
<div class="col-md-4">
<label class="form-label" data-en="<?= $page === 'sales' ? 'Customer' : 'Supplier' ?>" data-ar="<?= $page === 'sales' ? 'العميل' : 'المورد' ?>"><?= $page === 'sales' ? 'Customer' : 'Supplier' ?></label>
<select name="customer_id" id="edit_customer_id" class="form-select" required>
<option value="">--- Select ---</option>
<?php foreach ($data['customers_list'] as $c): ?>
<option value="<?= $c['id'] ?>"><?= htmlspecialchars($c['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<label class="form-label" data-en="Invoice Date" data-ar="تاريخ الفاتورة">Invoice Date</label>
<input type="date" name="invoice_date" id="edit_invoice_date" class="form-control" required>
</div>
<div class="col-md-4">
<label class="form-label" data-en="Payment Type" data-ar="نوع الدفع">Payment Type</label>
<select name="payment_type" id="edit_payment_type" class="form-select" required>
<option value="cash" data-en="Cash" data-ar="نقداً">Cash</option>
<option value="credit" data-en="Credit" data-ar="آجل">Credit</option>
</select>
</div>
</div>
<div class="mb-4 position-relative">
<label class="form-label fw-bold" data-en="Search Product (SKU / Name)" data-ar="بحث عن صنف (باركود / اسم)">Search Product (SKU / Name)</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" id="editProductSearchInput" class="form-control" placeholder="Search by SKU, Name EN or AR..." data-en="Search by SKU, Name EN or AR..." data-ar="ابحث بالباركود، الاسم الإنجليزي أو العربي...">
</div>
<div id="editSearchSuggestions" class="list-group position-absolute w-100 shadow-lg" style="z-index: 1050; display: none;"></div>
</div>
<div class="table-responsive">
<table class="table table-bordered">
<thead class="bg-light">
<tr>
<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="Total" data-ar="الإجمالي">Total</th>
<th style="width: 50px;"></th>
</tr>
</thead>
<tbody id="editInvoiceItemsTableBody">
<!-- Rows added dynamically -->
</tbody>
<tfoot>
<tr>
<td colspan="3" 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 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 class="fw-bold" id="edit_grandTotal">$0.00</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Cancel" data-ar="إلغاء">Cancel</button>
<button type="submit" name="edit_invoice" class="btn btn-primary" data-en="Update Invoice" data-ar="تحديث الفاتورة">Update Invoice</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="viewInvoiceModal" tabindex="-1"> <div class="modal fade" id="viewInvoiceModal" tabindex="-1">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content border-0 shadow"> <div class="modal-content border-0 shadow">
@ -1115,25 +1434,46 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body p-5" id="printableInvoice"> <div class="modal-body p-5" id="printableInvoice">
<!-- Loaded via AJAX or static content populated by JS --> <div class="row mb-5 align-items-center">
<div class="text-center mb-5">
<h2 class="fw-bold text-uppercase" data-en="INVOICE" data-ar="فاتورة">INVOICE</h2>
<div id="invoiceTypeLabel" class="badge bg-primary text-uppercase"></div>
</div>
<div class="row mb-4">
<div class="col-6"> <div class="col-6">
<h6 class="text-muted text-uppercase small" data-en="Billed To" data-ar="فاتورة إلى">Billed To</h6> <?php if (!empty($data['settings']['company_logo'])): ?>
<h5 id="invCustomerName" class="mb-1"></h5> <img src="<?= htmlspecialchars($data['settings']['company_logo']) ?>" alt="Logo" style="height: 80px;" class="mb-3">
<div id="invCustomerInfo" class="text-muted small"></div> <?php endif; ?>
<h4 class="fw-bold m-0"><?= htmlspecialchars($data['settings']['company_name'] ?? 'My Company') ?></h4>
<div class="text-muted small">
<?= nl2br(htmlspecialchars($data['settings']['company_address'] ?? '')) ?><br>
<?php if(!empty($data['settings']['company_phone'])): ?>
<span data-en="Phone" data-ar="الهاتف">Phone</span>: <?= htmlspecialchars($data['settings']['company_phone']) ?><br>
<?php endif; ?>
<?php if(!empty($data['settings']['company_email'])): ?>
<span data-en="Email" data-ar="البريد">Email</span>: <?= htmlspecialchars($data['settings']['company_email']) ?><br>
<?php endif; ?>
<?php if(!empty($data['settings']['vat_number'])): ?>
<span data-en="VAT No" data-ar="الرقم الضريبي">VAT No</span>: <?= htmlspecialchars($data['settings']['vat_number']) ?><br>
<?php endif; ?>
<?php if(!empty($data['settings']['cr_number'])): ?>
<span data-en="CR No" data-ar="السجل التجاري">CR No</span>: <?= htmlspecialchars($data['settings']['cr_number']) ?><br>
<?php endif; ?>
<?php if(!empty($data['settings']['registration_no'])): ?>
<span data-en="Reg No" data-ar="رقم التسجيل">Reg No</span>: <?= htmlspecialchars($data['settings']['registration_no']) ?>
<?php endif; ?>
</div>
</div> </div>
<div class="col-6 text-end"> <div class="col-6 text-end">
<h6 class="text-muted text-uppercase small" data-en="Invoice Details" data-ar="تفاصيل الفاتورة">Invoice Details</h6> <h1 class="fw-bold text-uppercase text-primary m-0" data-en="INVOICE" data-ar="فاتورة">INVOICE</h1>
<div><strong data-en="Invoice #" data-ar="رقم الفاتورة">Invoice #</strong> <span id="invNumber"></span></div> <div id="invoiceTypeLabel" class="badge bg-primary text-uppercase mb-2"></div>
<div><strong data-en="Date" data-ar="التاريخ">Date:</strong> <span id="invDate"></span></div> <div class="text-muted small">
<div><strong data-en="Payment" data-ar="الدفع">Payment:</strong> <span id="invPaymentType"></span></div> <div><strong data-en="Invoice #" data-ar="رقم الفاتورة">Invoice #</strong> <span id="invNumber" class="text-dark fw-bold"></span></div>
<div><strong data-en="Date" data-ar="التاريخ">Date:</strong> <span id="invDate" class="text-dark"></span></div>
<div><strong data-en="Payment" data-ar="الدفع">Payment:</strong> <span id="invPaymentType" class="text-dark"></span></div>
</div> </div>
</div> </div>
</div>
<div class="mb-4">
<h6 class="fw-bold text-muted text-uppercase small mb-2" id="invPartyLabel" data-en="Bill To" data-ar="فاتورة إلى">Bill To</h6>
<div class="h5 m-0" id="invCustomerName"></div>
</div>
<table class="table table-bordered mb-4"> <table class="table table-bordered mb-4">
<thead class="bg-light"> <thead class="bg-light">
@ -1149,14 +1489,47 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="text-end fw-bold" data-en="Total" data-ar="الإجمالي">Total</td> <td colspan="3" 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 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 class="text-end fw-bold" id="invGrandTotal"></td> <td class="text-end fw-bold" id="invGrandTotal"></td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div class="row mt-5 align-items-end">
<div class="col-6">
<div id="invQrCode" class="mb-3">
<!-- QR Code image will be injected here -->
</div>
<div class="text-muted small" data-en="Scan to verify invoice" data-ar="امسح للتحقق من الفاتورة">Scan to verify invoice</div>
</div>
<div class="col-6 text-end">
<?php if(!empty($data['settings']['manager_signature'])): ?>
<div class="mb-2">
<img src="<?= htmlspecialchars($data['settings']['manager_signature']) ?>" alt="Signature" style="height: 60px;">
</div>
<?php endif; ?>
<div class="fw-bold"><?= htmlspecialchars($data['settings']['manager_name'] ?? '') ?></div>
<div class="text-muted small" data-en="Authorized Manager" data-ar="المدير المعتمد">Authorized Manager</div>
</div>
</div>
<div class="mt-5 pt-5 border-top text-center text-muted small"> <div class="mt-5 pt-5 border-top text-center text-muted small">
<p data-en="Thank you for your business!" data-ar="شكراً لتعاملكم معنا!">Thank you for your business!</p> <?php if(!empty($data['settings']['bank_name']) || !empty($data['settings']['company_iban'])): ?>
<div class="mb-3 border rounded p-2 bg-light d-inline-block text-dark">
<strong><span data-en="Bank Details" data-ar="تفاصيل البنك">Bank Details</span>:</strong>
<?= htmlspecialchars($data['settings']['bank_name'] ?? '') ?> -
<span data-en="IBAN" data-ar="الأيبان">IBAN</span>: <?= htmlspecialchars($data['settings']['company_iban'] ?? '') ?>
</div>
<?php endif; ?>
<p><?= nl2br(htmlspecialchars($data['settings']['invoice_footer'] ?? 'Thank you for your business!')) ?></p>
</div> </div>
</div> </div>
<div class="modal-footer d-print-none"> <div class="modal-footer d-print-none">
@ -1169,6 +1542,108 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div> </div>
</div> </div>
<?php elseif ($page === 'settings'): ?>
<div class="card p-4">
<h5 class="mb-4" data-en="Company Settings" data-ar="إعدادات الشركة">Company Settings</h5>
<form method="POST" enctype="multipart/form-data">
<div class="row g-4">
<div class="col-md-6">
<div class="card p-3 bg-light border-0">
<h6 class="mb-3 fw-bold" data-en="Profile Information" data-ar="معلومات الملف الشخصي">Profile Information</h6>
<div class="mb-3">
<label class="form-label" data-en="Company Name" data-ar="اسم الشركة">Company Name</label>
<input type="text" name="settings[company_name]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_name'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Company Logo" data-ar="شعار الشركة">Company Logo</label>
<?php if (!empty($data['settings']['company_logo'])): ?>
<div class="mb-2">
<img src="<?= htmlspecialchars($data['settings']['company_logo']) ?>" alt="Logo" style="height: 50px;" class="rounded border">
</div>
<?php endif; ?>
<input type="file" name="company_logo" class="form-control" accept="image/*">
</div>
<div class="mb-3">
<label class="form-label" data-en="Favicon" data-ar="أيقونة الموقع">Favicon</label>
<?php if (!empty($data['settings']['favicon'])): ?>
<div class="mb-2">
<img src="<?= htmlspecialchars($data['settings']['favicon']) ?>" alt="Favicon" style="height: 32px;" class="rounded border">
</div>
<?php endif; ?>
<input type="file" name="favicon" class="form-control" accept="image/x-icon,image/png,image/gif">
</div>
<div class="mb-3">
<label class="form-label" data-en="Phone" data-ar="الهاتف">Phone</label>
<input type="text" name="settings[company_phone]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Company Email" data-ar="البريد الإلكتروني للشركة">Company Email</label>
<input type="email" name="settings[company_email]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_email'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Website" data-ar="الموقع الإلكتروني">Website</label>
<input type="url" name="settings[company_website]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_website'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Manager Name" data-ar="اسم المدير">Manager Name</label>
<input type="text" name="settings[manager_name]" class="form-control" value="<?= htmlspecialchars($data['settings']['manager_name'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Manager Signature" data-ar="توقيع المدير">Manager Signature</label>
<?php if (!empty($data['settings']['manager_signature'])): ?>
<div class="mb-2">
<img src="<?= htmlspecialchars($data['settings']['manager_signature']) ?>" alt="Signature" style="height: 50px;" class="rounded border">
</div>
<?php endif; ?>
<input type="file" name="manager_signature" class="form-control" accept="image/*">
</div>
<div class="mb-3">
<label class="form-label" data-en="Address" data-ar="العنوان">Address</label>
<textarea name="settings[company_address]" class="form-control" rows="2"><?= htmlspecialchars($data['settings']['company_address'] ?? '') ?></textarea>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card p-3 bg-light border-0">
<h6 class="mb-3 fw-bold" data-en="Legal & Tax Details" data-ar="التفاصيل القانونية والضريبية">Legal & Tax Details</h6>
<div class="mb-3">
<label class="form-label" data-en="VAT Number" data-ar="الرقم الضريبي">VAT Number</label>
<input type="text" name="settings[vat_number]" class="form-control" value="<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="CR Number" data-ar="السجل التجاري">CR Number</label>
<input type="text" name="settings[cr_number]" class="form-control" value="<?= htmlspecialchars($data['settings']['cr_number'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Registration Number" data-ar="رقم التسجيل">Registration Number</label>
<input type="text" name="settings[registration_no]" class="form-control" value="<?= htmlspecialchars($data['settings']['registration_no'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Bank Name" data-ar="اسم البنك">Bank Name</label>
<input type="text" name="settings[bank_name]" class="form-control" value="<?= htmlspecialchars($data['settings']['bank_name'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="IBAN" data-ar="رقم الأيبان">IBAN</label>
<input type="text" name="settings[company_iban]" class="form-control" value="<?= htmlspecialchars($data['settings']['company_iban'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Default VAT Rate (%)" data-ar="نسبة الضريبة الافتراضية (%)">Default VAT Rate (%)</label>
<input type="number" step="0.01" name="settings[default_vat_rate]" class="form-control" value="<?= htmlspecialchars($data['settings']['default_vat_rate'] ?? '15.00') ?>">
</div>
<div class="mb-3">
<label class="form-label" data-en="Invoice Footer Note" data-ar="ملاحظة أسفل الفاتورة">Invoice Footer Note</label>
<textarea name="settings[invoice_footer]" class="form-control" rows="2"><?= htmlspecialchars($data['settings']['invoice_footer'] ?? '') ?></textarea>
</div>
</div>
</div>
</div>
<div class="mt-4 text-end">
<button type="submit" name="update_settings" class="btn btn-primary px-5">
<i class="bi bi-save"></i> <span data-en="Save Settings" data-ar="حفظ الإعدادات">Save Settings</span>
</button>
</div>
</form>
</div>
<?php elseif ($page === 'payment_methods'): ?> <?php elseif ($page === 'payment_methods'): ?>
<div class="card p-4"> <div class="card p-4">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
@ -1384,7 +1859,11 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<label class="form-label" data-en="Item Picture" data-ar="صورة الصنف">Item Picture</label> <label class="form-label" data-en="Item Picture" data-ar="صورة الصنف">Item Picture</label>
<input type="file" name="image" class="form-control" accept="image/*"> <input type="file" name="image" class="form-control" accept="image/*">
</div> </div>
<div class="col-md-6"> <div class="col-md-4">
<label class="form-label" data-en="VAT Rate (%)" data-ar="ضريبة القيمة المضافة (%)">VAT Rate (%)</label>
<input type="number" step="0.01" name="vat_rate" class="form-control" value="15.00">
</div>
<div class="col-md-8">
<div class="form-check form-switch mt-4"> <div class="form-check form-switch mt-4">
<input class="form-check-input" type="checkbox" id="hasExpiryToggle"> <input class="form-check-input" type="checkbox" id="hasExpiryToggle">
<label class="form-check-label" for="hasExpiryToggle" data-en="Has Expiry Date?" data-ar="هل له تاريخ انتهاء؟">Has Expiry Date?</label> <label class="form-check-label" for="hasExpiryToggle" data-en="Has Expiry Date?" data-ar="هل له تاريخ انتهاء؟">Has Expiry Date?</label>
@ -1665,19 +2144,22 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
// Invoice Form Logic // Invoice Form Logic
const productSearchInput = document.getElementById('productSearchInput'); const initInvoiceForm = (searchInputId, suggestionsId, tableBodyId, grandTotalId, subtotalId, totalVatId) => {
const searchSuggestions = document.getElementById('searchSuggestions'); const searchInput = document.getElementById(searchInputId);
const itemsTableBody = document.getElementById('invoiceItemsTableBody'); const suggestions = document.getElementById(suggestionsId);
const grandTotalEl = document.getElementById('grandTotal'); const tableBody = document.getElementById(tableBodyId);
const invoiceType = '<?= $page === "sales" ? "sale" : ($page === "purchases" ? "purchase" : "") ?>'; const grandTotalEl = document.getElementById(grandTotalId);
const subtotalEl = document.getElementById(subtotalId);
const totalVatEl = document.getElementById(totalVatId);
if (!searchInput || !tableBody) return;
if (productSearchInput && itemsTableBody) {
let timeout = null; let timeout = null;
productSearchInput.addEventListener('input', function() { searchInput.addEventListener('input', function() {
clearTimeout(timeout); clearTimeout(timeout);
const q = this.value.trim(); const q = this.value.trim();
if (q.length < 1) { if (q.length < 1) {
searchSuggestions.style.display = 'none'; suggestions.style.display = 'none';
return; return;
} }
@ -1685,7 +2167,7 @@ document.addEventListener('DOMContentLoaded', function() {
fetch(`index.php?action=search_items&q=${encodeURIComponent(q)}`) fetch(`index.php?action=search_items&q=${encodeURIComponent(q)}`)
.then(res => res.json()) .then(res => res.json())
.then(data => { .then(data => {
searchSuggestions.innerHTML = ''; suggestions.innerHTML = '';
if (data.length > 0) { if (data.length > 0) {
data.forEach(item => { data.forEach(item => {
const btn = document.createElement('button'); const btn = document.createElement('button');
@ -1697,12 +2179,12 @@ document.addEventListener('DOMContentLoaded', function() {
<span class="text-muted small">Stock: ${item.stock_quantity}</span> <span class="text-muted small">Stock: ${item.stock_quantity}</span>
</div> </div>
`; `;
btn.onclick = () => addItemToInvoice(item); btn.onclick = () => addItemToTable(item, tableBody, searchInput, suggestions, grandTotalEl, subtotalEl, totalVatEl);
searchSuggestions.appendChild(btn); suggestions.appendChild(btn);
}); });
searchSuggestions.style.display = 'block'; suggestions.style.display = 'block';
} else { } else {
searchSuggestions.style.display = 'none'; suggestions.style.display = 'none';
} }
}); });
}, 300); }, 300);
@ -1710,67 +2192,126 @@ document.addEventListener('DOMContentLoaded', function() {
// Close suggestions when clicking outside // Close suggestions when clicking outside
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
if (!productSearchInput.contains(e.target) && !searchSuggestions.contains(e.target)) { if (!searchInput.contains(e.target) && !suggestions.contains(e.target)) {
searchSuggestions.style.display = 'none'; suggestions.style.display = 'none';
} }
}); });
};
function addItemToInvoice(item) { function addItemToTable(item, tableBody, searchInput, suggestions, grandTotalEl, subtotalEl, totalVatEl, customData = null) {
searchSuggestions.style.display = 'none'; if (suggestions) suggestions.style.display = 'none';
productSearchInput.value = ''; if (searchInput) searchInput.value = '';
// Check if item already exists in the table const existingRow = Array.from(tableBody.querySelectorAll('.item-id-input')).find(input => input.value == item.id);
const existingRow = Array.from(itemsTableBody.querySelectorAll('.item-id-input')).find(input => input.value == item.id); if (existingRow && !customData) {
if (existingRow) {
const row = existingRow.closest('tr'); const row = existingRow.closest('tr');
const qtyInput = row.querySelector('.item-qty'); const qtyInput = row.querySelector('.item-qty');
qtyInput.value = parseFloat(qtyInput.value) + 1; qtyInput.value = parseFloat(qtyInput.value) + 1;
calculateTotals(); recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl);
return; return;
} }
const row = document.createElement('tr'); const row = document.createElement('tr');
row.className = 'item-row'; row.className = 'item-row';
const price = invoiceType === 'sale' ? item.sale_price : item.purchase_price; const price = customData ? customData.unit_price : (invoiceType === 'sale' ? item.sale_price : item.purchase_price);
const qty = customData ? customData.quantity : 1;
const vatRate = item.vat_rate || 0;
row.innerHTML = ` row.innerHTML = `
<td> <td>
<input type="hidden" name="item_ids[]" class="item-id-input" value="${item.id}"> <input type="hidden" name="item_ids[]" class="item-id-input" value="${item.id}">
<input type="hidden" class="item-vat-rate" value="${vatRate}">
<div><strong>${item.name_en}</strong></div> <div><strong>${item.name_en}</strong></div>
<div class="small text-muted">${item.name_ar} (${item.sku})</div> <div class="small text-muted">${item.name_ar} (${item.sku})</div>
<div class="small text-primary">VAT: ${vatRate}%</div>
</td> </td>
<td><input type="number" step="0.01" name="quantities[]" class="form-control item-qty" value="1" required></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="number" step="0.01" name="prices[]" class="form-control item-price" value="${price}" required></td>
<td><input type="number" step="0.01" class="form-control item-total" value="${price}" 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> <td><button type="button" class="btn btn-outline-danger btn-sm remove-row"><i class="bi bi-trash"></i></button></td>
`; `;
itemsTableBody.appendChild(row); tableBody.appendChild(row);
attachRowListeners(row); attachRowListeners(row, tableBody, grandTotalEl, subtotalEl, totalVatEl);
calculateTotals(); recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl);
} }
function calculateTotals() { function recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl) {
let grandTotal = 0; let subtotal = 0;
itemsTableBody.querySelectorAll('.item-row').forEach(row => { let totalVat = 0;
tableBody.querySelectorAll('.item-row').forEach(row => {
const qty = parseFloat(row.querySelector('.item-qty').value) || 0; const qty = parseFloat(row.querySelector('.item-qty').value) || 0;
const price = parseFloat(row.querySelector('.item-price').value) || 0; const price = parseFloat(row.querySelector('.item-price').value) || 0;
const vatRate = parseFloat(row.querySelector('.item-vat-rate').value) || 0;
const total = qty * price; const total = qty * price;
const vatAmount = total * (vatRate / 100);
row.querySelector('.item-total').value = total.toFixed(2); row.querySelector('.item-total').value = total.toFixed(2);
grandTotal += total; subtotal += total;
totalVat += vatAmount;
}); });
grandTotalEl.textContent = '$' + grandTotal.toFixed(2); const grandTotal = subtotal + totalVat;
if (subtotalEl) subtotalEl.textContent = '$' + subtotal.toFixed(2);
if (totalVatEl) totalVatEl.textContent = '$' + totalVat.toFixed(2);
if (grandTotalEl) grandTotalEl.textContent = '$' + grandTotal.toFixed(2);
} }
function attachRowListeners(row) { function attachRowListeners(row, tableBody, grandTotalEl, subtotalEl, totalVatEl) {
row.querySelector('.item-qty').addEventListener('input', calculateTotals); row.querySelector('.item-qty').addEventListener('input', () => recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl));
row.querySelector('.item-price').addEventListener('input', calculateTotals); row.querySelector('.item-price').addEventListener('input', () => recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl));
row.querySelector('.remove-row').addEventListener('click', function() { row.querySelector('.remove-row').addEventListener('click', function() {
row.remove(); row.remove();
calculateTotals(); recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl);
}); });
} }
}
const invoiceType = '<?= $page === "sales" ? "sale" : ($page === "purchases" ? "purchase" : "") ?>';
initInvoiceForm('productSearchInput', 'searchSuggestions', 'invoiceItemsTableBody', 'grandTotal', 'subtotal', 'totalVat');
initInvoiceForm('editProductSearchInput', 'editSearchSuggestions', 'editInvoiceItemsTableBody', 'edit_grandTotal', 'edit_subtotal', 'edit_totalVat');
// Edit Invoice Logic
document.querySelectorAll('.edit-invoice-btn').forEach(btn => {
btn.addEventListener('click', function() {
const data = JSON.parse(this.dataset.json);
document.getElementById('edit_invoice_id').value = data.id;
document.getElementById('edit_customer_id').value = data.customer_id;
document.getElementById('edit_invoice_date').value = data.invoice_date;
document.getElementById('edit_payment_type').value = data.payment_type || 'cash';
const tableBody = document.getElementById('editInvoiceItemsTableBody');
tableBody.innerHTML = '';
data.items.forEach(item => {
// We need more data than what's in invoice_items (like SKU and names, but we have them from the join in PHP)
// The dataset-json already contains name_en, name_ar etc because of the PHP logic at line 1093
const itemMeta = {
id: item.item_id,
name_en: item.name_en,
name_ar: item.name_ar,
sku: '', // Optional, or fetch if needed
vat_rate: 0 // Will be handled if we have it in the join
};
// Fetch current item details to get VAT rate if possible, or use stored if available
// For simplicity, let's assume we want to use the item's current VAT rate or store it.
// Looking at the join at line 1093, it doesn't fetch vat_rate. Let's fix that in PHP too.
addItemToTable({
id: item.item_id,
name_en: item.name_en,
name_ar: item.name_ar,
sku: '',
vat_rate: item.vat_rate || 0 // We'll add this to PHP join
}, tableBody, null, null,
document.getElementById('edit_grandTotal'),
document.getElementById('edit_subtotal'),
document.getElementById('edit_totalVat'),
{ quantity: item.quantity, unit_price: item.unit_price });
});
});
});
// View Invoice Logic // View Invoice Logic
document.querySelectorAll('.view-invoice-btn').forEach(btn => { document.querySelectorAll('.view-invoice-btn').forEach(btn => {
@ -1780,6 +2321,9 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('invDate').textContent = data.invoice_date; document.getElementById('invDate').textContent = data.invoice_date;
document.getElementById('invPaymentType').textContent = data.payment_type ? data.payment_type.toUpperCase() : 'CASH'; document.getElementById('invPaymentType').textContent = data.payment_type ? data.payment_type.toUpperCase() : 'CASH';
document.getElementById('invCustomerName').textContent = data.customer_name || '---'; document.getElementById('invCustomerName').textContent = data.customer_name || '---';
document.getElementById('invPartyLabel').textContent = data.type === 'sale' ? 'Bill To' : 'Bill From';
document.getElementById('invPartyLabel').setAttribute('data-en', data.type === 'sale' ? 'Bill To' : 'Bill From');
document.getElementById('invPartyLabel').setAttribute('data-ar', data.type === 'sale' ? 'فاتورة إلى' : 'فاتورة من');
document.getElementById('invoiceTypeLabel').textContent = data.type; document.getElementById('invoiceTypeLabel').textContent = data.type;
document.getElementById('invoiceTypeLabel').className = 'badge text-uppercase ' + (data.type === 'sale' ? 'bg-success' : 'bg-warning'); document.getElementById('invoiceTypeLabel').className = 'badge text-uppercase ' + (data.type === 'sale' ? 'bg-success' : 'bg-warning');
@ -1795,7 +2339,17 @@ document.addEventListener('DOMContentLoaded', function() {
`; `;
body.appendChild(tr); body.appendChild(tr);
}); });
document.getElementById('invGrandTotal').textContent = '$' + parseFloat(data.total_amount).toFixed(2); document.getElementById('invSubtotal').textContent = '$' + parseFloat(data.total_amount).toFixed(2);
document.getElementById('invVatAmount').textContent = '$' + (parseFloat(data.vat_amount) || 0).toFixed(2);
document.getElementById('invGrandTotal').textContent = '$' + (parseFloat(data.total_with_vat) || parseFloat(data.total_amount)).toFixed(2);
// Generate QR Code
const companyName = "<?= htmlspecialchars($data['settings']['company_name'] ?? 'My Company') ?>";
const vatNo = "<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>";
const total = (parseFloat(data.total_with_vat) || parseFloat(data.total_amount)).toFixed(2);
const qrData = `Seller: ${companyName}\nVAT: ${vatNo}\nInvoice: INV-${data.id.toString().padStart(5, '0')}\nDate: ${data.invoice_date}\nTotal: ${total}`;
const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${encodeURIComponent(qrData)}`;
document.getElementById('invQrCode').innerHTML = `<img src="${qrUrl}" alt="QR Code" style="width: 100px; height: 100px;" class="border p-1 bg-white">`;
}); });
}); });
}); });

BIN
uploads/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB