symbol added for currency
This commit is contained in:
parent
13a3054fd1
commit
ac32148f05
@ -19,6 +19,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$vat_rate = $_POST['vat_rate'] ?? 0;
|
||||
$currency_symbol = $_POST['currency_symbol'] ?? '$';
|
||||
$currency_decimals = $_POST['currency_decimals'] ?? 2;
|
||||
$currency_position = $_POST['currency_position'] ?? 'before';
|
||||
$ctr_number = $_POST['ctr_number'] ?? '';
|
||||
$vat_number = $_POST['vat_number'] ?? '';
|
||||
$commission_enabled = isset($_POST['commission_enabled']) ? 1 : 0;
|
||||
@ -67,11 +68,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$exists = $pdo->query("SELECT COUNT(*) FROM company_settings")->fetchColumn();
|
||||
|
||||
if ($exists) {
|
||||
$stmt = $pdo->prepare("UPDATE company_settings SET company_name=?, address=?, phone=?, email=?, vat_rate=?, currency_symbol=?, currency_decimals=?, ctr_number=?, vat_number=?, logo_url=?, favicon_url=?, commission_enabled=?, updated_at=NOW()");
|
||||
$stmt->execute([$company_name, $address, $phone, $email, $vat_rate, $currency_symbol, $currency_decimals, $ctr_number, $vat_number, $logo_url, $favicon_url, $commission_enabled]);
|
||||
$stmt = $pdo->prepare("UPDATE company_settings SET company_name=?, address=?, phone=?, email=?, vat_rate=?, currency_symbol=?, currency_decimals=?, currency_position=?, ctr_number=?, vat_number=?, logo_url=?, favicon_url=?, commission_enabled=?, updated_at=NOW()");
|
||||
$stmt->execute([$company_name, $address, $phone, $email, $vat_rate, $currency_symbol, $currency_decimals, $currency_position, $ctr_number, $vat_number, $logo_url, $favicon_url, $commission_enabled]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("INSERT INTO company_settings (company_name, address, phone, email, vat_rate, currency_symbol, currency_decimals, ctr_number, vat_number, logo_url, favicon_url, commission_enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$company_name, $address, $phone, $email, $vat_rate, $currency_symbol, $currency_decimals, $ctr_number, $vat_number, $logo_url, $favicon_url, $commission_enabled]);
|
||||
$stmt = $pdo->prepare("INSERT INTO company_settings (company_name, address, phone, email, vat_rate, currency_symbol, currency_decimals, currency_position, ctr_number, vat_number, logo_url, favicon_url, commission_enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$company_name, $address, $phone, $email, $vat_rate, $currency_symbol, $currency_decimals, $currency_position, $ctr_number, $vat_number, $logo_url, $favicon_url, $commission_enabled]);
|
||||
}
|
||||
|
||||
$message = '<div class="alert alert-success">Company settings updated successfully!</div>';
|
||||
@ -138,18 +139,25 @@ include 'includes/header.php';
|
||||
<h5 class="mb-3">Financial Settings</h5>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">VAT Rate (%)</label>
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" name="vat_rate" class="form-control" value="<?= htmlspecialchars($settings['vat_rate'] ?? 0) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Currency Symbol</label>
|
||||
<input type="text" name="currency_symbol" class="form-control" value="<?= htmlspecialchars($settings['currency_symbol'] ?? '$') ?>" placeholder="e.g. $, €, £" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Currency Position</label>
|
||||
<select name="currency_position" class="form-select" <?= !has_permission('settings_add') ? 'disabled' : '' ?>>
|
||||
<option value="before" <?= ($settings['currency_position'] ?? 'before') === 'before' ? 'selected' : '' ?>>Before (e.g. $10.00)</option>
|
||||
<option value="after" <?= ($settings['currency_position'] ?? 'before') === 'after' ? 'selected' : '' ?>>After (e.g. 10.00 OMR)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label class="form-label">Decimal Places</label>
|
||||
<input type="number" name="currency_decimals" class="form-control" value="<?= htmlspecialchars($settings['currency_decimals'] ?? 2) ?>" min="0" max="4" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||
</div>
|
||||
|
||||
@ -489,6 +489,16 @@ function can_view($module) {
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= isActive('reports.php') ?>" href="reports.php">
|
||||
<i class="bi bi-graph-up me-2"></i> <?= t('daily_reports') ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= isActive('report_products.php') ?>" href="report_products.php">
|
||||
<i class="bi bi-box-seam me-2"></i> Product Sales
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= isActive('report_staff.php') ?>" href="report_staff.php">
|
||||
<i class="bi bi-person-badge me-2"></i> Staff Sales
|
||||
</a>
|
||||
</li>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -94,7 +94,7 @@ Thank you for dining with *{company_name}*! 🍽️
|
||||
*Order Details:*
|
||||
{order_details}
|
||||
|
||||
Total: *{total_amount}* OMR
|
||||
Total: *{total_amount}*
|
||||
|
||||
You've earned *{points_earned} points* with this order.
|
||||
💰 *Current Balance: {new_balance} points*";
|
||||
@ -193,7 +193,7 @@ require_once __DIR__ . '/includes/header.php';
|
||||
<div class="form-text mt-2">
|
||||
<strong>Available Variables:</strong><br>
|
||||
<code>{customer_name}</code>, <code>{company_name}</code>, <code>{order_id}</code>,
|
||||
<code>{order_details}</code> (list of items), <code>{total_amount}</code>,
|
||||
<code>{order_details}</code> (list of items), <code>{total_amount}</code>, <code>{currency_symbol}</code>,
|
||||
<code>{points_earned}</code>, <code>{points_redeemed}</code>, <code>{new_balance}</code>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
147
admin/report_products.php
Normal file
147
admin/report_products.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
// Check permission
|
||||
if (function_exists('require_permission')) {
|
||||
require_permission('reports_view');
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Date and Outlet Filter
|
||||
$startDate = $_GET['start_date'] ?? date('Y-m-d');
|
||||
$endDate = $_GET['end_date'] ?? date('Y-m-d');
|
||||
$outletId = $_GET['outlet_id'] ?? '';
|
||||
|
||||
// Fetch Outlets for filter
|
||||
$outletsStmt = $pdo->query("SELECT id, name, name_ar FROM outlets WHERE is_deleted = 0 ORDER BY name ASC");
|
||||
$allOutlets = $outletsStmt->fetchAll();
|
||||
|
||||
// Base query additions
|
||||
$outletCondition = "";
|
||||
$queryParams = [$startDate, $endDate];
|
||||
if (!empty($outletId)) {
|
||||
$outletCondition = " AND o.outlet_id = ? ";
|
||||
$queryParams[] = $outletId;
|
||||
}
|
||||
|
||||
// Product Sales & Profit
|
||||
$productSalesStmt = $pdo->prepare("
|
||||
SELECT
|
||||
p.name as product_name,
|
||||
p.name_ar as product_name_ar,
|
||||
SUM(oi.quantity) as qty_sold,
|
||||
SUM(oi.quantity * oi.unit_price) as total_amount,
|
||||
SUM(oi.quantity * (oi.unit_price - IFNULL(p.cost_price, 0))) as total_profit
|
||||
FROM order_items oi
|
||||
JOIN orders o ON oi.order_id = o.id
|
||||
JOIN products p ON oi.product_id = p.id
|
||||
WHERE DATE(o.created_at) BETWEEN ? AND ?
|
||||
$outletCondition
|
||||
GROUP BY p.id
|
||||
ORDER BY qty_sold DESC
|
||||
");
|
||||
$productSalesStmt->execute($queryParams);
|
||||
$productSales = $productSalesStmt->fetchAll();
|
||||
|
||||
// Totals for the footer
|
||||
$totalQty = 0;
|
||||
$totalAmount = 0;
|
||||
$totalProfit = 0;
|
||||
foreach ($productSales as $p) {
|
||||
$totalQty += $p['qty_sold'];
|
||||
$totalAmount += $p['total_amount'];
|
||||
$totalProfit += $p['total_profit'];
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container-fluid p-0">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
|
||||
<div>
|
||||
<h2 class="fw-bold mb-1">Product Sales Report</h2>
|
||||
<p class="text-muted mb-0">Detailed breakdown of product performance and profitability</p>
|
||||
</div>
|
||||
<form class="row g-2 align-items-center" method="GET">
|
||||
<div class="col-auto">
|
||||
<select name="outlet_id" class="form-select" style="min-width: 180px;">
|
||||
<option value="">All Outlets</option>
|
||||
<?php foreach ($allOutlets as $o): ?>
|
||||
<option value="<?= $o['id'] ?>" <?= $outletId == $o['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($o['name']) ?> <?= $o['name_ar'] ? '('.$o['name_ar'].')' : '' ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-calendar"></i></span>
|
||||
<input type="date" name="start_date" class="form-control border-start-0" value="<?= htmlspecialchars($startDate) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-calendar"></i></span>
|
||||
<input type="date" name="end_date" class="form-control border-start-0" value="<?= htmlspecialchars($endDate) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary px-4 text-nowrap">Filter</button>
|
||||
<a href="report_products.php" class="btn btn-light text-nowrap">Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-box-seam me-2 text-dark"></i> Products</h5>
|
||||
<button onclick="window.print()" class="btn btn-sm btn-outline-secondary"><i class="bi bi-printer me-1"></i> Print</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-3">Product Name</th>
|
||||
<th class="text-center">Qty Sold</th>
|
||||
<th class="text-end">Total Amount</th>
|
||||
<th class="text-end pe-3">Estimated Profit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($productSales)): ?>
|
||||
<tr><td colspan="4" class="text-center py-5 text-muted">No sales found for the selected criteria</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($productSales as $prod): ?>
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<div class="fw-bold text-dark"><?= htmlspecialchars($prod['product_name']) ?></div>
|
||||
<?php if ($prod['product_name_ar']): ?>
|
||||
<small class="text-muted d-block"><?= htmlspecialchars($prod['product_name_ar']) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= number_format($prod['qty_sold']) ?></td>
|
||||
<td class="text-end fw-bold"><?= format_currency($prod['total_amount']) ?></td>
|
||||
<td class="text-end pe-3 fw-bold text-success"><?= format_currency($prod['total_profit']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
<?php if (!empty($productSales)): ?>
|
||||
<tfoot class="bg-light fw-bold">
|
||||
<tr>
|
||||
<td class="ps-3 text-uppercase">Total</td>
|
||||
<td class="text-center"><?= number_format($totalQty) ?></td>
|
||||
<td class="text-end"><?= format_currency($totalAmount) ?></td>
|
||||
<td class="text-end pe-3 text-success"><?= format_currency($totalProfit) ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
171
admin/report_staff.php
Normal file
171
admin/report_staff.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/header.php';
|
||||
|
||||
// Check permission
|
||||
if (function_exists('require_permission')) {
|
||||
require_permission('reports_view');
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Date and Outlet Filter
|
||||
$startDate = $_GET['start_date'] ?? date('Y-m-d');
|
||||
$endDate = $_GET['end_date'] ?? date('Y-m-d');
|
||||
$outletId = $_GET['outlet_id'] ?? '';
|
||||
|
||||
// Fetch Outlets for filter
|
||||
$outletsStmt = $pdo->query("SELECT id, name, name_ar FROM outlets WHERE is_deleted = 0 ORDER BY name ASC");
|
||||
$allOutlets = $outletsStmt->fetchAll();
|
||||
|
||||
// Base query additions
|
||||
$outletCondition = "";
|
||||
$queryParams = [$startDate, $endDate];
|
||||
if (!empty($outletId)) {
|
||||
$outletCondition = " AND o.outlet_id = ? ";
|
||||
$queryParams[] = $outletId;
|
||||
}
|
||||
|
||||
// Staff Sales by Payment Type
|
||||
$staffPaymentStmt = $pdo->prepare("
|
||||
SELECT
|
||||
u.full_name as staff_name,
|
||||
u.full_name_ar as staff_name_ar,
|
||||
pt.name as payment_name,
|
||||
SUM(o.total_amount) as total_amount
|
||||
FROM orders o
|
||||
JOIN users u ON o.user_id = u.id
|
||||
LEFT JOIN payment_types pt ON o.payment_type_id = pt.id
|
||||
WHERE DATE(o.created_at) BETWEEN ? AND ?
|
||||
$outletCondition
|
||||
GROUP BY o.user_id, o.payment_type_id
|
||||
ORDER BY u.full_name ASC, total_amount DESC
|
||||
");
|
||||
$staffPaymentStmt->execute($queryParams);
|
||||
$staffPaymentSales = $staffPaymentStmt->fetchAll();
|
||||
|
||||
// Summary by payment type
|
||||
$paymentSummary = [];
|
||||
$grandTotal = 0;
|
||||
foreach ($staffPaymentSales as $row) {
|
||||
$pName = $row['payment_name'] ?? 'N/A';
|
||||
if (!isset($paymentSummary[$pName])) {
|
||||
$paymentSummary[$pName] = 0;
|
||||
}
|
||||
$paymentSummary[$pName] += $row['total_amount'];
|
||||
$grandTotal += $row['total_amount'];
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container-fluid p-0">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
|
||||
<div>
|
||||
<h2 class="fw-bold mb-1">Staff Sales Report</h2>
|
||||
<p class="text-muted mb-0">Sales breakdown by staff and payment method</p>
|
||||
</div>
|
||||
<form class="row g-2 align-items-center" method="GET">
|
||||
<div class="col-auto">
|
||||
<select name="outlet_id" class="form-select" style="min-width: 180px;">
|
||||
<option value="">All Outlets</option>
|
||||
<?php foreach ($allOutlets as $o): ?>
|
||||
<option value="<?= $o['id'] ?>" <?= $outletId == $o['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($o['name']) ?> <?= $o['name_ar'] ? '('.$o['name_ar'].')' : '' ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-calendar"></i></span>
|
||||
<input type="date" name="start_date" class="form-control border-start-0" value="<?= htmlspecialchars($startDate) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-white border-end-0 text-muted"><i class="bi bi-calendar"></i></span>
|
||||
<input type="date" name="end_date" class="form-control border-start-0" value="<?= htmlspecialchars($endDate) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary px-4 text-nowrap">Filter</button>
|
||||
<a href="report_staff.php" class="btn btn-light text-nowrap">Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-8">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-person-badge me-2 text-primary"></i> Detailed Staff Sales</h5>
|
||||
<button onclick="window.print()" class="btn btn-sm btn-outline-secondary"><i class="bi bi-printer me-1"></i> Print</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-3">Staff Name</th>
|
||||
<th>Payment Type</th>
|
||||
<th class="text-end pe-3">Total Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($staffPaymentSales)): ?>
|
||||
<tr><td colspan="3" class="text-center py-5 text-muted">No sales found for the selected criteria</td></tr>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$currentStaff = '';
|
||||
foreach ($staffPaymentSales as $row):
|
||||
$showStaff = ($currentStaff != $row['staff_name']);
|
||||
$currentStaff = $row['staff_name'];
|
||||
?>
|
||||
<tr class="<?= $showStaff ? 'border-top' : '' ?>">
|
||||
<td class="ps-3">
|
||||
<?php if ($showStaff): ?>
|
||||
<div class="fw-bold text-dark"><?= htmlspecialchars($row['staff_name']) ?></div>
|
||||
<?php if ($row['staff_name_ar']): ?>
|
||||
<small class="text-muted"><?= htmlspecialchars($row['staff_name_ar']) ?></small>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-light text-dark border"><?= htmlspecialchars($row['payment_name'] ?? 'N/A') ?></span>
|
||||
</td>
|
||||
<td class="text-end pe-3 fw-bold"><?= format_currency($row['total_amount']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white py-3 border-0">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-pie-chart me-2 text-success"></i> Payment Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($paymentSummary as $pName => $amount): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center px-0">
|
||||
<span><?= htmlspecialchars($pName) ?></span>
|
||||
<span class="fw-bold"><?= format_currency($amount) ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center px-0 bg-light mt-2 p-2 rounded">
|
||||
<span class="fw-bold">Grand Total</span>
|
||||
<span class="fw-bold text-primary fs-5"><?= format_currency($grandTotal) ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||
@ -14,7 +14,7 @@ $endDate = $_GET['end_date'] ?? date('Y-m-d');
|
||||
$outletId = $_GET['outlet_id'] ?? '';
|
||||
|
||||
// Fetch Outlets for filter
|
||||
$outletsStmt = $pdo->query("SELECT id, name, name_ar FROM outlets ORDER BY name ASC");
|
||||
$outletsStmt = $pdo->query("SELECT id, name, name_ar FROM outlets WHERE is_deleted = 0 ORDER BY name ASC");
|
||||
$allOutlets = $outletsStmt->fetchAll();
|
||||
|
||||
// Base query additions
|
||||
@ -124,7 +124,7 @@ $netProfit = ($summary['total_revenue'] ?? 0) - $totalExpenses;
|
||||
<div class="container-fluid p-0">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-3">
|
||||
<div>
|
||||
<h2 class="fw-bold mb-1">Daily Reports</h2>
|
||||
<h2 class="fw-bold mb-1">Business Reports</h2>
|
||||
<p class="text-muted mb-0">Overview of your business performance</p>
|
||||
</div>
|
||||
<form class="row g-2 align-items-center" method="GET">
|
||||
@ -216,86 +216,6 @@ $netProfit = ($summary['total_revenue'] ?? 0) - $totalExpenses;
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Sales by Staff -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3 border-0">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-people me-2 text-primary"></i> Sales by Staff</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-3">Staff Name</th>
|
||||
<th class="text-center">Orders</th>
|
||||
<th class="text-end pe-3">Total Sales</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($staffSales)): ?>
|
||||
<tr><td colspan="3" class="text-center py-4 text-muted">No data found for this period</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($staffSales as $staff): ?>
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<div class="fw-bold"><?= htmlspecialchars($staff['staff_name'] ?: $staff['username']) ?></div>
|
||||
<?php if ($staff['staff_name_ar']): ?>
|
||||
<small class="text-muted"><?= htmlspecialchars($staff['staff_name_ar']) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= $staff['order_count'] ?></td>
|
||||
<td class="text-end pe-3 fw-bold text-primary"><?= format_currency($staff['total_sales']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sales by Outlet -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3 border-0">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-shop me-2 text-success"></i> Sales by Outlet</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-3">Outlet</th>
|
||||
<th class="text-center">Orders</th>
|
||||
<th class="text-end pe-3">Total Sales</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($outletSales)): ?>
|
||||
<tr><td colspan="3" class="text-center py-4 text-muted">No data found for this period</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($outletSales as $outlet): ?>
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<div class="fw-bold"><?= htmlspecialchars($outlet['outlet_name']) ?></div>
|
||||
<?php if ($outlet['outlet_name_ar']): ?>
|
||||
<small class="text-muted"><?= htmlspecialchars($outlet['outlet_name_ar']) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= $outlet['order_count'] ?></td>
|
||||
<td class="text-end pe-3 fw-bold text-success"><?= format_currency($outlet['total_sales']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sales by Category -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
@ -373,6 +293,86 @@ $netProfit = ($summary['total_revenue'] ?? 0) - $totalExpenses;
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sales by Staff -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3 border-0">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-people me-2 text-primary"></i> Sales Summary by Staff</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-3">Staff Name</th>
|
||||
<th class="text-center">Orders</th>
|
||||
<th class="text-end pe-3">Total Sales</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($staffSales)): ?>
|
||||
<tr><td colspan="3" class="text-center py-4 text-muted">No data found for this period</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($staffSales as $staff): ?>
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<div class="fw-bold"><?= htmlspecialchars($staff['staff_name'] ?: $staff['username']) ?></div>
|
||||
<?php if ($staff['staff_name_ar']): ?>
|
||||
<small class="text-muted"><?= htmlspecialchars($staff['staff_name_ar']) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= $staff['order_count'] ?></td>
|
||||
<td class="text-end pe-3 fw-bold text-primary"><?= format_currency($staff['total_sales']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sales by Outlet -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3 border-0">
|
||||
<h5 class="fw-bold mb-0"><i class="bi bi-shop me-2 text-success"></i> Sales by Outlet</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-3">Outlet</th>
|
||||
<th class="text-center">Orders</th>
|
||||
<th class="text-end pe-3">Total Sales</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($outletSales)): ?>
|
||||
<tr><td colspan="3" class="text-center py-4 text-muted">No data found for this period</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($outletSales as $outlet): ?>
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<div class="fw-bold"><?= htmlspecialchars($outlet['outlet_name']) ?></div>
|
||||
<?php if ($outlet['outlet_name_ar']): ?>
|
||||
<small class="text-muted"><?= htmlspecialchars($outlet['outlet_name_ar']) ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center"><?= $outlet['order_count'] ?></td>
|
||||
<td class="text-end pe-3 fw-bold text-success"><?= format_currency($outlet['total_sales']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -269,8 +269,8 @@ try {
|
||||
$payment_type_id = !empty($data['payment_type_id']) ? intval($data['payment_type_id']) : null;
|
||||
|
||||
// Commission Calculation
|
||||
$commission_amount = 0;
|
||||
$companySettings = get_company_settings();
|
||||
$commission_amount = 0;
|
||||
if (!empty($companySettings['commission_enabled']) && $user_id) {
|
||||
$userStmt = $pdo->prepare("SELECT commission_rate FROM users WHERE id = ?");
|
||||
$userStmt->execute([$user_id]);
|
||||
@ -352,8 +352,9 @@ try {
|
||||
try {
|
||||
$final_points = $current_points - $points_deducted + ($loyalty_enabled ? $points_awarded : 0);
|
||||
$wablas = new WablasService($pdo);
|
||||
$companySettings = get_company_settings();
|
||||
$company_name = $companySettings['company_name'] ?? 'Flatlogic POS';
|
||||
$currency_symbol = $companySettings['currency_symbol'] ?? 'OMR';
|
||||
$currency_position = $companySettings['currency_position'] ?? 'after';
|
||||
|
||||
$stmt = $pdo->prepare("SELECT setting_value FROM integration_settings WHERE provider = 'wablas' AND setting_key = 'order_template'");
|
||||
$stmt->execute();
|
||||
@ -367,7 +368,7 @@ Thank you for dining with *{company_name}*! 🍽️
|
||||
*Order Details:*
|
||||
{order_details}
|
||||
|
||||
Total: *{total_amount}* OMR
|
||||
Total: *{total_amount}*
|
||||
|
||||
You've earned *{points_earned} points* with this order.
|
||||
💰 *Current Balance: {new_balance} points*
|
||||
@ -382,12 +383,20 @@ You've earned *{points_earned} points* with this order.
|
||||
: "You need *" . ($points_threshold - $final_points) . " more points* to unlock a free meal.";
|
||||
}
|
||||
|
||||
$formatted_total = number_format($final_total, (int)($companySettings['currency_decimals'] ?? 2));
|
||||
if ($currency_position === 'after') {
|
||||
$total_with_currency = $formatted_total . " " . $currency_symbol;
|
||||
} else {
|
||||
$total_with_currency = $currency_symbol . $formatted_total;
|
||||
}
|
||||
|
||||
$replacements = [
|
||||
'{customer_name}' => $customer_name,
|
||||
'{company_name}' => $company_name,
|
||||
'{order_id}' => $order_id,
|
||||
'{order_details}' => implode("\n", $order_items_list),
|
||||
'{total_amount}' => number_format($final_total, 3),
|
||||
'{total_amount}' => $total_with_currency,
|
||||
'{currency_symbol}' => $currency_symbol,
|
||||
'{points_earned}' => $loyalty_enabled ? $points_awarded : 0,
|
||||
'{points_redeemed}' => $points_deducted,
|
||||
'{new_balance}' => $final_points,
|
||||
|
||||
@ -32,6 +32,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const settings = (typeof COMPANY_SETTINGS !== 'undefined') ? COMPANY_SETTINGS : {
|
||||
currency_symbol: '$',
|
||||
currency_decimals: 2,
|
||||
currency_position: 'before',
|
||||
vat_rate: 0
|
||||
};
|
||||
|
||||
@ -101,7 +102,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
function formatCurrency(amount) {
|
||||
const symbol = settings.currency_symbol || '$';
|
||||
const decimals = parseInt(settings.currency_decimals || 2);
|
||||
return symbol + parseFloat(Math.abs(amount)).toFixed(decimals);
|
||||
const position = settings.currency_position || 'before';
|
||||
const formatted = parseFloat(Math.abs(amount)).toFixed(decimals);
|
||||
|
||||
if (position === 'after') {
|
||||
return formatted + ' ' + symbol;
|
||||
} else {
|
||||
return symbol + formatted;
|
||||
}
|
||||
}
|
||||
|
||||
function filterProducts() {
|
||||
@ -427,11 +435,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (data.success && data.tables.length > 0) {
|
||||
renderTables(data.tables);
|
||||
} else {
|
||||
grid.innerHTML = `<div class=\"p-4 text-center text-muted w-100\">${_t("none")}</div>`;
|
||||
grid.innerHTML = `<div class=\"p-4 text-center text-muted w-100">${_t("none")}</div>`;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
grid.innerHTML = `<div class=\"alert alert-danger w-100\">${_t("error")}</div>`;
|
||||
grid.innerHTML = `<div class=\"alert alert-danger w-100">${_t("error")}</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
@ -450,7 +458,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
for (const area in areas) {
|
||||
const areaHeader = document.createElement("div");
|
||||
areaHeader.className = "col-12 mt-3";
|
||||
areaHeader.innerHTML = `<h6 class=\"fw-bold text-muted text-uppercase small border-bottom pb-2\">${area}</h6>`;
|
||||
areaHeader.innerHTML = `<h6 class=\"fw-bold text-muted text-uppercase small border-bottom pb-2">${area}</h6>`;
|
||||
grid.appendChild(areaHeader);
|
||||
|
||||
areas[area].forEach(table => {
|
||||
|
||||
5
db/migrations/039_add_currency_position.sql
Normal file
5
db/migrations/039_add_currency_position.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- Add currency position setting
|
||||
ALTER TABLE company_settings ADD COLUMN currency_position ENUM('before', 'after') DEFAULT 'after';
|
||||
|
||||
-- Update existing Omani settings to use 'after' as it's more common for OMR
|
||||
UPDATE company_settings SET currency_position = 'after' WHERE currency_symbol IN ('OMR', 'ر.ع.', 'OMRR');
|
||||
@ -22,7 +22,8 @@ function get_company_settings() {
|
||||
'email' => 'info@restaurant.com',
|
||||
'vat_rate' => 0.00,
|
||||
'currency_symbol' => '$',
|
||||
'currency_decimals' => 2
|
||||
'currency_decimals' => 2,
|
||||
'currency_position' => 'before'
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -32,7 +33,17 @@ function get_company_settings() {
|
||||
// Function to format currency using settings
|
||||
function format_currency($amount) {
|
||||
$settings = get_company_settings();
|
||||
return ($settings['currency_symbol'] ?? '$') . number_format((float)$amount, (int)($settings['currency_decimals'] ?? 2));
|
||||
$symbol = $settings['currency_symbol'] ?? '$';
|
||||
$decimals = (int)($settings['currency_decimals'] ?? 2);
|
||||
$position = $settings['currency_position'] ?? 'before';
|
||||
|
||||
$formatted_number = number_format((float)$amount, $decimals);
|
||||
|
||||
if ($position === 'after') {
|
||||
return $formatted_number . ' ' . $symbol;
|
||||
} else {
|
||||
return $symbol . $formatted_number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
45
qorder.php
45
qorder.php
@ -143,12 +143,11 @@ foreach ($variants_raw as $v) {
|
||||
<img src="https://picsum.photos/seed/<?= $product['id'] ?>/400/300" class="card-img-top product-img" alt="<?= htmlspecialchars($product['name']) ?>">
|
||||
<div class="badge-price text-primary">
|
||||
<?php if ($is_promo): ?>
|
||||
<span class="text-danger fw-bold"><?= number_format($effective_price, 3) ?></span>
|
||||
<small class="text-muted text-decoration-line-through ms-1" style="font-size: 0.7rem;"><?= number_format((float)$product['price'], 3) ?></small>
|
||||
<span class="text-danger fw-bold"><?= format_currency($effective_price) ?></span>
|
||||
<small class="text-muted text-decoration-line-through ms-1" style="font-size: 0.7rem;"><?= format_currency((float)$product['price']) ?></small>
|
||||
<?php else: ?>
|
||||
<?= number_format((float)$product['price'], 3) ?>
|
||||
<?= format_currency((float)$product['price']) ?>
|
||||
<?php endif; ?>
|
||||
<span data-t="currency">OMR</span>
|
||||
</div>
|
||||
<?php if ($is_promo): ?>
|
||||
<div class="position-absolute top-0 start-0 m-2">
|
||||
@ -177,7 +176,7 @@ foreach ($variants_raw as $v) {
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<div class="small text-muted"><span id="cart-items-count">0</span> <span data-t="items">Items</span></div>
|
||||
<div class="fw-bold fs-5 text-primary" id="cart-total-display">0.000 OMR</div>
|
||||
<div class="fw-bold fs-5 text-primary" id="cart-total-display"><?= format_currency(0) ?></div>
|
||||
</div>
|
||||
<button class="btn btn-primary px-4 fw-bold" onclick="showCart()">
|
||||
<span data-t="view_cart">View Cart</span> <i class="bi bi-cart-fill ms-1"></i>
|
||||
@ -200,11 +199,11 @@ foreach ($variants_raw as $v) {
|
||||
<div class="p-3 bg-light border-top">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span data-t="subtotal">Subtotal</span>
|
||||
<span id="modal-subtotal">0.000 OMR</span>
|
||||
<span id="modal-subtotal"><?= format_currency(0) ?></span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between fw-bold fs-5">
|
||||
<span data-t="total">Total</span>
|
||||
<span id="modal-total">0.000 OMR</span>
|
||||
<span id="modal-total"><?= format_currency(0) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
@ -244,6 +243,7 @@ foreach ($variants_raw as $v) {
|
||||
const TABLE_ID = <?= $table_id ?>;
|
||||
const OUTLET_ID = <?= $outlet_id ?>;
|
||||
const PRODUCT_VARIANTS = <?= json_encode($variants_by_product) ?>;
|
||||
const COMPANY_SETTINGS = <?= json_encode($settings) ?>;
|
||||
|
||||
let cart = [];
|
||||
let currentLang = localStorage.getItem('qorder_lang') || 'en';
|
||||
@ -262,7 +262,6 @@ foreach ($variants_raw as $v) {
|
||||
cust_name_placeholder: 'To identify your order',
|
||||
place_order: 'Place Order',
|
||||
select_option: 'Select Option',
|
||||
currency: 'OMR',
|
||||
added_to_cart: 'added to cart',
|
||||
order_placed: 'Order Placed!',
|
||||
order_success_msg: 'Your order has been sent to the kitchen. Thank you!',
|
||||
@ -279,7 +278,6 @@ foreach ($variants_raw as $v) {
|
||||
cust_name_placeholder: 'لتحديد طلبك',
|
||||
place_order: 'إتمام الطلب',
|
||||
select_option: 'اختر الخيار',
|
||||
currency: 'ر.ع.',
|
||||
added_to_cart: 'تم الإضافة إلى السلة',
|
||||
order_placed: 'تم إرسال الطلب!',
|
||||
order_success_msg: 'تم إرسال طلبك إلى المطبخ. شكراً لك!',
|
||||
@ -287,6 +285,19 @@ foreach ($variants_raw as $v) {
|
||||
}
|
||||
};
|
||||
|
||||
function formatCurrency(amount) {
|
||||
const symbol = COMPANY_SETTINGS.currency_symbol || '$';
|
||||
const decimals = parseInt(COMPANY_SETTINGS.currency_decimals || 2);
|
||||
const position = COMPANY_SETTINGS.currency_position || 'before';
|
||||
const formatted = parseFloat(Math.abs(amount)).toFixed(decimals);
|
||||
|
||||
if (position === 'after') {
|
||||
return formatted + ' ' + symbol;
|
||||
} else {
|
||||
return symbol + formatted;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTranslations() {
|
||||
document.querySelectorAll('[data-t]').forEach(el => {
|
||||
const key = el.getAttribute('data-t');
|
||||
@ -358,9 +369,9 @@ foreach ($variants_raw as $v) {
|
||||
btn.innerHTML = `
|
||||
<div>
|
||||
<div class="fw-bold">${vName}</div>
|
||||
<div class="small text-muted">${adj > 0 ? '+' : ''}${adj.toFixed(3)} ${translations[currentLang].currency}</div>
|
||||
<div class="small text-muted">${adj > 0 ? '+' : ''}${formatCurrency(adj)}</div>
|
||||
</div>
|
||||
<div class="fw-bold text-primary">${finalPrice.toFixed(3)} ${translations[currentLang].currency}</div>
|
||||
<div class="fw-bold text-primary">${formatCurrency(finalPrice)}</div>
|
||||
`;
|
||||
btn.onclick = () => {
|
||||
addToCart(product.id, `${pName} (${vName})`, finalPrice, v.id);
|
||||
@ -385,10 +396,9 @@ foreach ($variants_raw as $v) {
|
||||
function updateCartUI() {
|
||||
const total = cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
|
||||
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
|
||||
const currency = translations[currentLang].currency;
|
||||
|
||||
document.getElementById('cart-items-count').textContent = count;
|
||||
document.getElementById('cart-total-display').textContent = total.toFixed(3) + ' ' + currency;
|
||||
document.getElementById('cart-total-display').textContent = formatCurrency(total);
|
||||
|
||||
const footer = document.getElementById('cart-footer');
|
||||
if (count > 0) {
|
||||
@ -401,7 +411,6 @@ foreach ($variants_raw as $v) {
|
||||
function showCart() {
|
||||
const list = document.getElementById('cart-list');
|
||||
list.innerHTML = '';
|
||||
const currency = translations[currentLang].currency;
|
||||
|
||||
cart.forEach((item, index) => {
|
||||
const div = document.createElement('div');
|
||||
@ -409,10 +418,10 @@ foreach ($variants_raw as $v) {
|
||||
div.innerHTML = `
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div class="fw-bold text-truncate me-2">${item.name}</div>
|
||||
<div class="fw-bold">${(item.unit_price * item.quantity).toFixed(3)}</div>
|
||||
<div class="fw-bold">${formatCurrency(item.unit_price * item.quantity)}</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="text-muted small">${item.unit_price.toFixed(3)} / unit</div>
|
||||
<div class="text-muted small">${formatCurrency(item.unit_price)} / unit</div>
|
||||
<div class="quantity-controls">
|
||||
<button class="quantity-btn" onclick="updateQty(${index}, -1)"><i class="bi bi-dash"></i></button>
|
||||
<span class="fw-bold">${item.quantity}</span>
|
||||
@ -424,8 +433,8 @@ foreach ($variants_raw as $v) {
|
||||
});
|
||||
|
||||
const total = cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
|
||||
document.getElementById('modal-subtotal').textContent = total.toFixed(3) + ' ' + currency;
|
||||
document.getElementById('modal-total').textContent = total.toFixed(3) + ' ' + currency;
|
||||
document.getElementById('modal-subtotal').textContent = formatCurrency(total);
|
||||
document.getElementById('modal-total').textContent = formatCurrency(total);
|
||||
|
||||
cartModal.show();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user