Autosave: 20260224-030706

This commit is contained in:
Flatlogic Bot 2026-02-24 03:07:06 +00:00
parent 9561548a1e
commit 0bec862e83
19 changed files with 995 additions and 599 deletions

View File

@ -17,6 +17,9 @@ $pdo->exec("CREATE TABLE IF NOT EXISTS ads_images (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)"); )");
// Ensure display_layout column exists (for older installations)
$pdo->exec("ALTER TABLE ads_images ADD COLUMN IF NOT EXISTS display_layout ENUM('both', 'split', 'fullscreen') DEFAULT 'both' AFTER is_active");
if (isset($_GET['delete'])) { if (isset($_GET['delete'])) {
if (!has_permission('ads_del')) { if (!has_permission('ads_del')) {
$message = '<div class="alert alert-danger border-0 shadow-sm rounded-3">Access Denied: You do not have permission to delete advertisements.</div>'; $message = '<div class="alert alert-danger border-0 shadow-sm rounded-3">Access Denied: You do not have permission to delete advertisements.</div>';

View File

@ -1,89 +0,0 @@
<?php
require_once __DIR__ . '/../db/config.php';
$pdo = db();
if (!isset($_GET['id'])) {
header("Location: customers.php");
exit;
}
$id = $_GET['id'];
$message = '';
// Fetch Customer
$stmt = $pdo->prepare("SELECT * FROM customers WHERE id = ?");
$stmt->execute([$id]);
$customer = $stmt->fetch();
if (!$customer) {
header("Location: customers.php");
exit;
}
// Handle Update
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$stmt = $pdo->prepare("UPDATE customers SET name = ?, email = ?, phone = ?, address = ? WHERE id = ?");
if ($stmt->execute([$name, $email, $phone, $address, $id])) {
$message = '<div class="alert alert-success">Customer updated successfully!</div>';
// Refresh data
$stmt = $pdo->prepare("SELECT * FROM customers WHERE id = ?");
$stmt->execute([$id]);
$customer = $stmt->fetch();
} else {
$message = '<div class="alert alert-danger">Error updating customer.</div>';
}
}
include 'includes/header.php';
?>
<div class="mb-4">
<a href="customers.php" class="text-decoration-none text-muted mb-2 d-inline-block"><i class="bi bi-arrow-left"></i> Back to Customers</a>
<h2 class="fw-bold mb-0">Edit Customer: <?= htmlspecialchars($customer['name']) ?></h2>
</div>
<?= $message ?>
<div class="card border-0 shadow-sm">
<div class="card-body">
<form method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Name</label>
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars($customer['name']) ?>" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($customer['email']) ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Phone</label>
<input type="text" name="phone" class="form-control" value="<?= htmlspecialchars($customer['phone']) ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Loyalty Points</label>
<div class="form-control bg-light"><?= intval($customer['points']) ?></div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Total Redemptions</label>
<div class="form-control bg-light"><?= intval($customer['loyalty_redemptions_count']) ?></div>
</div>
<div class="col-md-12 mb-3">
<label class="form-label">Address</label>
<textarea name="address" class="form-control" rows="3"><?= htmlspecialchars($customer['address']) ?></textarea>
</div>
</div>
<div class="d-flex justify-content-end gap-2">
<a href="customers.php" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
<?php include 'includes/footer.php'; ?>

View File

@ -6,21 +6,40 @@ $pdo = db();
$message = ''; $message = '';
// Handle Add Customer // Handle Add/Edit Customer
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'add_customer') { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if (!has_permission('customers_add')) { if ($_POST['action'] === 'add_customer') {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to add customers.</div>'; if (!has_permission('customers_add')) {
} else { $message = '<div class="alert alert-danger">Access Denied: You do not have permission to add customers.</div>';
$name = $_POST['name'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$stmt = $pdo->prepare("INSERT INTO customers (name, email, phone, address) VALUES (?, ?, ?, ?)");
if ($stmt->execute([$name, $email, $phone, $address])) {
$message = '<div class="alert alert-success">Customer added successfully!</div>';
} else { } else {
$message = '<div class="alert alert-danger">Error adding customer.</div>'; $name = $_POST['name'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$stmt = $pdo->prepare("INSERT INTO customers (name, email, phone, address) VALUES (?, ?, ?, ?)");
if ($stmt->execute([$name, $email, $phone, $address])) {
$message = '<div class="alert alert-success">Customer added successfully!</div>';
} else {
$message = '<div class="alert alert-danger">Error adding customer.</div>';
}
}
} elseif ($_POST['action'] === 'edit_customer') {
if (!has_permission('customers_add')) { // Use customers_add for editing as well
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to edit customers.</div>';
} else {
$id = $_POST['id'];
$name = $_POST['name'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$stmt = $pdo->prepare("UPDATE customers SET name = ?, email = ?, phone = ?, address = ? WHERE id = ?");
if ($stmt->execute([$name, $email, $phone, $address, $id])) {
$message = '<div class="alert alert-success">Customer updated successfully!</div>';
} else {
$message = '<div class="alert alert-danger">Error updating customer.</div>';
}
} }
} }
} }
@ -48,7 +67,7 @@ include 'includes/header.php';
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Customers</h2> <h2 class="fw-bold mb-0">Customers</h2>
<?php if (has_permission('customers_add')): ?> <?php if (has_permission('customers_add')): ?>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCustomerModal"> <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#customerModal" onclick="openAddModal()">
<i class="bi bi-plus-lg"></i> Add Customer <i class="bi bi-plus-lg"></i> Add Customer
</button> </button>
<?php endif; ?> <?php endif; ?>
@ -87,7 +106,11 @@ include 'includes/header.php';
<td> <td>
<div class="btn-group"> <div class="btn-group">
<?php if (has_permission('customers_add')): ?> <?php if (has_permission('customers_add')): ?>
<a href="customer_edit.php?id=<?= $customer['id'] ?>" class="btn btn-sm btn-outline-primary" title="Edit Customer"><i class="bi bi-pencil"></i></a> <button type="button" class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#customerModal"
onclick="openEditModal(<?= htmlspecialchars(json_encode($customer)) ?>)"
title="Edit Customer"><i class="bi bi-pencil"></i></button>
<?php endif; ?> <?php endif; ?>
<?php if (has_permission('customers_del')): ?> <?php if (has_permission('customers_del')): ?>
@ -112,33 +135,34 @@ include 'includes/header.php';
</div> </div>
</div> </div>
<!-- Add Customer Modal --> <!-- Customer Modal -->
<?php if (has_permission('customers_add')): ?> <?php if (has_permission('customers_add')): ?>
<div class="modal fade" id="addCustomerModal" tabindex="-1"> <div class="modal fade" id="customerModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Add New Customer</h5> <h5 class="modal-title" id="customerModalTitle">Add New Customer</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div> </div>
<form method="POST"> <form method="POST" id="customerForm">
<div class="modal-body"> <div class="modal-body">
<input type="hidden" name="action" value="add_customer"> <input type="hidden" name="action" id="customerAction" value="add_customer">
<input type="hidden" name="id" id="customerId">
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Name</label> <label class="form-label">Name</label>
<input type="text" name="name" class="form-control" required> <input type="text" name="name" id="customerName" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Email</label> <label class="form-label">Email</label>
<input type="email" name="email" class="form-control"> <input type="email" name="email" id="customerEmail" class="form-control">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Phone</label> <label class="form-label">Phone</label>
<input type="text" name="phone" class="form-control"> <input type="text" name="phone" id="customerPhone" class="form-control">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Address</label> <label class="form-label">Address</label>
<textarea name="address" class="form-control" rows="3"></textarea> <textarea name="address" id="customerAddress" class="form-control" rows="3"></textarea>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -149,6 +173,25 @@ include 'includes/header.php';
</div> </div>
</div> </div>
</div> </div>
<script>
function openAddModal() {
document.getElementById('customerModalTitle').innerText = 'Add New Customer';
document.getElementById('customerAction').value = 'add_customer';
document.getElementById('customerForm').reset();
document.getElementById('customerId').value = '';
}
function openEditModal(customer) {
document.getElementById('customerModalTitle').innerText = 'Edit Customer';
document.getElementById('customerAction').value = 'edit_customer';
document.getElementById('customerId').value = customer.id;
document.getElementById('customerName').value = customer.name;
document.getElementById('customerEmail').value = customer.email || '';
document.getElementById('customerPhone').value = customer.phone || '';
document.getElementById('customerAddress').value = customer.address || '';
}
</script>
<?php endif; ?> <?php endif; ?>
<?php include 'includes/footer.php'; ?> <?php include 'includes/footer.php'; ?>

View File

@ -1,133 +0,0 @@
<?php
require_once __DIR__ . "/../includes/functions.php";
require_permission("expenses_edit");
require_once __DIR__ . '/../db/config.php';
$pdo = db();
$id = isset($_GET['id']) ? (int)$_GET['id'] : null;
$expense = null;
$message = '';
$isEdit = false;
if ($id) {
$stmt = $pdo->prepare("SELECT * FROM expenses WHERE id = ?");
$stmt->execute([$id]);
$expense = $stmt->fetch();
if ($expense) {
$isEdit = true;
} else {
header("Location: expenses.php");
exit;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$category_id = $_POST['category_id'];
$outlet_id = $_POST['outlet_id'];
$amount = $_POST['amount'];
$description = trim($_POST['description']);
$expense_date = $_POST['expense_date'];
if (empty($category_id) || empty($amount) || empty($expense_date)) {
$message = '<div class="alert alert-danger">Category, amount, and date are required.</div>';
} else {
try {
if ($isEdit) {
$stmt = $pdo->prepare("UPDATE expenses SET category_id = ?, outlet_id = ?, amount = ?, description = ?, expense_date = ? WHERE id = ?");
$stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date, $id]);
$message = '<div class="alert alert-success">Expense updated successfully!</div>';
$stmt = $pdo->prepare("SELECT * FROM expenses WHERE id = ?");
$stmt->execute([$id]);
$expense = $stmt->fetch();
} else {
$stmt = $pdo->prepare("INSERT INTO expenses (category_id, outlet_id, amount, description, expense_date) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date]);
header("Location: expenses.php?success=created");
exit;
}
} catch (PDOException $e) {
$message = '<div class="alert alert-danger">Database error: ' . $e->getMessage() . '</div>';
}
}
}
if (!$isEdit) {
$expense = [
'category_id' => $_POST['category_id'] ?? '',
'outlet_id' => $_POST['outlet_id'] ?? '',
'amount' => $_POST['amount'] ?? '',
'description' => $_POST['description'] ?? '',
'expense_date' => $_POST['expense_date'] ?? date('Y-m-d')
];
}
$expense_categories = $pdo->query("SELECT * FROM expense_categories ORDER BY name ASC")->fetchAll();
$outlets = $pdo->query("SELECT * FROM outlets ORDER BY name ASC")->fetchAll();
include 'includes/header.php';
?>
<div class="mb-4">
<a href="expenses.php" class="text-decoration-none text-muted mb-2 d-inline-block"><i class="bi bi-arrow-left"></i> Back to Expenses</a>
<h2 class="fw-bold mb-0"><?= $isEdit ? 'Edit Expense' : 'Add New Expense' ?></h2>
</div>
<?= $message ?>
<div class="card border-0 shadow-sm">
<div class="card-body">
<form method="POST">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Category <span class="text-danger">*</span></label>
<select name="category_id" class="form-select" required>
<option value="">Select Category</option>
<?php foreach ($expense_categories as $cat): ?>
<option value="<?= $cat['id'] ?>" <?= $expense['category_id'] == $cat['id'] ? 'selected' : '' ?>><?= htmlspecialchars($cat['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Outlet <span class="text-danger">*</span></label>
<select name="outlet_id" class="form-select" required>
<?php foreach ($outlets as $outlet): ?>
<option value="<?= $outlet['id'] ?>" <?= $expense['outlet_id'] == $outlet['id'] ? 'selected' : '' ?>><?= htmlspecialchars($outlet['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Amount <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" step="0.01" name="amount" class="form-control" value="<?= htmlspecialchars($expense['amount']) ?>" required>
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Date <span class="text-danger">*</span></label>
<input type="date" name="expense_date" class="form-control" value="<?= htmlspecialchars($expense['expense_date']) ?>" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="description" class="form-control" rows="3" placeholder="What was this expense for?"><?= htmlspecialchars($expense['description']) ?></textarea>
</div>
<hr>
<div class="d-flex justify-content-end gap-2">
<a href="expenses.php" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary"><?= $isEdit ? 'Save Changes' : 'Record Expense' ?></button>
</div>
</form>
</div>
</div>
<?php include 'includes/footer.php'; ?>

View File

@ -6,17 +6,56 @@ $pdo = db();
$message = ''; $message = '';
// Handle Create/Update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if ($_POST['action'] === 'save_expense') {
if (!has_permission('expenses_add') && !has_permission('expenses_edit')) {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission.</div>';
} else {
$id = isset($_POST['id']) ? (int)$_POST['id'] : null;
$category_id = $_POST['category_id'];
$outlet_id = $_POST['outlet_id'];
$amount = $_POST['amount'];
$description = trim($_POST['description']);
$expense_date = $_POST['expense_date'];
if (empty($category_id) || empty($amount) || empty($expense_date)) {
$message = '<div class="alert alert-danger">Category, amount, and date are required.</div>';
} else {
try {
if ($id) {
$stmt = $pdo->prepare("UPDATE expenses SET category_id = ?, outlet_id = ?, amount = ?, description = ?, expense_date = ? WHERE id = ?");
$stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date, $id]);
$message = '<div class="alert alert-success">Expense updated successfully!</div>';
} else {
$stmt = $pdo->prepare("INSERT INTO expenses (category_id, outlet_id, amount, description, expense_date) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date]);
$message = '<div class="alert alert-success">Expense recorded successfully!</div>';
}
} catch (PDOException $e) {
$message = '<div class="alert alert-danger">Database error: ' . $e->getMessage() . '</div>';
}
}
}
}
}
// Handle Delete
if (isset($_GET['delete'])) { if (isset($_GET['delete'])) {
if (!has_permission('expenses_del')) { if (!has_permission('expenses_del')) {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to delete expenses.</div>'; $message = '<div class="alert alert-danger">Access Denied: You do not have permission to delete expenses.</div>';
} else { } else {
$id = $_GET['delete']; $id = $_GET['delete'];
$pdo->prepare("DELETE FROM expenses WHERE id = ?")->execute([$id]); $pdo->prepare("DELETE FROM expenses WHERE id = ?")->execute([$id]);
header("Location: expenses.php"); header("Location: expenses.php?success=deleted");
exit; exit;
} }
} }
if (isset($_GET['success']) && $_GET['success'] === 'deleted') {
$message = '<div class="alert alert-success">Expense deleted successfully!</div>';
}
$expense_categories = $pdo->query("SELECT * FROM expense_categories ORDER BY name")->fetchAll(); $expense_categories = $pdo->query("SELECT * FROM expense_categories ORDER BY name")->fetchAll();
$outlets = $pdo->query("SELECT * FROM outlets ORDER BY name")->fetchAll(); $outlets = $pdo->query("SELECT * FROM outlets ORDER BY name")->fetchAll();
@ -65,9 +104,9 @@ include 'includes/header.php';
<p class="text-muted mb-0">Track and manage business expenditures</p> <p class="text-muted mb-0">Track and manage business expenditures</p>
</div> </div>
<?php if (has_permission('expenses_add')): ?> <?php if (has_permission('expenses_add')): ?>
<a href="expense_edit.php" class="btn btn-primary"> <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#expenseModal" onclick="resetExpenseModal()">
<i class="bi bi-plus-lg"></i> Add Expense <i class="bi bi-plus-lg"></i> Add Expense
</a> </button>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -131,8 +170,13 @@ include 'includes/header.php';
<td><?= htmlspecialchars($exp['description']) ?></td> <td><?= htmlspecialchars($exp['description']) ?></td>
<td class="fw-bold"><?= format_currency($exp['amount']) ?></td> <td class="fw-bold"><?= format_currency($exp['amount']) ?></td>
<td class="text-end pe-4"> <td class="text-end pe-4">
<?php if (has_permission('expenses_add')): ?> <?php if (has_permission('expenses_edit')): ?>
<a href="expense_edit.php?id=<?= $exp['id'] ?>" class="btn btn-sm btn-outline-primary me-1"><i class="bi bi-pencil"></i></a> <button type="button" class="btn btn-sm btn-outline-primary me-1"
data-bs-toggle="modal"
data-bs-target="#expenseModal"
onclick='editExpense(<?= htmlspecialchars(json_encode($exp), ENT_QUOTES, 'UTF-8') ?>)'>
<i class="bi bi-pencil"></i>
</button>
<?php endif; ?> <?php endif; ?>
<?php if (has_permission('expenses_del')): ?> <?php if (has_permission('expenses_del')): ?>
<a href="?delete=<?= $exp['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')"><i class="bi bi-trash"></i></a> <a href="?delete=<?= $exp['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')"><i class="bi bi-trash"></i></a>
@ -154,4 +198,94 @@ include 'includes/header.php';
</div> </div>
</div> </div>
<!-- Expense Modal -->
<div class="modal fade" id="expenseModal" tabindex="-1" aria-labelledby="expenseModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form method="POST">
<input type="hidden" name="action" value="save_expense">
<input type="hidden" name="id" id="expense_id">
<div class="modal-header">
<h5 class="modal-title" id="expenseModalLabel">Add New Expense</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Category <span class="text-danger">*</span></label>
<select name="category_id" id="expense_category_id" class="form-select" required>
<option value="">Select Category</option>
<?php foreach ($expense_categories as $cat): ?>
<option value="<?= $cat['id'] ?>"><?= htmlspecialchars($cat['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Outlet <span class="text-danger">*</span></label>
<select name="outlet_id" id="expense_outlet_id" class="form-select" required>
<?php foreach ($outlets as $outlet): ?>
<option value="<?= $outlet['id'] ?>"><?= htmlspecialchars($outlet['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Amount <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" step="0.01" name="amount" id="expense_amount" class="form-control" required>
</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Date <span class="text-danger">*</span></label>
<input type="date" name="expense_date" id="expense_date" class="form-control" value="<?= date('Y-m-d') ?>" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="description" id="expense_description" class="form-control" rows="3" placeholder="What was this expense for?"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" id="expenseSubmitBtn">Record Expense</button>
</div>
</form>
</div>
</div>
</div>
<script>
function resetExpenseModal() {
document.getElementById('expenseModalLabel').innerText = 'Add New Expense';
document.getElementById('expenseSubmitBtn').innerText = 'Record Expense';
document.getElementById('expense_id').value = '';
document.getElementById('expense_category_id').value = '';
document.getElementById('expense_outlet_id').value = '<?= !empty($outlets) ? $outlets[0]['id'] : '' ?>';
document.getElementById('expense_amount').value = '';
document.getElementById('expense_date').value = '<?= date('Y-m-d') ?>';
document.getElementById('expense_description').value = '';
}
function editExpense(exp) {
document.getElementById('expenseModalLabel').innerText = 'Edit Expense';
document.getElementById('expenseSubmitBtn').innerText = 'Save Changes';
document.getElementById('expense_id').value = exp.id;
document.getElementById('expense_category_id').value = exp.category_id;
document.getElementById('expense_outlet_id').value = exp.outlet_id;
document.getElementById('expense_amount').value = exp.amount;
document.getElementById('expense_date').value = exp.expense_date;
document.getElementById('expense_description').value = exp.description;
}
</script>
<?php include 'includes/footer.php'; ?> <?php include 'includes/footer.php'; ?>

View File

@ -357,7 +357,7 @@ function can_view($module) {
<?php endif; ?> <?php endif; ?>
<?php <?php
$peopleGroup = ['customers.php', 'customer_edit.php', 'suppliers.php', 'supplier_edit.php', 'loyalty.php']; $peopleGroup = ['customers.php', 'suppliers.php', 'loyalty.php'];
$canViewPeopleGroup = can_view('customers') || can_view('suppliers') || can_view('loyalty'); $canViewPeopleGroup = can_view('customers') || can_view('suppliers') || can_view('loyalty');
if ($canViewPeopleGroup): if ($canViewPeopleGroup):
?> ?>
@ -396,7 +396,7 @@ function can_view($module) {
<?php endif; ?> <?php endif; ?>
<?php <?php
$financialsGroup = ['expenses.php', 'expense_edit.php', 'expense_categories.php', 'expense_category_edit.php', 'purchases.php', 'purchase_edit.php']; $financialsGroup = ['expenses.php', 'expense_categories.php', 'expense_category_edit.php', 'purchases.php', 'purchase_edit.php'];
$canViewFinancialsGroup = can_view('expenses') || can_view('expense_categories') || can_view('purchases'); $canViewFinancialsGroup = can_view('expenses') || can_view('expense_categories') || can_view('purchases');
if ($canViewFinancialsGroup): if ($canViewFinancialsGroup):
?> ?>
@ -417,7 +417,7 @@ function can_view($module) {
<?php endif; ?> <?php endif; ?>
<?php if (can_view('expenses')): ?> <?php if (can_view('expenses')): ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= isActive('expenses.php') || isActive('expense_edit.php') ? 'active' : '' ?>" href="expenses.php"> <a class="nav-link <?= isActive('expenses.php') ? 'active' : '' ?>" href="expenses.php">
<i class="bi bi-cash-stack me-2"></i> Expenses <i class="bi bi-cash-stack me-2"></i> Expenses
</a> </a>
</li> </li>

View File

@ -14,7 +14,7 @@ if (!$id) {
// Fetch Order Details // Fetch Order Details
$stmt = $pdo->prepare("SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name, $stmt = $pdo->prepare("SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name,
c.name as customer_name, c.phone as customer_phone, c.email as customer_email, c.name as customer_name, c.phone as customer_phone, c.email as customer_email, c.address as customer_address,
u.username as created_by_username u.username as created_by_username
FROM orders o FROM orders o
LEFT JOIN outlets ot ON o.outlet_id = ot.id LEFT JOIN outlets ot ON o.outlet_id = ot.id
@ -56,6 +56,7 @@ foreach ($items as $item) {
<a href="orders.php" class="btn btn-outline-secondary"> <a href="orders.php" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Back to List <i class="bi bi-arrow-left"></i> Back to List
</a> </a>
<button onclick="printThermalReceipt()" class="btn btn-warning border-warning"><i class="bi bi-printer-fill"></i> Thermal Receipt</button>
<button onclick="window.print()" class="btn btn-light border"> <button onclick="window.print()" class="btn btn-light border">
<i class="bi bi-printer"></i> Print Receipt <i class="bi bi-printer"></i> Print Receipt
</button> </button>
@ -222,4 +223,246 @@ foreach ($items as $item) {
</div> </div>
</div> </div>
<?php include 'includes/footer.php'; ?> <?php include 'includes/footer.php'; ?>
<script>
const COMPANY_SETTINGS = <?= json_encode(get_company_settings()) ?>;
const CURRENT_USER = { name: '<?= addslashes($order['created_by_username'] ?? 'System') ?>' };
const CURRENT_OUTLET = { name: '<?= addslashes($order['outlet_name'] ?? 'N/A') ?>' };
const BASE_URL = '<?= get_base_url() ?>';
function formatCurrency(amount) {
const symbol = COMPANY_SETTINGS.currency_symbol || '$';
const decimals = parseInt(COMPANY_SETTINGS.currency_decimals || 2);
return symbol + parseFloat(amount).toFixed(decimals);
}
function printThermalReceipt() {
const data = {
orderId: '<?= $order['id'] ?>',
customer: <?= $order['customer_name'] ? json_encode(['name' => $order['customer_name'], 'phone' => $order['customer_phone'], 'address' => $order['customer_address'] ?? '']) : 'null' ?>,
items: <?= json_encode(array_map(function($i) {
return [
'name' => $i['product_name'],
'variant_name' => $i['variant_name'],
'quantity' => $i['quantity'],
'price' => $i['unit_price']
];
}, $items)) ?>,
total: <?= (float)$order['total_amount'] ?>,
discount: <?= (float)$order['discount'] ?>,
orderType: '<?= $order['order_type'] ?>',
tableNumber: '<?= $order['table_number'] ?>',
date: '<?= date('M d, Y H:i', strtotime($order['created_at'])) ?>',
paymentMethod: '<?= $order['payment_type_name'] ?? 'Unpaid' ?>',
loyaltyRedeemed: <?= ($order['discount'] >= $subtotal && $order['discount'] > 0) ? 'true' : 'false' ?>
};
const width = 400;
const height = 800;
const left = (screen.width - width) / 2;
const top = (screen.height - height) / 2;
const win = window.open('', 'Receipt', `width=${width},height=${height},top=${top},left=${left}`);
const tr = {
'Order': 'الطلب',
'Type': 'النوع',
'Date': 'التاريخ',
'Staff': 'الموظف',
'Table': 'طاولة',
'Payment': 'الدفع',
'ITEM': 'الصنف',
'TOTAL': 'المجموع',
'Subtotal': 'المجموع الفرعي',
'Discount': 'الخصم',
'Tax Included': 'شامل الضريبة',
'THANK YOU FOR YOUR VISIT!': 'شكراً لزيارتكم!',
'Please come again.': 'يرجى زيارتنا مرة أخرى.',
'Customer Details': 'تفاصيل العميل',
'Tel': 'هاتف',
'takeaway': 'سفري',
'dine-in': 'محلي',
'delivery': 'توصيل',
'VAT No': 'الرقم الضريبي',
'CTR No': 'رقم السجل التجاري'
};
const itemsHtml = data.items.map(item => `
<tr>
<td style="padding: 5px 0; border-bottom: 1px solid #eee;">
<div style="font-weight: bold;">${item.name}</div>
${item.variant_name ? `<div style="font-size: 10px; color: #555;">(${item.variant_name})</div>` : ''}
<div style="font-size: 11px;">${item.quantity} x ${formatCurrency(item.price)}</div>
</td>
<td style="text-align: right; vertical-align: middle; font-weight: bold;">${formatCurrency(item.quantity * item.price)}</td>
</tr>
`).join('');
const customerHtml = data.customer ? `
<div style="margin-bottom: 10px; border: 1px solid #eee; padding: 8px; border-radius: 4px;">
<div style="font-weight: bold; text-transform: uppercase; font-size: 10px; color: #666; margin-bottom: 3px;">
<span style="float: left;">Customer Details</span>
<span style="float: right;">${tr['Customer Details']}</span>
<div style="clear: both;"></div>
</div>
<div style="font-weight: bold;">${data.customer.name}</div>
${data.customer.phone ? `<div>Tel: ${data.customer.phone}</div>` : ''}
${data.customer.address ? `<div style="font-size: 11px;">${data.customer.address}</div>` : ''}
</div>
` : '';
const tableHtml = data.tableNumber && data.orderType === 'dine-in' ? `
<div style="display: flex; justify-content: space-between;">
<span><strong>Table:</strong> ${data.tableNumber}</span>
<span><strong>${tr['Table']}:</strong> ${data.tableNumber}</span>
</div>` : '';
const paymentHtml = data.paymentMethod ? `
<div style="display: flex; justify-content: space-between;">
<span><strong>Payment:</strong> ${data.paymentMethod}</span>
<span><strong>${tr['Payment']}:</strong> ${data.paymentMethod}</span>
</div>` : '';
const loyaltyHtml = data.loyaltyRedeemed ? `<div style="color: #d63384; font-weight: bold; margin: 5px 0; text-align: center;">* Loyalty Reward Applied *</div>` : '';
const vatRate = parseFloat(COMPANY_SETTINGS.vat_rate) || 0;
const subtotal = data.total + data.discount;
const vatAmount = vatRate > 0 ? (data.total * (vatRate / (100 + vatRate))) : 0;
const logoHtml = COMPANY_SETTINGS.logo_url ? `<img src="${BASE_URL}${COMPANY_SETTINGS.logo_url}" style="max-height: 80px; max-width: 150px; margin-bottom: 10px;">` : '';
const html = `
<html dir="ltr">
<head>
<title>Receipt #${data.orderId}</title>
<style>
body {
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
margin: 0;
padding: 15px;
color: #000;
line-height: 1.4;
}
.header { text-align: center; margin-bottom: 15px; }
.header h2 { margin: 0 0 5px 0; font-size: 20px; font-weight: bold; }
.header div { font-size: 12px; }
table { width: 100%; border-collapse: collapse; }
.divider { border-bottom: 2px dashed #000; margin: 10px 0; }
.thick-divider { border-bottom: 2px solid #000; margin: 10px 0; }
.totals td { padding: 3px 0; }
.footer { text-align: center; margin-top: 25px; font-size: 12px; }
.order-info { font-size: 11px; margin-bottom: 10px; }
.order-info-row { display: flex; justify-content: space-between; margin-bottom: 2px; }
.rtl { direction: rtl; unicode-bidi: embed; }
@media print {
body { width: 80mm; padding: 5mm; }
@page { margin: 0; }
}
</style>
</head>
<body>
<div class="header">
${logoHtml}
<h2>${COMPANY_SETTINGS.company_name}</h2>
<div style="font-weight: bold;">${CURRENT_OUTLET.name}</div>
<div>${COMPANY_SETTINGS.address}</div>
<div>Tel: ${COMPANY_SETTINGS.phone}</div>
${COMPANY_SETTINGS.vat_number ? `<div style="margin-top: 4px;">VAT No / الرقم الضريبي: ${COMPANY_SETTINGS.vat_number}</div>` : ''}
${COMPANY_SETTINGS.ctr_number ? `<div>CTR No / رقم السجل: ${COMPANY_SETTINGS.ctr_number}</div>` : ''}
</div>
<div class="divider"></div>
<div class="order-info">
<div class="order-info-row">
<span><strong>Order:</strong> #${data.orderId}</span>
<span><strong>${tr['Order']}:</strong> #${data.orderId}</span>
</div>
<div class="order-info-row">
<span><strong>Type:</strong> ${data.orderType.toUpperCase()}</span>
<span><strong>${tr['Type']}:</strong> ${tr[data.orderType] || data.orderType}</span>
</div>
<div class="order-info-row">
<span><strong>Date:</strong> ${data.date}</span>
<span><strong>${tr['Date']}:</strong> ${data.date}</span>
</div>
<div class="order-info-row">
<span><strong>Staff:</strong> ${CURRENT_USER.name}</span>
<span><strong>${tr['Staff']}:</strong> ${CURRENT_USER.name}</span>
</div>
</div>
${tableHtml}
${paymentHtml}
${loyaltyHtml}
<div class="thick-divider"></div>
${customerHtml}
<table>
<thead>
<tr>
<th style="text-align: left; padding-bottom: 5px;">
ITEM / الصنف
</th>
<th style="text-align: right; padding-bottom: 5px;">
TOTAL / المجموع
</th>
</tr>
</thead>
<tbody>
${itemsHtml}
</tbody>
</table>
<div class="divider"></div>
<div class="totals">
<table style="width: 100%">
<tr>
<td>Subtotal / ${tr['Subtotal']}</td>
<td style="text-align: right">${formatCurrency(subtotal)}</td>
</tr>
${data.discount > 0 ? `
<tr>
<td>Discount / ${tr['Discount']}</td>
<td style="text-align: right">-${formatCurrency(data.discount)}</td>
</tr>` : ''}
${vatRate > 0 ? `
<tr>
<td style="font-size: 11px;">Tax Incl. (${vatRate}%) / ${tr['Tax Included']}</td>
<td style="text-align: right">${formatCurrency(vatAmount)}</td>
</tr>` : ''}
<tr style="font-weight: bold; font-size: 18px;">
<td style="padding-top: 10px;">TOTAL / ${tr['TOTAL']}</td>
<td style="text-align: right; padding-top: 10px;">${formatCurrency(data.total)}</td>
</tr>
</table>
</div>
<div class="thick-divider"></div>
<div class="footer">
<div style="font-weight: bold; font-size: 14px; margin-bottom: 2px;">THANK YOU FOR YOUR VISIT!</div>
<div style="font-weight: bold; font-size: 14px; margin-bottom: 5px;" class="rtl">${tr['THANK YOU FOR YOUR VISIT!']}</div>
<div>Please come again.</div>
<div class="rtl">${tr['Please come again.']}</div>
${COMPANY_SETTINGS.email ? `<div style="margin-top: 5px; font-size: 10px;">${COMPANY_SETTINGS.email}</div>` : ''}
<div style="margin-top: 15px; font-size: 10px; color: #888;">Powered by Flatlogic POS</div>
</div>
<script>
window.onload = function() {
window.print();
setTimeout(function() { window.close(); }, 1500);
}
</script>
</body>
</html>
`;
win.document.write(html);
win.document.close();
}
</script>

View File

@ -42,37 +42,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
if ($id) { if ($id) {
// Update purchase
// Before updating, if it was completed and now it's not, we might need to revert stock.
// But for simplicity, we'll only increase stock when status changes TO completed.
$old_status = $purchase['status']; $old_status = $purchase['status'];
$stmt = $pdo->prepare("UPDATE purchases SET supplier_id = ?, purchase_date = ?, status = ?, notes = ?, total_amount = ? WHERE id = ?"); $stmt = $pdo->prepare("UPDATE purchases SET supplier_id = ?, purchase_date = ?, status = ?, notes = ?, total_amount = ? WHERE id = ?");
$stmt->execute([$supplier_id, $purchase_date, $status, $notes, $total_amount, $id]); $stmt->execute([$supplier_id, $purchase_date, $status, $notes, $total_amount, $id]);
// Re-fetch items to handle stock reversal if needed
$stmt = $pdo->prepare("SELECT * FROM purchase_items WHERE purchase_id = ?"); $stmt = $pdo->prepare("SELECT * FROM purchase_items WHERE purchase_id = ?");
$stmt->execute([$id]); $stmt->execute([$id]);
$old_items = $stmt->fetchAll(); $old_items = $stmt->fetchAll();
// If status was completed, revert old stock
if ($old_status === 'completed') { if ($old_status === 'completed') {
foreach ($old_items as $oi) { foreach ($old_items as $oi) {
$pdo->prepare("UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?") $pdo->prepare("UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?")
->execute([$oi['quantity'], $oi['product_id']]); ->execute([$oi['quantity'], $oi['product_id']]);
} }
} }
// Delete old items
$pdo->prepare("DELETE FROM purchase_items WHERE purchase_id = ?")->execute([$id]); $pdo->prepare("DELETE FROM purchase_items WHERE purchase_id = ?")->execute([$id]);
} else { } else {
// Create purchase
$stmt = $pdo->prepare("INSERT INTO purchases (supplier_id, purchase_date, status, notes, total_amount) VALUES (?, ?, ?, ?, ?)"); $stmt = $pdo->prepare("INSERT INTO purchases (supplier_id, purchase_date, status, notes, total_amount) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$supplier_id, $purchase_date, $status, $notes, $total_amount]); $stmt->execute([$supplier_id, $purchase_date, $status, $notes, $total_amount]);
$id = $pdo->lastInsertId(); $id = $pdo->lastInsertId();
} }
// Insert items
foreach ($product_ids as $index => $pid) { foreach ($product_ids as $index => $pid) {
$qty = $quantities[$index]; $qty = $quantities[$index];
$cost = $cost_prices[$index]; $cost = $cost_prices[$index];
@ -81,7 +71,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = $pdo->prepare("INSERT INTO purchase_items (purchase_id, product_id, quantity, cost_price, total_price) VALUES (?, ?, ?, ?, ?)"); $stmt = $pdo->prepare("INSERT INTO purchase_items (purchase_id, product_id, quantity, cost_price, total_price) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$id, $pid, $qty, $cost, $total_item_price]); $stmt->execute([$id, $pid, $qty, $cost, $total_item_price]);
// If status is completed, increase stock
if ($status === 'completed') { if ($status === 'completed') {
$pdo->prepare("UPDATE products SET stock_quantity = stock_quantity + ?, cost_price = ? WHERE id = ?") $pdo->prepare("UPDATE products SET stock_quantity = stock_quantity + ?, cost_price = ? WHERE id = ?")
->execute([$qty, $cost, $pid]); ->execute([$qty, $cost, $pid]);
@ -98,7 +87,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
$suppliers = $pdo->query("SELECT * FROM suppliers ORDER BY name")->fetchAll(); $suppliers = $pdo->query("SELECT * FROM suppliers ORDER BY name")->fetchAll();
$products = $pdo->query("SELECT * FROM products ORDER BY name")->fetchAll(); $products = $pdo->query("SELECT id, name, cost_price FROM products ORDER BY name")->fetchAll();
$products_json = json_encode($products);
include 'includes/header.php'; include 'includes/header.php';
?> ?>
@ -114,53 +104,56 @@ include 'includes/header.php';
<form method="POST" id="purchaseForm"> <form method="POST" id="purchaseForm">
<div class="row g-4"> <div class="row g-4">
<div class="col-lg-4"> <div class="col-12">
<div class="card border-0 shadow-sm" style="border-radius: 15px;"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<h5 class="fw-bold mb-4">General Information</h5> <h5 class="fw-bold mb-4">Purchase Details</h5>
<div class="mb-3"> <div class="row g-3">
<label class="form-label text-muted small fw-bold">SUPPLIER</label> <div class="col-md-4">
<select name="supplier_id" class="form-select" style="border-radius: 10px;"> <label class="form-label text-muted small fw-bold">SUPPLIER</label>
<option value="">Direct Purchase / None</option> <select name="supplier_id" class="form-select">
<?php foreach ($suppliers as $s): ?> <option value="">Direct Purchase / None</option>
<option value="<?= $s['id'] ?>" <?= ($purchase && $purchase['supplier_id'] == $s['id']) ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option> <?php foreach ($suppliers as $s): ?>
<?php endforeach; ?> <option value="<?= $s['id'] ?>" <?= ($purchase && $purchase['supplier_id'] == $s['id']) ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option>
</select> <?php endforeach; ?>
</div> </select>
<div class="mb-3"> </div>
<label class="form-label text-muted small fw-bold">PURCHASE DATE</label> <div class="col-md-4">
<input type="date" name="purchase_date" class="form-control" value="<?= $purchase['purchase_date'] ?? date('Y-m-d') ?>" required style="border-radius: 10px;"> <label class="form-label text-muted small fw-bold">PURCHASE DATE</label>
</div> <input type="date" name="purchase_date" class="form-control" value="<?= $purchase['purchase_date'] ?? date('Y-m-d') ?>" required>
<div class="mb-3"> </div>
<label class="form-label text-muted small fw-bold">STATUS</label> <div class="col-md-4">
<select name="status" class="form-select" style="border-radius: 10px;"> <label class="form-label text-muted small fw-bold">STATUS</label>
<option value="pending" <?= ($purchase && $purchase['status'] == 'pending') ? 'selected' : '' ?>>Pending</option> <select name="status" class="form-select">
<option value="completed" <?= ($purchase && $purchase['status'] == 'completed') ? 'selected' : '' ?>>Completed (Updates Stock)</option> <option value="pending" <?= ($purchase && $purchase['status'] == 'pending') ? 'selected' : '' ?>>Pending</option>
<option value="cancelled" <?= ($purchase && $purchase['status'] == 'cancelled') ? 'selected' : '' ?>>Cancelled</option> <option value="completed" <?= ($purchase && $purchase['status'] == 'completed') ? 'selected' : '' ?>>Completed (Updates Stock)</option>
</select> <option value="cancelled" <?= ($purchase && $purchase['status'] == 'cancelled') ? 'selected' : '' ?>>Cancelled</option>
<div class="form-text mt-2 small text-info">Stock is only updated when status is set to <strong>Completed</strong>.</div> </select>
</div> </div>
<div class="mb-0"> <div class="col-12">
<label class="form-label text-muted small fw-bold">NOTES</label> <label class="form-label text-muted small fw-bold">NOTES</label>
<textarea name="notes" class="form-control" rows="3" placeholder="Reference No, delivery details..." style="border-radius: 10px;"><?= htmlspecialchars($purchase['notes'] ?? '') ?></textarea> <textarea name="notes" class="form-control" rows="2" placeholder="Reference No, delivery details..."><?= htmlspecialchars($purchase['notes'] ?? '') ?></textarea>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="card border-0 shadow-sm">
<div class="col-lg-8">
<div class="card border-0 shadow-sm" style="border-radius: 15px;">
<div class="card-body p-4"> <div class="card-body 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">
<h5 class="fw-bold mb-0">Purchase Items</h5> <h5 class="fw-bold mb-0">Products to Purchase</h5>
<button type="button" class="btn btn-soft-primary btn-sm" id="addItemBtn"> <div class="position-relative" style="width: 350px;">
<i class="bi bi-plus-lg me-1"></i> Add Item <div class="input-group">
</button> <span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-search"></i></span>
<input type="text" id="productSearch" class="form-control border-start-0" placeholder="Search products to add...">
</div>
<div id="searchResults" class="dropdown-menu w-100 shadow-sm mt-1" style="max-height: 300px; overflow-y: auto;"></div>
</div>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
<table class="table align-middle" id="itemsTable"> <table class="table align-middle" id="itemsTable">
<thead class="text-muted small fw-bold"> <thead class="table-light">
<tr> <tr>
<th style="width: 45%;">PRODUCT</th> <th style="width: 45%;">PRODUCT</th>
<th style="width: 20%;">QTY</th> <th style="width: 20%;">QTY</th>
@ -170,40 +163,22 @@ include 'includes/header.php';
</tr> </tr>
</thead> </thead>
<tbody id="itemsBody"> <tbody id="itemsBody">
<?php if (empty($items)): ?> <?php if (!empty($items)): ?>
<tr class="item-row">
<td>
<select name="product_id[]" class="form-select product-select" required>
<option value="">Select Product</option>
<?php foreach ($products as $p): ?>
<option value="<?= $p['id'] ?>" data-price="<?= $p['cost_price'] ?>"><?= htmlspecialchars($p['name']) ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input type="number" name="quantity[]" class="form-control qty-input" min="1" value="1" required></td>
<td><input type="number" step="0.01" name="cost_price[]" class="form-control cost-input" value="0.00" required></td>
<td class="text-end fw-bold row-total"><?= format_currency(0) ?></td>
<td></td>
</tr>
<?php else: ?>
<?php foreach ($items as $item): ?> <?php foreach ($items as $item): ?>
<tr class="item-row"> <tr class="item-row" data-product-id="<?= $item['product_id'] ?>">
<td> <td>
<select name="product_id[]" class="form-select product-select" required> <div class="fw-bold"><?= htmlspecialchars($item['product_name']) ?></div>
<?php foreach ($products as $p): ?> <input type="hidden" name="product_id[]" value="<?= $item['product_id'] ?>">
<option value="<?= $p['id'] ?>" data-price="<?= $p['cost_price'] ?>" <?= $item['product_id'] == $p['id'] ? 'selected' : '' ?>><?= htmlspecialchars($p['name']) ?></option>
<?php endforeach; ?>
</select>
</td> </td>
<td><input type="number" name="quantity[]" class="form-control qty-input" min="1" value="<?= $item['quantity'] ?>" required></td> <td><input type="number" name="quantity[]" class="form-control qty-input" min="1" value="<?= $item['quantity'] ?>" required></td>
<td><input type="number" step="0.01" name="cost_price[]" class="form-control cost-input" value="<?= $item['cost_price'] ?>" required></td> <td><input type="number" step="0.01" name="cost_price[]" class="form-control cost-input" value="<?= $item['cost_price'] ?>" required></td>
<td class="text-end fw-bold row-total"><?= format_currency($item['total_price']) ?></td> <td class="text-end fw-bold row-total"><?= format_currency($item['total_price']) ?></td>
<td><button type="button" class="btn btn-link text-danger remove-item p-0"><i class="bi bi-x-circle-fill"></i></button></td> <td class="text-end"><button type="button" class="btn btn-link text-danger remove-item p-0"><i class="bi bi-trash"></i></button></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
</tbody> </tbody>
<tfoot> <tfoot id="tableFooter" class="<?= empty($items) ? 'd-none' : '' ?>">
<tr> <tr>
<td colspan="3" class="text-end fw-bold pt-4">Grand Total:</td> <td colspan="3" class="text-end fw-bold pt-4">Grand Total:</td>
<td class="text-end fw-bold pt-4 text-primary fs-5" id="grandTotal"><?= format_currency($purchase['total_amount'] ?? 0) ?></td> <td class="text-end fw-bold pt-4 text-primary fs-5" id="grandTotal"><?= format_currency($purchase['total_amount'] ?? 0) ?></td>
@ -211,12 +186,17 @@ include 'includes/header.php';
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div id="noItemsMessage" class="text-center py-5 text-muted <?= !empty($items) ? 'd-none' : '' ?>">
<div class="mb-2 display-6"><i class="bi bi-search"></i></div>
<div>Use the search bar above to add products to this purchase.</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="mt-4 text-end"> <div class="mt-4 text-end">
<button type="submit" class="btn btn-primary btn-lg px-5 shadow-sm" style="border-radius: 10px;"> <button type="submit" class="btn btn-primary btn-lg px-5 shadow-sm">
<i class="bi bi-check-lg me-1"></i> Save Purchase <i class="bi bi-check-lg me-1"></i> Save Purchase
</button> </button>
</div> </div>
@ -226,9 +206,13 @@ include 'includes/header.php';
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const products = <?= $products_json ?>;
const productSearch = document.getElementById('productSearch');
const searchResults = document.getElementById('searchResults');
const itemsBody = document.getElementById('itemsBody'); const itemsBody = document.getElementById('itemsBody');
const addItemBtn = document.getElementById('addItemBtn');
const grandTotalElement = document.getElementById('grandTotal'); const grandTotalElement = document.getElementById('grandTotal');
const noItemsMessage = document.getElementById('noItemsMessage');
const tableFooter = document.getElementById('tableFooter');
function formatCurrency(amount) { function formatCurrency(amount) {
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount);
@ -236,7 +220,8 @@ document.addEventListener('DOMContentLoaded', function() {
function calculateTotals() { function calculateTotals() {
let grandTotal = 0; let grandTotal = 0;
document.querySelectorAll('.item-row').forEach(row => { const rows = document.querySelectorAll('.item-row');
rows.forEach(row => {
const qty = parseFloat(row.querySelector('.qty-input').value) || 0; const qty = parseFloat(row.querySelector('.qty-input').value) || 0;
const cost = parseFloat(row.querySelector('.cost-input').value) || 0; const cost = parseFloat(row.querySelector('.cost-input').value) || 0;
const total = qty * cost; const total = qty * cost;
@ -244,38 +229,98 @@ document.addEventListener('DOMContentLoaded', function() {
grandTotal += total; grandTotal += total;
}); });
grandTotalElement.textContent = formatCurrency(grandTotal); grandTotalElement.textContent = formatCurrency(grandTotal);
if (rows.length > 0) {
noItemsMessage.classList.add('d-none');
tableFooter.classList.remove('d-none');
} else {
noItemsMessage.classList.remove('d-none');
tableFooter.classList.add('d-none');
}
} }
addItemBtn.addEventListener('click', function() { productSearch.addEventListener('input', function() {
const firstRow = document.querySelector('.item-row'); const query = this.value.toLowerCase().trim();
const newRow = firstRow.cloneNode(true); searchResults.innerHTML = '';
// Reset values if (query.length < 1) {
newRow.querySelector('.qty-input').value = 1; searchResults.classList.remove('show');
newRow.querySelector('.cost-input').value = 0.00; return;
newRow.querySelector('.product-select').value = "";
newRow.querySelector('.row-total').textContent = formatCurrency(0);
// Add remove button if not exists
if (!newRow.querySelector('.remove-item')) {
const td = document.createElement('td');
td.innerHTML = '<button type="button" class="btn btn-link text-danger remove-item p-0"><i class="bi bi-x-circle-fill"></i></button>';
newRow.appendChild(td);
} }
itemsBody.appendChild(newRow); const filtered = products.filter(p => p.name.toLowerCase().includes(query));
if (filtered.length > 0) {
filtered.forEach(p => {
const item = document.createElement('a');
item.className = 'dropdown-item py-2 border-bottom';
item.href = '#';
item.innerHTML = `
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="fw-bold">${p.name}</div>
<small class="text-muted">Cost: ${formatCurrency(p.cost_price)}</small>
</div>
<i class="bi bi-plus-circle text-primary"></i>
</div>
`;
item.addEventListener('click', function(e) {
e.preventDefault();
addProductToTable(p);
productSearch.value = '';
searchResults.classList.remove('show');
});
searchResults.appendChild(item);
});
searchResults.classList.add('show');
} else {
const item = document.createElement('div');
item.className = 'dropdown-item disabled text-center py-3';
item.textContent = 'No products found';
searchResults.appendChild(item);
searchResults.classList.add('show');
}
}); });
itemsBody.addEventListener('change', function(e) { // Close search results on click outside
if (e.target.classList.contains('product-select')) { document.addEventListener('click', function(e) {
const selectedOption = e.target.options[e.target.selectedIndex]; if (!productSearch.contains(e.target) && !searchResults.contains(e.target)) {
const price = selectedOption.dataset.price || 0; searchResults.classList.remove('show');
const row = e.target.closest('.item-row');
row.querySelector('.cost-input').value = price;
calculateTotals();
} }
}); });
function addProductToTable(product) {
// Check if already in table
const existingRow = Array.from(document.querySelectorAll('.item-row')).find(row => row.dataset.productId == product.id);
if (existingRow) {
const qtyInput = existingRow.querySelector('.qty-input');
qtyInput.value = parseInt(qtyInput.value) + 1;
calculateTotals();
return;
}
const row = document.createElement('tr');
row.className = 'item-row';
row.dataset.productId = product.id;
row.innerHTML = `
<td>
<div class="fw-bold">${product.name}</div>
<input type="hidden" name="product_id[]" value="${product.id}">
</td>
<td><input type="number" name="quantity[]" class="form-control qty-input" min="1" value="1" required></td>
<td><input type="number" step="0.01" name="cost_price[]" class="form-control cost-input" value="${product.cost_price}" required></td>
<td class="text-end fw-bold row-total">${format_currency(product.cost_price)}</td>
<td class="text-end"><button type="button" class="btn btn-link text-danger remove-item p-0"><i class="bi bi-trash"></i></button></td>
`;
itemsBody.appendChild(row);
calculateTotals();
}
// Helper to call from JS
function format_currency(amount) {
return formatCurrency(amount);
}
itemsBody.addEventListener('input', function(e) { itemsBody.addEventListener('input', function(e) {
if (e.target.classList.contains('qty-input') || e.target.classList.contains('cost-input')) { if (e.target.classList.contains('qty-input') || e.target.classList.contains('cost-input')) {
calculateTotals(); calculateTotals();
@ -284,35 +329,24 @@ document.addEventListener('DOMContentLoaded', function() {
itemsBody.addEventListener('click', function(e) { itemsBody.addEventListener('click', function(e) {
if (e.target.closest('.remove-item')) { if (e.target.closest('.remove-item')) {
const rows = document.querySelectorAll('.item-row'); e.target.closest('.item-row').remove();
if (rows.length > 1) { calculateTotals();
e.target.closest('.item-row').remove();
calculateTotals();
} else {
alert("At least one item is required.");
}
} }
}); });
}); });
</script> </script>
<style> <style>
.btn-soft-primary { .dropdown-menu.show {
background-color: rgba(13, 110, 253, 0.1); display: block;
color: #0d6efd;
border: none;
}
.btn-soft-primary:hover {
background-color: #0d6efd;
color: #fff;
}
.form-control, .form-select {
border-color: #e9ecef;
} }
.form-control:focus, .form-select:focus { .form-control:focus, .form-select:focus {
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.05); box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.05);
border-color: #0d6efd; border-color: #0d6efd;
} }
.dropdown-item:hover {
background-color: #f8f9fa;
}
</style> </style>
<?php include 'includes/footer.php'; ?> <?php include 'includes/footer.php'; ?>

View File

@ -1,6 +1,6 @@
<?php <?php
require_once __DIR__ . "/../includes/functions.php"; require_once __DIR__ . "/../includes/functions.php";
require_permission("purchases_view"); // You might need to add this permission to users table if permissions are enforced strictly require_permission("purchases_view");
require_once __DIR__ . '/../db/config.php'; require_once __DIR__ . '/../db/config.php';
$pdo = db(); $pdo = db();
@ -11,7 +11,6 @@ if (isset($_GET['delete'])) {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to delete purchases.</div>'; $message = '<div class="alert alert-danger">Access Denied: You do not have permission to delete purchases.</div>';
} else { } else {
$id = $_GET['delete']; $id = $_GET['delete'];
// Logic to revert stock could be added here, but usually deletions are just deletions.
$pdo->prepare("DELETE FROM purchases WHERE id = ?")->execute([$id]); $pdo->prepare("DELETE FROM purchases WHERE id = ?")->execute([$id]);
header("Location: purchases.php"); header("Location: purchases.php");
exit; exit;
@ -64,7 +63,7 @@ include 'includes/header.php';
<p class="text-muted mb-0">Manage inventory restocks and supplier invoices</p> <p class="text-muted mb-0">Manage inventory restocks and supplier invoices</p>
</div> </div>
<?php if (has_permission('purchases_add')): ?> <?php if (has_permission('purchases_add')): ?>
<a href="purchase_edit.php" class="btn btn-primary btn-lg shadow-sm" style="border-radius: 10px;"> <a href="purchase_edit.php" class="btn btn-primary shadow-sm">
<i class="bi bi-plus-lg me-1"></i> New Purchase <i class="bi bi-plus-lg me-1"></i> New Purchase
</a> </a>
<?php endif; ?> <?php endif; ?>
@ -72,17 +71,17 @@ include 'includes/header.php';
<?= $message ?> <?= $message ?>
<div class="card border-0 shadow-sm mb-4" style="border-radius: 15px;"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body">
<form method="GET" class="row g-3 align-items-center"> <form method="GET" class="row g-3 align-items-center">
<div class="col-md-4"> <div class="col-md-4">
<div class="input-group"> <div class="input-group">
<span class="input-group-text bg-white border-end-0 text-muted ps-3" style="border-radius: 10px 0 0 10px;"><i class="bi bi-search"></i></span> <span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-search"></i></span>
<input type="text" name="search" class="form-control border-start-0 ps-0" placeholder="Search notes..." value="<?= htmlspecialchars($search) ?>" style="border-radius: 0 10px 10px 0;"> <input type="text" name="search" class="form-control border-start-0" placeholder="Search notes..." value="<?= htmlspecialchars($search) ?>">
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<select name="supplier_filter" class="form-select" style="border-radius: 10px;"> <select name="supplier_filter" class="form-select">
<option value="">All Suppliers</option> <option value="">All Suppliers</option>
<?php foreach ($suppliers as $s): ?> <?php foreach ($suppliers as $s): ?>
<option value="<?= $s['id'] ?>" <?= $supplier_filter == $s['id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option> <option value="<?= $s['id'] ?>" <?= $supplier_filter == $s['id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option>
@ -90,7 +89,7 @@ include 'includes/header.php';
</select> </select>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<select name="status_filter" class="form-select" style="border-radius: 10px;"> <select name="status_filter" class="form-select">
<option value="">All Status</option> <option value="">All Status</option>
<option value="pending" <?= $status_filter == 'pending' ? 'selected' : '' ?>>Pending</option> <option value="pending" <?= $status_filter == 'pending' ? 'selected' : '' ?>>Pending</option>
<option value="completed" <?= $status_filter == 'completed' ? 'selected' : '' ?>>Completed</option> <option value="completed" <?= $status_filter == 'completed' ? 'selected' : '' ?>>Completed</option>
@ -98,20 +97,20 @@ include 'includes/header.php';
</select> </select>
</div> </div>
<div class="col-md-3 d-flex gap-2"> <div class="col-md-3 d-flex gap-2">
<button type="submit" class="btn btn-primary px-4 w-100" style="border-radius: 10px;">Filter</button> <button type="submit" class="btn btn-primary px-4 w-100">Filter</button>
<?php if ($search || $supplier_filter || $status_filter): ?> <?php if ($search || $supplier_filter || $status_filter): ?>
<a href="purchases.php" class="btn btn-light text-muted px-3" style="border-radius: 10px;"><i class="bi bi-x-lg"></i></a> <a href="purchases.php" class="btn btn-light text-muted px-3"><i class="bi bi-x-lg"></i></a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<div class="card border-0 shadow-sm" style="border-radius: 15px; overflow: hidden;"> <div class="card border-0 shadow-sm">
<div class="card-body p-0"> <div class="card-body p-0">
<div class="table-responsive"> <div class="table-responsive">
<table class="friendly-table mb-0"> <table class="table table-hover align-middle mb-0">
<thead> <thead class="table-light">
<tr> <tr>
<th class="ps-4">Date</th> <th class="ps-4">Date</th>
<th>Supplier</th> <th>Supplier</th>
@ -139,18 +138,18 @@ include 'includes/header.php';
<span class="badge <?= $status_badge ?> rounded-pill px-3"><?= ucfirst($p['status']) ?></span> <span class="badge <?= $status_badge ?> rounded-pill px-3"><?= ucfirst($p['status']) ?></span>
</td> </td>
<td> <td>
<div class="fw-bold text-primary" style="font-size: 1.1rem;"><?= format_currency($p['total_amount']) ?></div> <div class="fw-bold text-primary"><?= format_currency($p['total_amount']) ?></div>
</td> </td>
<td class="text-end pe-4"> <td class="text-end pe-4">
<div class="d-inline-flex gap-2"> <div class="d-inline-flex gap-2">
<?php if (has_permission('purchases_add')): ?> <?php if (has_permission('purchases_add')): ?>
<a href="purchase_edit.php?id=<?= $p['id'] ?>" class="btn-icon-soft edit" title="Edit/View"> <a href="purchase_edit.php?id=<?= $p['id'] ?>" class="btn btn-sm btn-outline-primary" title="Edit/View">
<i class="bi bi-eye-fill"></i> <i class="bi bi-pencil-square me-1"></i> Edit
</a> </a>
<?php endif; ?> <?php endif; ?>
<?php if (has_permission('purchases_del')): ?> <?php if (has_permission('purchases_del')): ?>
<a href="?delete=<?= $p['id'] ?>" class="btn-icon-soft delete" onclick="return confirm('Are you sure you want to delete this purchase record?')" title="Delete"> <a href="?delete=<?= $p['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this purchase record?')" title="Delete">
<i class="bi bi-trash-fill"></i> <i class="bi bi-trash me-1"></i> Delete
</a> </a>
<?php endif; ?> <?php endif; ?>
</div> </div>

View File

@ -8,39 +8,70 @@ if (function_exists('require_permission')) {
$pdo = db(); $pdo = db();
$tab = $_GET['tab'] ?? 'staff'; $tab = $_GET['tab'] ?? 'staff';
$date_from = $_GET['date_from'] ?? '';
$date_to = $_GET['date_to'] ?? '';
$params = [];
$where_clauses = [];
if ($date_from) {
$where_clauses[] = "r.created_at >= :date_from";
$params['date_from'] = $date_from . ' 00:00:00';
}
if ($date_to) {
$where_clauses[] = "r.created_at <= :date_to";
$params['date_to'] = $date_to . ' 23:59:59';
}
$where_sql = !empty($where_clauses) ? "WHERE " . implode(" AND ", $where_clauses) : "";
// Fetch Staff summary stats // Fetch Staff summary stats
$summaryStmt = $pdo->query(" $summaryQuery = "
SELECT u.id, u.full_name, u.username, u.profile_pic, SELECT u.id, u.full_name, u.username, u.profile_pic,
AVG(r.rating) as avg_rating, COUNT(r.id) as total_ratings AVG(r.rating) as avg_rating, COUNT(r.id) as total_ratings
FROM users u FROM users u
JOIN staff_ratings r ON u.id = r.user_id JOIN staff_ratings r ON u.id = r.user_id
$where_sql
GROUP BY u.id GROUP BY u.id
ORDER BY avg_rating DESC ORDER BY avg_rating DESC
"); ";
$summaryStmt = $pdo->prepare($summaryQuery);
$summaryStmt->execute($params);
$summaries = $summaryStmt->fetchAll(PDO::FETCH_ASSOC); $summaries = $summaryStmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch Service summary stats // Fetch Service summary stats
$serviceSummaryStmt = $pdo->query(" $serviceWhereSql = str_replace('r.created_at', 'created_at', $where_sql);
SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM service_ratings $serviceParams = $params;
");
$serviceSummaryQuery = "SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM service_ratings $serviceWhereSql";
$serviceSummaryStmt = $pdo->prepare($serviceSummaryQuery);
$serviceSummaryStmt->execute($serviceParams);
$serviceSummary = $serviceSummaryStmt->fetch(PDO::FETCH_ASSOC); $serviceSummary = $serviceSummaryStmt->fetch(PDO::FETCH_ASSOC);
if ($tab === 'service') { if ($tab === 'service') {
$query = "SELECT * FROM service_ratings ORDER BY created_at DESC"; $query = "SELECT * FROM service_ratings $serviceWhereSql ORDER BY created_at DESC";
$list_params = $serviceParams;
} else { } else {
$query = " $query = "
SELECT r.*, u.full_name, u.username, u.profile_pic SELECT r.*, u.full_name, u.username, u.profile_pic
FROM staff_ratings r FROM staff_ratings r
JOIN users u ON r.user_id = u.id JOIN users u ON r.user_id = u.id
$where_sql
ORDER BY r.created_at DESC ORDER BY r.created_at DESC
"; ";
$list_params = $params;
} }
$pagination = paginate_query($pdo, $query); $pagination = paginate_query($pdo, $query, $list_params);
$ratings = $pagination['data']; $ratings = $pagination['data'];
?> ?>
<style>
.extra-small { font-size: 0.7rem; }
.table-ratings { font-size: 0.8rem; }
.table-ratings th { font-weight: 600; text-transform: uppercase; font-size: 0.7rem; letter-spacing: 0.03em; }
</style>
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="h3 mb-0 text-gray-800">Ratings & Feedback</h2> <h2 class="h3 mb-0 text-gray-800">Ratings & Feedback</h2>
<div class="d-flex gap-2"> <div class="d-flex gap-2">
@ -53,14 +84,38 @@ $ratings = $pagination['data'];
</div> </div>
</div> </div>
<div class="card border-0 shadow-sm rounded-4 mb-4">
<div class="card-body py-3">
<form method="GET" class="row g-3 align-items-end">
<input type="hidden" name="tab" value="<?= htmlspecialchars($tab) ?>">
<div class="col-md-3">
<label class="form-label small fw-bold mb-1">From Date</label>
<input type="date" name="date_from" class="form-control form-control-sm" value="<?= htmlspecialchars($date_from) ?>">
</div>
<div class="col-md-3">
<label class="form-label small fw-bold mb-1">To Date</label>
<input type="date" name="date_to" class="form-control form-control-sm" value="<?= htmlspecialchars($date_to) ?>">
</div>
<div class="col-md-4">
<button type="submit" class="btn btn-primary btn-sm px-3">
<i class="bi bi-filter"></i> Filter
</button>
<a href="ratings.php?tab=<?= htmlspecialchars($tab) ?>" class="btn btn-light btn-sm px-3 border">
<i class="bi bi-arrow-clockwise"></i> Reset
</a>
</div>
</form>
</div>
</div>
<ul class="nav nav-pills mb-4 bg-white p-2 rounded-4 shadow-sm d-inline-flex"> <ul class="nav nav-pills mb-4 bg-white p-2 rounded-4 shadow-sm d-inline-flex">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link rounded-pill <?= $tab === 'staff' ? 'active' : '' ?>" href="?tab=staff"> <a class="nav-link rounded-pill <?= $tab === 'staff' ? 'active' : '' ?>" href="?tab=staff&date_from=<?= $date_from ?>&date_to=<?= $date_to ?>">
<i class="bi bi-people me-1"></i> Staff Performance <i class="bi bi-people me-1"></i> Staff Performance
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link rounded-pill <?= $tab === 'service' ? 'active' : '' ?>" href="?tab=service"> <a class="nav-link rounded-pill <?= $tab === 'service' ? 'active' : '' ?>" href="?tab=service&date_from=<?= $date_from ?>&date_to=<?= $date_to ?>">
<i class="bi bi-shop me-1"></i> Restaurant Service <i class="bi bi-shop me-1"></i> Restaurant Service
</a> </a>
</li> </li>
@ -71,7 +126,7 @@ $ratings = $pagination['data'];
<?php if (empty($summaries)): ?> <?php if (empty($summaries)): ?>
<div class="alert alert-info border-0 shadow-sm rounded-4 text-center py-4 mb-5"> <div class="alert alert-info border-0 shadow-sm rounded-4 text-center py-4 mb-5">
<i class="bi bi-info-circle fs-2 mb-2"></i> <i class="bi bi-info-circle fs-2 mb-2"></i>
<p class="mb-0">No staff members have been rated yet.</p> <p class="mb-0">No staff members have been rated yet for the selected period.</p>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="row g-4 mb-5"> <div class="row g-4 mb-5">
@ -80,25 +135,25 @@ $ratings = $pagination['data'];
<div class="card border-0 shadow-sm rounded-4 h-100"> <div class="card border-0 shadow-sm rounded-4 h-100">
<div class="card-body text-center"> <div class="card-body text-center">
<?php if ($summary['profile_pic']): ?> <?php if ($summary['profile_pic']): ?>
<img src="../<?= htmlspecialchars($summary['profile_pic']) ?>" alt="Staff" class="rounded-circle mb-3" style="width: 64px; height: 64px; object-fit: cover;"> <img src="../<?= htmlspecialchars($summary['profile_pic']) ?>" alt="Staff" class="rounded-circle mb-3" style="width: 56px; height: 56px; object-fit: cover;">
<?php else: ?> <?php else: ?>
<div class="rounded-circle bg-light d-inline-flex align-items-center justify-content-center mb-3" style="width: 64px; height: 64px;"> <div class="rounded-circle bg-light d-inline-flex align-items-center justify-content-center mb-3" style="width: 56px; height: 56px;">
<i class="bi bi-person fs-2 text-primary"></i> <i class="bi bi-person fs-3 text-primary"></i>
</div> </div>
<?php endif; ?> <?php endif; ?>
<h5 class="card-title mb-1 small fw-bold text-truncate" title="<?= htmlspecialchars($summary['full_name'] ?: $summary['username']) ?>"> <h5 class="card-title mb-1 small fw-bold text-truncate" title="<?= htmlspecialchars($summary['full_name'] ?: $summary['username']) ?>">
<?= htmlspecialchars($summary['full_name'] ?: $summary['username']) ?> <?= htmlspecialchars($summary['full_name'] ?: $summary['username']) ?>
</h5> </h5>
<div class="text-warning mb-2"> <div class="text-warning mb-2" style="font-size: 0.8rem;">
<?php <?php
$avg = round($summary['avg_rating']); $avg = round($summary['avg_rating']);
for ($i = 1; $i <= 5; $i++) { for ($i = 1; $i <= 5; $i++) {
echo $i <= $avg ? '<i class="bi bi-star-fill"></i>' : '<i class="bi bi-star"></i>'; echo $i <= $avg ? '<i class="bi bi-star-fill"></i>' : '<i class="bi bi-star"></i>';
} }
?> ?>
<span class="text-muted ms-1">(<?= number_format($summary['avg_rating'], 1) ?>)</span> <span class="text-muted ms-1 small">(<?= number_format($summary['avg_rating'], 1) ?>)</span>
</div> </div>
<p class="text-muted small mb-0"><?= $summary['total_ratings'] ?> total ratings</p> <p class="text-muted extra-small mb-0"><?= $summary['total_ratings'] ?> ratings</p>
</div> </div>
</div> </div>
</div> </div>
@ -120,8 +175,8 @@ $ratings = $pagination['data'];
N/A N/A
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="text-white-50"> <div class="text-white-50 extra-small">
Based on <?= $serviceSummary['total_ratings'] ?> customer reviews Based on <?= $serviceSummary['total_ratings'] ?> reviews
</div> </div>
</div> </div>
</div> </div>
@ -131,14 +186,14 @@ $ratings = $pagination['data'];
<!-- Detailed List --> <!-- Detailed List -->
<div class="card border-0 shadow-sm rounded-4"> <div class="card border-0 shadow-sm rounded-4">
<div class="card-header bg-white py-3"> <div class="card-header bg-white py-3 border-bottom-0">
<h5 class="card-title mb-0"><?= $tab === 'service' ? 'Service Feedback' : 'Staff Feedback' ?></h5> <h5 class="card-title mb-0 small fw-bold"><?= $tab === 'service' ? 'Service Feedback' : 'Staff Feedback' ?></h5>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover align-middle mb-0"> <table class="table table-hover align-middle mb-0 table-ratings">
<thead class="bg-light"> <thead class="bg-light">
<tr> <tr>
<th>Date</th> <th class="ps-3">Date</th>
<?php if ($tab === 'staff'): ?><th>Staff Member</th><?php endif; ?> <?php if ($tab === 'staff'): ?><th>Staff Member</th><?php endif; ?>
<th>Rating</th> <th>Rating</th>
<th>Comment</th> <th>Comment</th>
@ -147,21 +202,25 @@ $ratings = $pagination['data'];
<tbody> <tbody>
<?php if (empty($ratings)): ?> <?php if (empty($ratings)): ?>
<tr> <tr>
<td colspan="<?= $tab === 'staff' ? 4 : 3 ?>" class="text-center py-4 text-muted">No ratings found yet.</td> <td colspan="<?= $tab === 'staff' ? 4 : 3 ?>" class="text-center py-4 text-muted small">No ratings found yet.</td>
</tr> </tr>
<?php else: ?> <?php else: ?>
<?php foreach ($ratings as $rating): ?> <?php foreach ($ratings as $rating): ?>
<tr> <tr>
<td class="small text-muted"> <td class="ps-3 text-muted">
<?= date('M d, Y H:i', strtotime($rating['created_at'])) ?> <?= date('M d, Y H:i', strtotime($rating['created_at'])) ?>
</td> </td>
<?php if ($tab === 'staff'): ?> <?php if ($tab === 'staff'): ?>
<td> <td>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<?php if ($rating['profile_pic']): ?> <?php if ($rating['profile_pic']): ?>
<img src="../<?= htmlspecialchars($rating['profile_pic']) ?>" class="rounded-circle me-2" style="width: 32px; height: 32px; object-fit: cover;"> <img src="../<?= htmlspecialchars($rating['profile_pic']) ?>" class="rounded-circle me-2 shadow-sm" style="width: 24px; height: 24px; object-fit: cover;">
<?php else: ?>
<div class="rounded-circle bg-light d-flex align-items-center justify-content-center me-2 shadow-sm" style="width: 24px; height: 24px;">
<i class="bi bi-person text-primary" style="font-size: 0.7rem;"></i>
</div>
<?php endif; ?> <?php endif; ?>
<span><?= htmlspecialchars($rating['full_name'] ?: $rating['username']) ?></span> <span class="fw-medium text-dark"><?= htmlspecialchars($rating['full_name'] ?: $rating['username']) ?></span>
</div> </div>
</td> </td>
<?php endif; ?> <?php endif; ?>
@ -169,13 +228,13 @@ $ratings = $pagination['data'];
<div class="text-warning"> <div class="text-warning">
<?php <?php
for ($i = 1; $i <= 5; $i++) { for ($i = 1; $i <= 5; $i++) {
echo $i <= $rating['rating'] ? '<i class="bi bi-star-fill"></i>' : '<i class="bi bi-star text-muted"></i>'; echo $i <= $rating['rating'] ? '<i class="bi bi-star-fill"></i>' : '<i class="bi bi-star text-muted" style="opacity: 0.3;"></i>';
} }
?> ?>
</div> </div>
</td> </td>
<td> <td class="text-wrap" style="max-width: 300px;">
<span class="text-muted small"><?= htmlspecialchars($rating['comment'] ?: '-') ?></span> <span class="text-muted italic"><?= htmlspecialchars($rating['comment'] ?: '-') ?></span>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
@ -183,28 +242,28 @@ $ratings = $pagination['data'];
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="card-footer bg-white"> <div class="card-footer bg-white py-3">
<?php render_pagination_controls($pagination); ?> <?php render_pagination_controls($pagination, ['date_from' => $date_from, 'date_to' => $date_to]); ?>
</div> </div>
</div> </div>
<!-- Rating QR Modal --> <!-- Rating QR Modal -->
<div class="modal fade" id="ratingQRModal" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="ratingQRModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow"> <div class="modal-content border-0 shadow-lg rounded-4">
<div class="modal-header bg-warning"> <div class="modal-header bg-warning border-0">
<h5 class="modal-title fw-bold text-dark"><i class="bi bi-star-fill me-2"></i> Customer Rating QR</h5> <h5 class="modal-title fw-bold text-dark"><i class="bi bi-star-fill me-2"></i> Customer Rating QR</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body text-center py-4"> <div class="modal-body text-center py-4">
<p class="text-muted mb-4">Show this QR code to the customer to rate our staff and service.</p> <p class="text-muted mb-4 extra-small px-4">Show this QR code to the customer to rate our staff and service.</p>
<div class="mb-4"> <div class="mb-4">
<img id="rating-qr-img" src="" alt="Rating QR Code" class="img-fluid border p-3 bg-white shadow-sm" style="max-width: 250px;"> <img id="rating-qr-img" src="" alt="Rating QR Code" class="img-fluid border p-3 bg-white shadow-sm rounded-3" style="max-width: 180px;">
</div> </div>
<div class="alert alert-light border small text-break text-center mb-0" id="rating-url-text"></div> <div class="alert alert-light border extra-small text-break text-center mx-4 mb-0" id="rating-url-text"></div>
</div> </div>
<div class="modal-footer border-top-0 justify-content-center pb-4"> <div class="modal-footer border-top-0 justify-content-center pb-4">
<button class="btn btn-primary px-4 fw-bold" onclick="window.print()"> <button class="btn btn-primary px-4 fw-bold btn-sm rounded-pill" onclick="window.print()">
<i class="bi bi-printer me-2"></i> Print QR Code <i class="bi bi-printer me-2"></i> Print QR Code
</button> </button>
</div> </div>

View File

@ -1,93 +0,0 @@
<?php
require_once __DIR__ . '/../db/config.php';
$pdo = db();
if (!isset($_GET['id'])) {
header("Location: suppliers.php");
exit;
}
$id = $_GET['id'];
$message = '';
// Fetch Supplier
$stmt = $pdo->prepare("SELECT * FROM suppliers WHERE id = ?");
$stmt->execute([$id]);
$supplier = $stmt->fetch();
if (!$supplier) {
header("Location: suppliers.php");
exit;
}
// Handle Update
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'];
$contact_person = $_POST['contact_person'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$vat_no = $_POST['vat_no'];
$address = $_POST['address'];
$stmt = $pdo->prepare("UPDATE suppliers SET name = ?, contact_person = ?, email = ?, phone = ?, vat_no = ?, address = ? WHERE id = ?");
if ($stmt->execute([$name, $contact_person, $email, $phone, $vat_no, $address, $id])) {
$message = '<div class="alert alert-success">Supplier updated successfully!</div>';
// Refresh data
$stmt = $pdo->prepare("SELECT * FROM suppliers WHERE id = ?");
$stmt->execute([$id]);
$supplier = $stmt->fetch();
} else {
$message = '<div class="alert alert-danger">Error updating supplier.</div>';
}
}
include 'includes/header.php';
?>
<div class="mb-4">
<a href="suppliers.php" class="text-decoration-none text-muted mb-2 d-inline-block"><i class="bi bi-arrow-left"></i> Back to Suppliers</a>
<h2 class="fw-bold mb-0">Edit Supplier: <?= htmlspecialchars($supplier['name']) ?></h2>
</div>
<?= $message ?>
<div class="card border-0 shadow-sm">
<div class="card-body">
<form method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Company Name</label>
<input type="text" name="name" class="form-control" value="<?= htmlspecialchars($supplier['name']) ?>" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Contact Person</label>
<input type="text" name="contact_person" class="form-control" value="<?= htmlspecialchars($supplier['contact_person']) ?>">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($supplier['email']) ?>">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Phone</label>
<input type="text" name="phone" class="form-control" value="<?= htmlspecialchars($supplier['phone']) ?>">
</div>
</div>
<div class="mb-3">
<label class="form-label">VAT No</label>
<input type="text" name="vat_no" class="form-control" value="<?= htmlspecialchars($supplier['vat_no']) ?>" placeholder="e.g. GB123456789">
</div>
<div class="mb-3">
<label class="form-label">Address</label>
<textarea name="address" class="form-control" rows="3"><?= htmlspecialchars($supplier['address']) ?></textarea>
</div>
<div class="d-flex justify-content-end gap-2">
<a href="suppliers.php" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
<?php include 'includes/footer.php'; ?>

View File

@ -6,23 +6,44 @@ $pdo = db();
$message = ''; $message = '';
// Handle Add Supplier // Handle Add/Edit Supplier
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'add_supplier') { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if (!has_permission('suppliers_add')) { if ($_POST['action'] === 'add_supplier') {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to add suppliers.</div>'; if (!has_permission('suppliers_add')) {
} else { $message = '<div class="alert alert-danger">Access Denied: You do not have permission to add suppliers.</div>';
$name = $_POST['name'];
$contact_person = $_POST['contact_person'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$vat_no = $_POST['vat_no'];
$stmt = $pdo->prepare("INSERT INTO suppliers (name, contact_person, email, phone, address, vat_no) VALUES (?, ?, ?, ?, ?, ?)");
if ($stmt->execute([$name, $contact_person, $email, $phone, $address, $vat_no])) {
$message = '<div class="alert alert-success">Supplier added successfully!</div>';
} else { } else {
$message = '<div class="alert alert-danger">Error adding supplier.</div>'; $name = $_POST['name'];
$contact_person = $_POST['contact_person'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$vat_no = $_POST['vat_no'];
$stmt = $pdo->prepare("INSERT INTO suppliers (name, contact_person, email, phone, address, vat_no) VALUES (?, ?, ?, ?, ?, ?)");
if ($stmt->execute([$name, $contact_person, $email, $phone, $address, $vat_no])) {
$message = '<div class="alert alert-success">Supplier added successfully!</div>';
} else {
$message = '<div class="alert alert-danger">Error adding supplier.</div>';
}
}
} elseif ($_POST['action'] === 'edit_supplier') {
if (!has_permission('suppliers_add')) {
$message = '<div class="alert alert-danger">Access Denied: You do not have permission to edit suppliers.</div>';
} else {
$id = $_POST['id'];
$name = $_POST['name'];
$contact_person = $_POST['contact_person'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$vat_no = $_POST['vat_no'];
$stmt = $pdo->prepare("UPDATE suppliers SET name = ?, contact_person = ?, email = ?, phone = ?, address = ?, vat_no = ? WHERE id = ?");
if ($stmt->execute([$name, $contact_person, $email, $phone, $address, $vat_no, $id])) {
$message = '<div class="alert alert-success">Supplier updated successfully!</div>';
} else {
$message = '<div class="alert alert-danger">Error updating supplier.</div>';
}
} }
} }
} }
@ -50,7 +71,7 @@ include 'includes/header.php';
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Suppliers</h2> <h2 class="fw-bold mb-0">Suppliers</h2>
<?php if (has_permission('suppliers_add')): ?> <?php if (has_permission('suppliers_add')): ?>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addSupplierModal"> <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#supplierModal" onclick="openAddModal()">
<i class="bi bi-plus-lg"></i> Add Supplier <i class="bi bi-plus-lg"></i> Add Supplier
</button> </button>
<?php endif; ?> <?php endif; ?>
@ -88,7 +109,11 @@ include 'includes/header.php';
<td> <td>
<div class="btn-group"> <div class="btn-group">
<?php if (has_permission('suppliers_add')): ?> <?php if (has_permission('suppliers_add')): ?>
<a href="supplier_edit.php?id=<?= $supplier['id'] ?>" class="btn btn-sm btn-outline-primary" title="Edit Supplier"><i class="bi bi-pencil"></i></a> <button type="button" class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#supplierModal"
onclick="openEditModal(<?= htmlspecialchars(json_encode($supplier)) ?>)"
title="Edit Supplier"><i class="bi bi-pencil"></i></button>
<?php endif; ?> <?php endif; ?>
<?php if (has_permission('suppliers_del')): ?> <?php if (has_permission('suppliers_del')): ?>
@ -113,43 +138,44 @@ include 'includes/header.php';
</div> </div>
</div> </div>
<!-- Add Supplier Modal --> <!-- Supplier Modal -->
<?php if (has_permission('suppliers_add')): ?> <?php if (has_permission('suppliers_add')): ?>
<div class="modal fade" id="addSupplierModal" tabindex="-1"> <div class="modal fade" id="supplierModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Add New Supplier</h5> <h5 class="modal-title" id="supplierModalTitle">Add New Supplier</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div> </div>
<form method="POST"> <form method="POST" id="supplierForm">
<div class="modal-body"> <div class="modal-body">
<input type="hidden" name="action" value="add_supplier"> <input type="hidden" name="action" id="supplierAction" value="add_supplier">
<input type="hidden" name="id" id="supplierId">
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Company Name</label> <label class="form-label">Company Name</label>
<input type="text" name="name" class="form-control" required> <input type="text" name="name" id="supplierName" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Contact Person</label> <label class="form-label">Contact Person</label>
<input type="text" name="contact_person" class="form-control"> <input type="text" name="contact_person" id="supplierContactPerson" class="form-control">
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">Email</label> <label class="form-label">Email</label>
<input type="email" name="email" class="form-control"> <input type="email" name="email" id="supplierEmail" class="form-control">
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">Phone</label> <label class="form-label">Phone</label>
<input type="text" name="phone" class="form-control"> <input type="text" name="phone" id="supplierPhone" class="form-control">
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">VAT No</label> <label class="form-label">VAT No</label>
<input type="text" name="vat_no" class="form-control" placeholder="e.g. GB123456789"> <input type="text" name="vat_no" id="supplierVatNo" class="form-control" placeholder="e.g. GB123456789">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Address</label> <label class="form-label">Address</label>
<textarea name="address" class="form-control" rows="3"></textarea> <textarea name="address" id="supplierAddress" class="form-control" rows="3"></textarea>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -160,6 +186,27 @@ include 'includes/header.php';
</div> </div>
</div> </div>
</div> </div>
<script>
function openAddModal() {
document.getElementById('supplierModalTitle').innerText = 'Add New Supplier';
document.getElementById('supplierAction').value = 'add_supplier';
document.getElementById('supplierForm').reset();
document.getElementById('supplierId').value = '';
}
function openEditModal(supplier) {
document.getElementById('supplierModalTitle').innerText = 'Edit Supplier';
document.getElementById('supplierAction').value = 'edit_supplier';
document.getElementById('supplierId').value = supplier.id;
document.getElementById('supplierName').value = supplier.name;
document.getElementById('supplierContactPerson').value = supplier.contact_person || '';
document.getElementById('supplierEmail').value = supplier.email || '';
document.getElementById('supplierPhone').value = supplier.phone || '';
document.getElementById('supplierVatNo').value = supplier.vat_no || '';
document.getElementById('supplierAddress').value = supplier.address || '';
}
</script>
<?php endif; ?> <?php endif; ?>
<?php include 'includes/footer.php'; ?> <?php include 'includes/footer.php'; ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -23,6 +23,7 @@ document.addEventListener('DOMContentLoaded', () => {
const quickOrderBtn = document.getElementById('quick-order-btn'); const quickOrderBtn = document.getElementById('quick-order-btn');
const placeOrderBtn = document.getElementById('place-order-btn'); const placeOrderBtn = document.getElementById('place-order-btn');
const recallBtn = document.getElementById('recall-bill-btn'); const recallBtn = document.getElementById('recall-bill-btn');
const reprintReceiptBtn = document.getElementById('reprint-receipt-btn');
// Recall Modal // Recall Modal
const recallModalEl = document.getElementById('recallOrderModal'); const recallModalEl = document.getElementById('recallOrderModal');
@ -504,6 +505,11 @@ document.addEventListener('DOMContentLoaded', () => {
}; };
function updateCart() { function updateCart() {
if (reprintReceiptBtn) {
if (currentOrderId) reprintReceiptBtn.classList.remove('d-none');
else reprintReceiptBtn.classList.add('d-none');
}
if (cart.length === 0) { if (cart.length === 0) {
cartItemsContainer.innerHTML = ` cartItemsContainer.innerHTML = `
<div class="text-center text-muted mt-5"> <div class="text-center text-muted mt-5">
@ -514,14 +520,6 @@ document.addEventListener('DOMContentLoaded', () => {
cartTotalPrice.innerText = formatCurrency(0); cartTotalPrice.innerText = formatCurrency(0);
if (quickOrderBtn) quickOrderBtn.disabled = true; if (quickOrderBtn) quickOrderBtn.disabled = true;
if (placeOrderBtn) placeOrderBtn.disabled = true; if (placeOrderBtn) placeOrderBtn.disabled = true;
// RESET current Order ID if cart becomes empty?
// Actually, if we empty the cart, we might want to "cancel" the update?
// No, user can add items back. But if they leave it empty, we can't submit.
// If they start adding items, it's still the recalled order.
// What if they want to START NEW? They should reload or we should provide a Clear button.
// For now, let's assume they continue working on it.
// If they want new, they can refresh or we can add a "New Order" button later.
return; return;
} }
@ -782,101 +780,229 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
} }
window.reprintCurrentReceipt = function() {
if (!currentOrderId) return;
const subtotal = cart.reduce((acc, item) => acc + (item.price * item.quantity), 0);
const discount = parseFloat(cartDiscountInput.value) || 0;
const totalAmount = Math.max(0, subtotal - discount);
const orderType = document.querySelector('input[name="order_type"]:checked').value;
printReceipt({
orderId: currentOrderId,
customer: currentCustomer,
items: [...cart],
total: totalAmount,
discount: discount,
orderType: orderType,
tableNumber: (orderType === 'dine-in') ? currentTableName : null,
date: new Date().toLocaleString() + ' (Reprint)',
paymentMethod: 'Previously Settled',
loyaltyRedeemed: isLoyaltyRedemption
});
};
function printReceipt(data) { function printReceipt(data) {
const width = 300; const settings = (typeof COMPANY_SETTINGS !== "undefined") ? COMPANY_SETTINGS : {};
const height = 600; const width = 400;
const height = 800;
const left = (screen.width - width) / 2; const left = (screen.width - width) / 2;
const top = (screen.height - height) / 2; const top = (screen.height - height) / 2;
const win = window.open('', 'Receipt', `width=${width},height=${height},top=${top},left=${left}`); const win = window.open('', 'Receipt', `width=${width},height=${height},top=${top},left=${left}`);
const tr = {
'Order': 'الطلب',
'Type': 'النوع',
'Date': 'التاريخ',
'Staff': 'الموظف',
'Table': 'طاولة',
'Payment': 'الدفع',
'ITEM': 'الصنف',
'TOTAL': 'المجموع',
'Subtotal': 'المجموع الفرعي',
'Discount': 'الخصم',
'Tax Included': 'شامل الضريبة',
'THANK YOU FOR YOUR VISIT!': 'شكراً لزيارتكم!',
'Please come again.': 'يرجى زيارتنا مرة أخرى.',
'Customer Details': 'تفاصيل العميل',
'Tel': 'هاتف',
'takeaway': 'سفري',
'dine-in': 'محلي',
'delivery': 'توصيل',
'VAT No': 'الرقم الضريبي',
'CTR No': 'رقم السجل التجاري'
};
const itemsHtml = data.items.map(item => ` const itemsHtml = data.items.map(item => `
<tr> <tr>
<td style="padding: 2px 0;"> <td style="padding: 5px 0; border-bottom: 1px solid #eee;">
${item.name} <br> <div style="font-weight: bold;">${item.name}</div>
${item.variant_name ? `<small>(${item.variant_name})</small>` : ''} ${item.variant_name ? `<div style="font-size: 10px; color: #555;">(${item.variant_name})</div>` : ''}
<div style="font-size: 11px;">${item.quantity} x ${formatCurrency(item.price)}</div>
</td> </td>
<td style="text-align: right; vertical-align: top;">${item.quantity} x ${formatCurrency(item.price)}</td> <td style="text-align: right; vertical-align: middle; font-weight: bold;">${formatCurrency(item.quantity * item.price)}</td>
</tr> </tr>
`).join(''); `).join('');
const customerHtml = data.customer ? ` const customerHtml = data.customer ? `
<div style="margin-bottom: 10px; border-bottom: 1px dashed #000; padding-bottom: 5px;"> <div style="margin-bottom: 10px; border: 1px solid #eee; padding: 8px; border-radius: 4px;">
<strong>Customer:</strong><br> <div style="font-weight: bold; text-transform: uppercase; font-size: 10px; color: #666; margin-bottom: 3px;">
${data.customer.name}<br> <span style="float: left;">Customer Details</span>
${data.customer.phone || ''} <span style="float: right;">${tr['Customer Details']}</span>
<div style="clear: both;"></div>
</div>
<div style="font-weight: bold;">${data.customer.name}</div>
${data.customer.phone ? `<div>Tel: ${data.customer.phone}</div>` : ''}
${data.customer.address ? `<div style="font-size: 11px;">${data.customer.address}</div>` : ''}
</div> </div>
` : ''; ` : '';
const tableHtml = data.tableNumber ? `<div>Table: ${data.tableNumber}</div>` : ''; const tableHtml = data.tableNumber ? `
const paymentHtml = data.paymentMethod ? `<div>Payment: ${data.paymentMethod}</div>` : ''; <div style="display: flex; justify-content: space-between;">
const loyaltyHtml = data.loyaltyRedeemed ? `<div><strong>* Free Meal Redeemed *</strong></div>` : ''; <span><strong>Table:</strong> ${data.tableNumber}</span>
<span><strong>${tr['Table']}:</strong> ${data.tableNumber}</span>
</div>` : '';
const paymentHtml = data.paymentMethod ? `
<div style="display: flex; justify-content: space-between;">
<span><strong>Payment:</strong> ${data.paymentMethod}</span>
<span><strong>${tr['Payment']}:</strong> ${data.paymentMethod}</span>
</div>` : '';
const loyaltyHtml = data.loyaltyRedeemed ? `<div style="color: #d63384; font-weight: bold; margin: 5px 0; text-align: center;">* Loyalty Reward Applied *</div>` : '';
const vatRate = parseFloat(settings.vat_rate) || 0;
const subtotal = data.total + data.discount;
const vatAmount = vatRate > 0 ? (data.total * (vatRate / (100 + vatRate))) : 0;
const logoHtml = settings.logo_url ? `<img src="${BASE_URL}${settings.logo_url}" style="max-height: 80px; max-width: 150px; margin-bottom: 10px;">` : '';
const html = ` const html = `
<html> <html dir="ltr">
<head> <head>
<title>Receipt #${data.orderId}</title> <title>Receipt #${data.orderId}</title>
<style> <style>
body { font-family: 'Courier New', monospace; font-size: 12px; margin: 0; padding: 10px; } body {
.header { text-align: center; margin-bottom: 10px; } font-family: 'Courier New', Courier, monospace;
.header h2 { margin: 0; font-size: 16px; } font-size: 13px;
margin: 0;
padding: 15px;
color: #000;
line-height: 1.4;
}
.header { text-align: center; margin-bottom: 15px; }
.header h2 { margin: 0 0 5px 0; font-size: 20px; font-weight: bold; }
.header div { font-size: 12px; }
table { width: 100%; border-collapse: collapse; } table { width: 100%; border-collapse: collapse; }
.totals { margin-top: 10px; border-top: 1px dashed #000; padding-top: 5px; } .divider { border-bottom: 2px dashed #000; margin: 10px 0; }
.footer { text-align: center; margin-top: 20px; font-size: 10px; } .thick-divider { border-bottom: 2px solid #000; margin: 10px 0; }
.totals td { padding: 3px 0; }
.footer { text-align: center; margin-top: 25px; font-size: 12px; }
.order-info { font-size: 11px; margin-bottom: 10px; }
.order-info-row { display: flex; justify-content: space-between; margin-bottom: 2px; }
.rtl { direction: rtl; unicode-bidi: embed; }
@media print { @media print {
.no-print { display: none; } body { width: 80mm; padding: 5mm; }
@page { margin: 0; }
} }
</style> </style>
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<h2>FLATLOGIC POS</h2> ${logoHtml}
<div>123 Main St, City</div> <h2>${settings.company_name}</h2>
<div>Tel: 123-456-7890</div> <div style="font-weight: bold;">${CURRENT_OUTLET.name}</div>
<div>${settings.address}</div>
<div>Tel: ${settings.phone}</div>
${settings.vat_number ? `<div style="margin-top: 4px;">VAT No / الرقم الضريبي: ${settings.vat_number}</div>` : ''}
${settings.ctr_number ? `<div>CTR No / رقم السجل: ${settings.ctr_number}</div>` : ''}
</div> </div>
<div style="margin-bottom: 10px;"> <div class="divider"></div>
<div>Order #${data.orderId}</div>
<div>${data.date}</div> <div class="order-info">
<div>Type: ${data.orderType.toUpperCase()}</div> <div class="order-info-row">
${tableHtml} <span><strong>Order:</strong> #${data.orderId}</span>
${paymentHtml} <span><strong>${tr['Order']}:</strong> #${data.orderId}</span>
${loyaltyHtml} </div>
<div class="order-info-row">
<span><strong>Type:</strong> ${data.orderType.toUpperCase()}</span>
<span><strong>${tr['Type']}:</strong> ${tr[data.orderType] || data.orderType}</span>
</div>
<div class="order-info-row">
<span><strong>Date:</strong> ${data.date}</span>
<span><strong>${tr['Date']}:</strong> ${data.date}</span>
</div>
<div class="order-info-row">
<span><strong>Staff:</strong> ${CURRENT_USER.name}</span>
<span><strong>${tr['Staff']}:</strong> ${CURRENT_USER.name}</span>
</div>
</div> </div>
${tableHtml}
${paymentHtml}
${loyaltyHtml}
<div class="thick-divider"></div>
${customerHtml} ${customerHtml}
<table> <table>
${itemsHtml} <thead>
<tr>
<th style="text-align: left; padding-bottom: 5px;">
ITEM / الصنف
</th>
<th style="text-align: right; padding-bottom: 5px;">
TOTAL / المجموع
</th>
</tr>
</thead>
<tbody>
${itemsHtml}
</tbody>
</table> </table>
<div class="divider"></div>
<div class="totals"> <div class="totals">
<table style="width: 100%"> <table style="width: 100%">
<tr> <tr>
<td>Subtotal</td> <td>Subtotal / ${tr['Subtotal']}</td>
<td style="text-align: right">${formatCurrency(data.total + data.discount)}</td> <td style="text-align: right">${formatCurrency(subtotal)}</td>
</tr> </tr>
${data.discount > 0 ? ` ${data.discount > 0 ? `
<tr> <tr>
<td>Discount</td> <td>Discount / ${tr['Discount']}</td>
<td style="text-align: right">-${formatCurrency(data.discount)}</td> <td style="text-align: right">-${formatCurrency(data.discount)}</td>
</tr>` : ''} </tr>` : ''}
<tr style="font-weight: bold; font-size: 14px;"> ${vatRate > 0 ? `
<td>Total</td> <tr>
<td style="text-align: right">${formatCurrency(data.total)}</td> <td style="font-size: 11px;">Tax Incl. (${vatRate}%) / ${tr['Tax Included']}</td>
<td style="text-align: right">${formatCurrency(vatAmount)}</td>
</tr>` : ''}
<tr style="font-weight: bold; font-size: 18px;">
<td style="padding-top: 10px;">TOTAL / ${tr['TOTAL']}</td>
<td style="text-align: right; padding-top: 10px;">${formatCurrency(data.total)}</td>
</tr> </tr>
</table> </table>
</div> </div>
<div class="thick-divider"></div>
<div class="footer"> <div class="footer">
Thank you for your visit!<br> <div style="font-weight: bold; font-size: 14px; margin-bottom: 2px;">THANK YOU FOR YOUR VISIT!</div>
Please come again. <div style="font-weight: bold; font-size: 14px; margin-bottom: 5px;" class="rtl">${tr['THANK YOU FOR YOUR VISIT!']}</div>
<div>Please come again.</div>
<div class="rtl">${tr['Please come again.']}</div>
${settings.email ? `<div style="margin-top: 5px; font-size: 10px;">${settings.email}</div>` : ''}
<div style="margin-top: 15px; font-size: 10px; color: #888;">Powered by Flatlogic POS</div>
</div> </div>
<script> <script>
window.onload = function() { window.onload = function() {
window.print(); window.print();
setTimeout(function() { window.close(); }, 500); setTimeout(function() { window.close(); }, 1500);
} }
</script> </script>
</body> </body>

View File

@ -0,0 +1,2 @@
-- Add discount column to orders table
ALTER TABLE orders ADD COLUMN discount DECIMAL(10, 2) DEFAULT 0.00 AFTER total_amount;

View File

@ -0,0 +1,18 @@
-- Add address column to customers table
SET @dbname = DATABASE();
SET @tablename = "customers";
SET @columnname = "address";
SET @preparedStatement = (SELECT IF(
(
SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
(table_name = @tablename)
AND (table_schema = @dbname)
AND (column_name = @columnname)
) > 0,
"SELECT 1",
"ALTER TABLE customers ADD COLUMN address TEXT;"
));
PREPARE alterIfNotExists FROM @preparedStatement;
EXECUTE alterIfNotExists;
DEALLOCATE PREPARE alterIfNotExists;

View File

@ -0,0 +1,2 @@
-- Add display_layout column to ads_images table
ALTER TABLE ads_images ADD COLUMN IF NOT EXISTS display_layout ENUM('both', 'split', 'fullscreen') DEFAULT 'both' AFTER is_active;

View File

@ -485,6 +485,7 @@ if (!$loyalty_settings) {
const LOYALTY_SETTINGS = <?= json_encode($loyalty_settings) ?>; const LOYALTY_SETTINGS = <?= json_encode($loyalty_settings) ?>;
const PRODUCT_VARIANTS = <?= json_encode($variants_by_product) ?>; const PRODUCT_VARIANTS = <?= json_encode($variants_by_product) ?>;
const PAYMENT_TYPES = <?= json_encode($payment_types) ?>; const PAYMENT_TYPES = <?= json_encode($payment_types) ?>;
const CURRENT_USER = <?= json_encode(['id' => $currentUser['id'], 'name' => $currentUser['name']]) ?>;
const CURRENT_OUTLET = <?= json_encode(['id' => $outlet_id, 'name' => $current_outlet_name]) ?>; const CURRENT_OUTLET = <?= json_encode(['id' => $outlet_id, 'name' => $current_outlet_name]) ?>;
const BASE_URL = '<?= get_base_url() ?>'; const BASE_URL = '<?= get_base_url() ?>';