Autosave: 20260302-152644

This commit is contained in:
Flatlogic Bot 2026-03-02 15:26:44 +00:00
parent 275baf63cd
commit 2a38c2ffdb
13 changed files with 1090 additions and 67 deletions

View File

@ -119,8 +119,8 @@ $recent_orders = $stmt->fetchAll();
<td><?= $order['id'] ?></td>
<td><?= $lang === 'ar' ? ($order['customer_name_ar'] ?: $order['customer_name_en']) : $order['customer_name_en'] ?></td>
<td><?= format_amount($order['total_price']) ?></td>
<td><span class="badge bg-<?= getStatusColor($order['status']) ?>"><?= __($order['status']) ?></span></td>
<td><span class="badge bg-<?= getPaymentStatusColor($order['payment_status']) ?>"><?= __($order['payment_status']) ?></span></td>
<td><span class="badge bg-<?= getStatusColor($order['status']) ?> rounded-pill px-3 py-1"><?= __($order['status']) ?></span></td>
<td><span class="badge badge-soft-<?= getPaymentStatusColor($order['payment_status']) ?> rounded-pill px-3 py-1"><?= __($order['payment_status']) ?></span></td>
<td>
<a href="order_details.php?id=<?= $order['id'] ?>" class="btn btn-sm btn-light border-0" style="border-radius: 8px;">
<i class="bi bi-eye"></i>
@ -166,4 +166,5 @@ function getPaymentStatusColor($status) {
'paid' => 'success',
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
require_once __DIR__ . '/includes/footer.php';
?>

42
api/translate.php Normal file
View File

@ -0,0 +1,42 @@
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../ai/LocalAIApi.php';
session_start();
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
exit;
}
$text = $_POST['text'] ?? '';
$direction = $_POST['direction'] ?? 'en-ar'; // en-ar or ar-en
if (empty($text)) {
echo json_encode(['success' => false, 'error' => 'Text is empty']);
exit;
}
$target_lang = $direction === 'en-ar' ? 'Arabic' : 'English';
$source_lang = $direction === 'en-ar' ? 'English' : 'Arabic';
try {
$prompt = "Translate the following text from $source_lang to $target_lang. Provide ONLY the translated text, no explanation or extra characters.\n\nText: $text";
$response = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are a professional translator specializing in business and laundry service terminology.'],
['role' => 'user', 'content' => $prompt],
],
]);
if (!empty($response['success'])) {
$translatedText = LocalAIApi::extractText($response);
echo json_encode(['success' => true, 'translation' => trim($translatedText)]);
} else {
echo json_encode(['success' => false, 'error' => $response['error'] ?? 'AI translation failed']);
}
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}

View File

@ -49,6 +49,32 @@ body {
padding: 0.5em 1em;
}
/* Soft Badges */
.badge-soft-success {
background-color: rgba(16, 185, 129, 0.15) !important;
color: #10b981 !important;
}
.badge-soft-danger {
background-color: rgba(239, 68, 68, 0.15) !important;
color: #ef4444 !important;
}
.badge-soft-warning {
background-color: rgba(245, 158, 11, 0.15) !important;
color: #f59e0b !important;
}
.badge-soft-primary {
background-color: rgba(59, 130, 246, 0.15) !important;
color: #3b82f6 !important;
}
.badge-soft-info {
background-color: rgba(6, 182, 212, 0.15) !important;
color: #06b6d4 !important;
}
.badge-soft-secondary {
background-color: rgba(107, 114, 128, 0.15) !important;
color: #6b7280 !important;
}
.table thead th {
background-color: #f9fafb;
text-transform: uppercase;
@ -81,4 +107,4 @@ body {
.item-card:hover {
transform: scale(1.02);
}
}

280
defined_keys.txt Normal file
View File

@ -0,0 +1,280 @@
en
dashboard
pos
orders
customers
items
services
branches
users
settings
logout
login
username
password
confirm_password
change_password
submit
save
save_changes
cancel
add_new
edit
delete
actions
search
total
status
date
phone
name
name_en
name_ar
price
quantity
subtotal
vat
vat_total
payment_status
received
processing
ready
delivered
cancelled
unpaid
partially_paid
paid
cash
card
transfer
new_order
order_details
customer_name
select_customer
add_item
edit_item
delete_item
select_item
select_service
checkout
back_to_pos
language
arabic
english
branch
all_branches
category
categories
add_category
edit_category
delete_category
categories_management
add_new_category
select_category
all
variant
variants
add_variant
select_variant
variant_name_en
variant_name_ar
default
vat_percent
image_url
items_management
variants_prices
no_items_found
pricing_management
are_you_sure
company_profile
user_profile
logo
favicon
email
address_en
address_ar
vat_number
profile_picture
full_name_en
full_name_ar
update_profile
success_update
error_update
print
print_invoice
thermal_receipt
order
customer
qty
close
payments
no_payments
update_status
update
remaining_amount
add_payment
payment_method
customer_details
order_date
method
amount
currency
ctr_no
vat_no
add_new_user
role
add_user
lab
outlet
view_items
initial_letters
from_date
to_date
order_no
all_status
all_payments
search_placeholder
previous
next
page
of
ar
dashboard
pos
orders
customers
items
services
branches
users
settings
logout
login
username
password
confirm_password
change_password
submit
save
save_changes
cancel
add_new
edit
delete
actions
search
total
status
date
phone
name
name_en
name_ar
price
quantity
subtotal
vat
vat_total
payment_status
received
processing
ready
delivered
cancelled
unpaid
partially_paid
paid
cash
card
transfer
new_order
order_details
customer_name
select_customer
add_item
edit_item
delete_item
select_item
select_service
checkout
back_to_pos
language
arabic
english
branch
all_branches
category
categories
add_category
edit_category
delete_category
categories_management
add_new_category
select_category
all
variant
variants
add_variant
select_variant
variant_name_en
variant_name_ar
default
vat_percent
image_url
items_management
variants_prices
no_items_found
pricing_management
are_you_sure
company_profile
user_profile
logo
favicon
email
address_en
address_ar
vat_number
profile_picture
full_name_en
full_name_ar
update_profile
success_update
error_update
print
print_invoice
thermal_receipt
order
customer
qty
close
payments
no_payments
update_status
update
remaining_amount
add_payment
payment_method
customer_details
order_date
method
amount
currency
ctr_no
vat_no
add_new_user
role
add_user
lab
outlet
view_items
initial_letters
from_date
to_date
order_no
all_status
all_payments
search_placeholder
previous
next
page
of

View File

@ -143,6 +143,12 @@ if ($current_user_id) {
</a>
</li>
<?php if ($current_role !== 'cashier'): ?>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'reports.php' ? 'active' : '' ?>" href="reports.php">
<i class="bi bi-file-earmark-bar-graph"></i>
<?= __('reports') ?>
</a>
</li>
<li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'items.php' ? 'active' : '' ?>" href="items.php">
<i class="bi bi-tags"></i>
@ -239,4 +245,4 @@ if ($current_user_id) {
</div>
</div>
</div>
</header>
</header>

View File

@ -147,7 +147,45 @@ $translations = [
'previous' => 'Previous',
'next' => 'Next',
'page' => 'Page',
'of' => 'of'
'of' => 'of',
'Access Denied' => 'Access Denied',
'active_orders' => 'Active Orders',
'add_branch' => 'Add Branch',
'add_new_branch' => 'Add New Branch',
'add_new_customer' => 'Add New Customer',
'add_new_service' => 'Add New Service',
'company' => 'Company',
'customers_list' => 'Customers List',
'edit_service' => 'Edit Service',
'exactly_3_letters' => 'Exactly 3 letters',
'image' => 'Image',
'item' => 'Item',
'manage_orders_across_outlets' => 'Manage orders across outlets',
'new_customers' => 'New Customers',
'no_orders_found' => 'No orders found',
'order_number' => 'Order Number',
'orders_list' => 'Orders List',
'pricing_services' => 'Pricing & Services',
'quick_actions' => 'Quick Actions',
'ready_orders' => 'Ready Orders',
'recent_orders' => 'Recent Orders',
'reports' => 'Reports',
'service' => 'Service',
'service_pricing' => 'Service Pricing',
'services_management' => 'Services Management',
'today_revenue' => 'Today Revenue',
'view' => 'View',
'generate_report' => 'Generate Report',
'total_revenue' => 'Total Revenue',
'total_orders' => 'Total Orders',
'average_order_value' => 'Average Order Value',
'revenue_by_outlet' => 'Revenue by Outlet',
'revenue_by_cashier' => 'Revenue by Cashier',
'orders_by_status' => 'Orders by Status',
'top_items' => 'Top Items',
'top_services' => 'Top Services',
'cashier' => 'Cashier',
'revenue_by_payment_method' => 'Revenue by Payment Method'
],
'ar' => [
'dashboard' => 'لوحة القيادة',
@ -288,7 +326,45 @@ $translations = [
'previous' => 'السابق',
'next' => 'التالي',
'page' => 'صفحة',
'of' => 'من'
'of' => 'من',
'Access Denied' => 'تم رفض الوصول',
'active_orders' => 'الطلبات النشطة',
'add_branch' => 'إضافة فرع',
'add_new_branch' => 'إضافة فرع جديد',
'add_new_customer' => 'إضافة عميل جديد',
'add_new_service' => 'إضافة خدمة جديدة',
'company' => 'الشركة',
'customers_list' => 'قائمة العملاء',
'edit_service' => 'تعديل خدمة',
'exactly_3_letters' => '3 أحرف بالضبط',
'image' => 'صورة',
'item' => 'صنف',
'manage_orders_across_outlets' => 'إدارة الطلبات عبر المنافذ',
'new_customers' => 'عملاء جدد',
'no_orders_found' => 'لا يوجد طلبات',
'order_number' => 'رقم الطلب',
'orders_list' => 'قائمة الطلبات',
'pricing_services' => 'الأسعار والخدمات',
'quick_actions' => 'إجراءات سريعة',
'ready_orders' => 'طلبات جاهزة',
'recent_orders' => 'آخر الطلبات',
'reports' => 'التقارير',
'service' => 'خدمة',
'service_pricing' => 'أسعار الخدمات',
'services_management' => 'إدارة الخدمات',
'today_revenue' => 'إيرادات اليوم',
'view' => 'عرض',
'generate_report' => 'إنشاء تقرير',
'total_revenue' => 'إجمالي الإيرادات',
'total_orders' => 'إجمالي الطلبات',
'average_order_value' => 'متوسط قيمة الطلب',
'revenue_by_outlet' => 'الإيرادات حسب المنفذ',
'revenue_by_cashier' => 'الإيرادات حسب الكاشير',
'orders_by_status' => 'الطلبات حسب الحالة',
'top_items' => 'أفضل الأصناف',
'top_services' => 'أفضل الخدمات',
'cashier' => 'الكاشير',
'revenue_by_payment_method' => 'الإيرادات حسب طريقة الدفع'
]
];
@ -317,4 +393,4 @@ function decimals() {
function format_amount($amount) {
return number_format((float)$amount, decimals()) . ' ' . currency();
}
}

View File

@ -322,11 +322,11 @@ foreach ($prices_raw as $p) {
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold small"><?= __('name_en') ?></label>
<input type="text" name="name_en" id="itemNameEn" class="form-control" required style="border-radius: 12px;">
<div class="input-group"><input type="text" name="name_en" id="itemNameEn" class="form-control" required style="border-top-left-radius: 12px; border-bottom-left-radius: 12px;"><button type="button" class="btn btn-outline-secondary" style="border-top-right-radius: 12px; border-bottom-right-radius: 12px;" onclick="translateField('itemNameEn', 'itemNameAr', 'en-ar')"><i class="bi bi-translate"></i></button></div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold small"><?= __('name_ar') ?></label>
<input type="text" name="name_ar" id="itemNameAr" class="form-control" style="border-radius: 12px;">
<div class="input-group"><input type="text" name="name_ar" id="itemNameAr" class="form-control" style="border-top-left-radius: 12px; border-bottom-left-radius: 12px;"><button type="button" class="btn btn-outline-secondary" style="border-top-right-radius: 12px; border-bottom-right-radius: 12px;" onclick="translateField('itemNameAr', 'itemNameEn', 'ar-en')"><i class="bi bi-translate"></i></button></div>
</div>
</div>
@ -375,11 +375,11 @@ foreach ($prices_raw as $p) {
<input type="hidden" name="id" id="catId">
<div class="mb-3">
<label class="form-label small"><?= __('name_en') ?></label>
<input type="text" name="cat_name_en" id="catNameEn" class="form-control" required style="border-radius: 12px;">
<div class="input-group"><input type="text" name="cat_name_en" id="catNameEn" class="form-control" required style="border-top-left-radius: 12px; border-bottom-left-radius: 12px;"><button type="button" class="btn btn-outline-secondary" style="border-top-right-radius: 12px; border-bottom-right-radius: 12px;" onclick="translateField('catNameEn', 'catNameAr', 'en-ar')"><i class="bi bi-translate"></i></button></div>
</div>
<div class="mb-3">
<label class="form-label small"><?= __('name_ar') ?></label>
<input type="text" name="cat_name_ar" id="catNameAr" class="form-control" style="border-radius: 12px;">
<div class="input-group"><input type="text" name="cat_name_ar" id="catNameAr" class="form-control" style="border-top-left-radius: 12px; border-bottom-left-radius: 12px;"><button type="button" class="btn btn-outline-secondary" style="border-top-right-radius: 12px; border-bottom-right-radius: 12px;" onclick="translateField('catNameAr', 'catNameEn', 'ar-en')"><i class="bi bi-translate"></i></button></div>
</div>
<button type="submit" class="btn btn-secondary w-100 py-2 fw-bold" style="border-radius: 12px;">
<?= __('save') ?>
@ -444,11 +444,11 @@ foreach ($prices_raw as $p) {
<input type="hidden" name="id" id="svcId">
<div class="mb-3">
<label class="form-label small"><?= __('name_en') ?></label>
<input type="text" name="svc_name_en" id="svcNameEn" class="form-control" required style="border-radius: 12px;">
<div class="input-group"><input type="text" name="svc_name_en" id="svcNameEn" class="form-control" required style="border-top-left-radius: 12px; border-bottom-left-radius: 12px;"><button type="button" class="btn btn-outline-secondary" style="border-top-right-radius: 12px; border-bottom-right-radius: 12px;" onclick="translateField('svcNameEn', 'svcNameAr', 'en-ar')"><i class="bi bi-translate"></i></button></div>
</div>
<div class="mb-3">
<label class="form-label small"><?= __('name_ar') ?></label>
<input type="text" name="svc_name_ar" id="svcNameAr" class="form-control" style="border-radius: 12px;">
<div class="input-group"><input type="text" name="svc_name_ar" id="svcNameAr" class="form-control" style="border-top-left-radius: 12px; border-bottom-left-radius: 12px;"><button type="button" class="btn btn-outline-secondary" style="border-top-right-radius: 12px; border-bottom-right-radius: 12px;" onclick="translateField('svcNameAr', 'svcNameEn', 'ar-en')"><i class="bi bi-translate"></i></button></div>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold" style="border-radius: 12px;">
<?= __('save') ?>
@ -564,6 +564,25 @@ foreach ($prices_raw as $p) {
</style>
<script>
async function translateField(sourceId, targetId, direction) {
const sourceEl = document.getElementById(sourceId);
const targetEl = document.getElementById(targetId);
const text = sourceEl.value.trim();
if (!text) return;
const btn = event.currentTarget;
const originalHtml = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = "<span class=\"spinner-border spinner-border-sm\"></span>";
try {
const formData = new FormData();
formData.append("text", text);
formData.append("direction", direction);
const resp = await fetch("api/translate.php", { method: "POST", body: formData });
const data = await resp.json();
if (data.success) targetEl.value = data.translation;
} catch (e) { console.error(e); }
finally { btn.disabled = false; btn.innerHTML = originalHtml; }
}
function openItemModal(item = null) {
const modal = new bootstrap.Modal(document.getElementById('itemModal'));
const form = document.getElementById('itemForm');

View File

@ -72,7 +72,10 @@ $payments = $stmt->fetchAll();
<div class="card p-4 mb-4" style="border-radius: 25px; border: none; shadow: 0 10px 30px rgba(0,0,0,0.05);">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5 class="fw-bold mb-0"><?= __('order') ?> #<?= $order['order_number'] ?></h5>
<span class="badge bg-<?= getStatusColor($order['status']) ?> fs-6 px-3 py-2" style="border-radius: 12px;"><?= __($order['status']) ?></span>
<div class="d-flex gap-2">
<span class="badge bg-<?= getStatusColor($order['status']) ?> fs-6 px-3 py-2" style="border-radius: 12px;"><?= __($order['status']) ?></span>
<span class="badge badge-soft-<?= getPaymentStatusColor($order['payment_status']) ?> fs-6 px-3 py-2" style="border-radius: 12px;"><?= __($order['payment_status']) ?></span>
</div>
</div>
<div class="table-responsive mb-4">
@ -243,5 +246,12 @@ function getStatusColor($status) {
'cancelled' => 'danger',
][$status] ?? 'info';
}
function getPaymentStatusColor($status) {
return [
'unpaid' => 'danger',
'partially_paid' => 'warning',
'paid' => 'success',
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
?>
?>

View File

@ -161,7 +161,7 @@ if ($current_role === 'super_admin') {
</td>
<td class="fw-bold text-primary"><?= format_amount($order['total_price']) ?></td>
<td><span class="badge bg-<?= getStatusColor($order['status']) ?> rounded-pill px-3 py-2"><?= __($order['status']) ?></span></td>
<td><span class="badge bg-<?= getPaymentStatusColor($order['payment_status']) ?> rounded-pill px-3 py-2"><?= __($order['payment_status']) ?></span></td>
<td><span class="badge badge-soft-<?= getPaymentStatusColor($order['payment_status']) ?> rounded-pill px-3 py-2"><?= __($order['payment_status']) ?></span></td>
<td class="small"><?= date('d/m/Y H:i', strtotime($order['created_at'])) ?></td>
<td class="text-end">
<div class="d-flex gap-1 justify-content-end">
@ -345,4 +345,4 @@ function getPaymentStatusColor($status) {
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
?>
?>

View File

@ -315,7 +315,13 @@ $pageTitle = $edit_order ? ($lang == 'en' ? 'Edit Order #' . $edit_order['order_
<span class="small fw-bold"><?= $lang == 'en' ? 'Card' : 'بطاقة' ?></span>
</button>
</div>
<div class="col-12">
<div class="col-6">
<button class="btn btn-outline-primary w-100 py-3 rounded-3 d-flex flex-column align-items-center" onclick="completeCheckout('transfer')">
<i class="bi bi-arrow-left-right mb-1 fs-4"></i>
<span class="small fw-bold"><?= $lang == 'en' ? 'Transfer' : 'تحويل' ?></span>
</button>
</div>
<div class="col-6">
<button class="btn btn-outline-secondary w-100 py-3 rounded-3 d-flex flex-column align-items-center" onclick="completeCheckout('pay_later')">
<i class="bi bi-clock-history mb-1 fs-4"></i>
<span class="small fw-bold"><?= $lang == 'en' ? 'Pay Later' : 'الدفع لاحقاً' ?></span>

View File

@ -42,9 +42,17 @@ $stmt->execute([$order_id]);
$total_paid = $stmt->fetch()['total_paid'] ?? 0;
$remaining = $order['total_price'] - $total_paid;
// Bilingual Helper for Labels
function b_label($key) {
global $translations;
$en = $translations['en'][$key] ?? $key;
$ar = $translations['ar'][$key] ?? $key;
return '<span class="label-en">' . $en . '</span> / <span class="label-ar">' . $ar . '</span>';
}
?>
<!DOCTYPE html>
<html lang="<?= $lang ?>" dir="<?= is_rtl() ? 'rtl' : 'ltr' ?>">
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -67,8 +75,8 @@ $remaining = $order['total_price'] - $total_paid;
margin: 0 auto;
}
.text-center { text-align: center; }
.text-end { text-align: <?= is_rtl() ? 'left' : 'right' ?>; }
.text-start { text-align: <?= is_rtl() ? 'right' : 'left' ?>; }
.text-end { text-align: right; }
.text-start { text-align: left; }
.fw-bold { font-weight: bold; }
.mb-1 { margin-bottom: 2mm; }
.mb-2 { margin-bottom: 4mm; }
@ -89,9 +97,24 @@ $remaining = $order['total_price'] - $total_paid;
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 1mm;
margin-bottom: 1.5mm;
align-items: flex-start;
}
.info-label {
font-size: 10px;
flex-shrink: 0;
margin-right: 2mm;
}
.info-value {
text-align: right;
word-break: break-all;
}
.label-en { font-size: 10px; }
.label-ar { font-size: 11px; font-family: 'Cairo', sans-serif; }
table {
width: 100%;
border-collapse: collapse;
@ -102,12 +125,26 @@ $remaining = $order['total_price'] - $total_paid;
text-align: inherit;
border-bottom: 1px solid #000;
padding: 1mm 0;
font-size: 11px;
font-size: 10px;
}
td {
padding: 1.5mm 0;
padding: 2mm 0;
vertical-align: top;
border-bottom: 0.1mm solid #eee;
}
.item-name-ar {
font-family: 'Cairo', sans-serif;
font-size: 11px;
display: block;
margin-top: 0.5mm;
}
.item-service {
font-size: 9px;
color: #555;
display: block;
}
.totals {
@ -119,6 +156,7 @@ $remaining = $order['total_price'] - $total_paid;
display: flex;
justify-content: space-between;
margin-bottom: 1mm;
align-items: center;
}
.grand-total {
@ -134,12 +172,17 @@ $remaining = $order['total_price'] - $total_paid;
padding-top: 3mm;
font-size: 10px;
}
.arabic-text {
font-family: 'Cairo', sans-serif;
direction: rtl;
}
@media print {
body {
width: 80mm;
margin: 0;
padding: 5mm;
padding: 3mm;
}
.no-print {
display: none;
@ -149,66 +192,92 @@ $remaining = $order['total_price'] - $total_paid;
</head>
<body>
<div class="no-print text-center mb-2">
<button onclick="window.print()" style="padding: 10px 20px; cursor: pointer; border-radius: 8px; border: 1px solid #000; background: #eee;"><?= __('print') ?? 'Print' ?></button>
<button onclick="window.close()" style="padding: 10px 20px; cursor: pointer; border-radius: 8px; border: 1px solid #000; background: #fff;"><?= __('close') ?? 'Close' ?></button>
<button onclick="window.print()" style="padding: 10px 20px; cursor: pointer; border-radius: 8px; border: 1px solid #000; background: #eee;">Print / طباعة</button>
<button onclick="window.close()" style="padding: 10px 20px; cursor: pointer; border-radius: 8px; border: 1px solid #000; background: #fff;">Close / إغلاق</button>
</div>
<div class="text-center receipt-header">
<?php if ($company['logo']): ?>
<img src="<?= $company['logo'] ?>" alt="Logo" class="logo">
<?php else: ?>
<h2 class="fw-bold"><?= is_arabic() ? $company['name_ar'] : $company['name_en'] ?></h2>
<?php endif; ?>
<div class="fw-bold fs-6"><?= is_arabic() ? $order['branch_name_ar'] : $order['branch_name_en'] ?></div>
<div><?= is_arabic() ? $order['branch_address_ar'] : $order['branch_address_en'] ?></div>
<div><?= __('phone') ?>: <?= $order['branch_phone'] ?></div>
<h3 class="fw-bold"><?= $company['name_en'] ?></h3>
<h3 class="fw-bold arabic-text"><?= $company['name_ar'] ?></h3>
<div class="mt-1">
<div class="fw-bold"><?= $order['branch_name_en'] ?></div>
<div class="fw-bold arabic-text"><?= $order['branch_name_ar'] ?></div>
</div>
<div class="small mt-1">
<div><?= $order['branch_address_en'] ?></div>
<div class="arabic-text"><?= $order['branch_address_ar'] ?></div>
</div>
<div class="mt-1">
<span><?= $translations['en']['phone'] ?> / <?= $translations['ar']['phone'] ?>:</span>
<span><?= $order['branch_phone'] ?></span>
</div>
<?php if (!empty($company['ctr_no'])): ?>
<div><?= __('ctr_no') ?>: <?= $company['ctr_no'] ?></div>
<div>
<span><?= $translations['en']['ctr_no'] ?> / <?= $translations['ar']['ctr_no'] ?>:</span>
<span><?= $company['ctr_no'] ?></span>
</div>
<?php endif; ?>
<?php if (!empty($company['vat_no'])): ?>
<div><?= __('vat_no') ?>: <?= $company['vat_no'] ?></div>
<?php elseif (!empty($company['vat_number'])): ?>
<div><?= __('vat_no') ?>: <?= $company['vat_number'] ?></div>
<?php if (!empty($company['vat_no']) || !empty($company['vat_number'])): ?>
<div>
<span><?= $translations['en']['vat_no'] ?> / <?= $translations['ar']['vat_no'] ?>:</span>
<span><?= $company['vat_no'] ?: $company['vat_number'] ?></span>
</div>
<?php endif; ?>
</div>
<div class="mb-2">
<div class="info-row">
<span class="fw-bold"><?= __('order') ?> #</span>
<span><?= $order['order_number'] ?></span>
<div class="info-label"><?= b_label('order') ?> #</div>
<div class="info-value fw-bold"><?= $order['order_number'] ?></div>
</div>
<div class="info-row">
<span class="fw-bold"><?= __('date') ?>:</span>
<span><?= date('d/m/Y H:i', strtotime($order['created_at'])) ?></span>
<div class="info-label"><?= b_label('date') ?></div>
<div class="info-value"><?= date('d/m/Y H:i', strtotime($order['created_at'])) ?></div>
</div>
<div class="info-row">
<span class="fw-bold"><?= __('customer') ?>:</span>
<span><?= is_arabic() ? ($order['customer_name_ar'] ?: $order['customer_name_en']) : $order['customer_name_en'] ?></span>
<div class="info-label"><?= b_label('customer') ?></div>
<div class="info-value">
<div><?= $order['customer_name_en'] ?: 'Walk-in' ?></div>
<?php if ($order['customer_name_ar']): ?>
<div class="arabic-text"><?= $order['customer_name_ar'] ?></div>
<?php endif; ?>
</div>
</div>
<?php if ($order['customer_phone']): ?>
<div class="info-row">
<span class="fw-bold"><?= __('phone') ?>:</span>
<span><?= $order['customer_phone'] ?></span>
<div class="info-label"><?= b_label('phone') ?></div>
<div class="info-value"><?= $order['customer_phone'] ?></div>
</div>
<?php endif; ?>
</div>
<table>
<thead>
<tr>
<th><?= __('item') ?></th>
<th class="text-center"><?= __('qty') ?? 'Qty' ?></th>
<th class="text-end"><?= __('total') ?></th>
<th style="width: 55%;"><?= b_label('item') ?></th>
<th class="text-center" style="width: 15%;"><?= b_label('qty') ?></th>
<th class="text-end" style="width: 30%;"><?= b_label('total') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($order_items as $item): ?>
<tr>
<td>
<div class="fw-bold"><?= is_arabic() ? ($item['item_ar'] ?: $item['item_en']) : $item['item_en'] ?></div>
<div style="font-size: 10px; color: #444;"><?= is_arabic() ? ($item['service_ar'] ?: $item['service_en']) : $item['service_en'] ?></div>
<div class="fw-bold" style="font-size: 11px;"><?= $item['item_en'] ?></div>
<div class="item-name-ar"><?= $item['item_ar'] ?></div>
<div class="item-service"><?= $item['service_en'] ?> / <span class="arabic-text"><?= $item['service_ar'] ?></span></div>
</td>
<td class="text-center"><?= $item['quantity'] ?></td>
<td class="text-end"><?= format_amount($item['subtotal'] + ($item['vat_amount'] * $item['quantity'])) ?></td>
<td class="text-end fw-bold"><?= number_format($item['subtotal'] + ($item['vat_amount'] * $item['quantity']), decimals()) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
@ -216,37 +285,43 @@ $remaining = $order['total_price'] - $total_paid;
<div class="totals">
<div class="total-row">
<span><?= __('subtotal') ?></span>
<span><?= format_amount($order['total_price'] - $order['vat_total']) ?></span>
<div class="info-label"><?= b_label('subtotal') ?></div>
<div class="info-value"><?= format_amount($order['total_price'] - $order['vat_total']) ?></div>
</div>
<div class="total-row">
<span><?= __('vat') ?></span>
<span><?= format_amount($order['vat_total']) ?></span>
<div class="info-label"><?= b_label('vat') ?></div>
<div class="info-value"><?= format_amount($order['vat_total']) ?></div>
</div>
<div class="total-row fw-bold" style="font-size: 14px; border-top: 1px solid #000; margin-top: 1mm; padding-top: 1mm;">
<span><?= __('total') ?></span>
<span><?= format_amount($order['total_price']) ?></span>
<div class="total-row fw-bold grand-total">
<div class="info-label"><?= b_label('total') ?></div>
<div class="info-value" style="font-size: 16px;"><?= format_amount($order['total_price']) ?></div>
</div>
<div class="total-row mt-2">
<span><?= __('paid') ?></span>
<span><?= format_amount($total_paid) ?></span>
<div class="info-label"><?= b_label('paid') ?></div>
<div class="info-value"><?= format_amount($total_paid) ?></div>
</div>
<?php if ($remaining > 0): ?>
<div class="total-row">
<span><?= __('remaining_amount') ?></span>
<span><?= format_amount(max(0, $remaining)) ?></span>
<div class="info-label"><?= b_label('remaining_amount') ?></div>
<div class="info-value fw-bold text-danger"><?= format_amount($remaining) ?></div>
</div>
<?php endif; ?>
</div>
<div class="text-center footer">
<div class="mb-1 fw-bold"><?= is_arabic() ? 'شكراً لزيارتكم!' : 'Thank you for your visit!' ?></div>
<div><?= is_arabic() ? 'يرجى الاحتفاظ بالإيصال' : 'Please keep your receipt' ?></div>
<div class="fw-bold mb-1">Thank you for your visit!</div>
<div class="fw-bold arabic-text mb-1">شكراً لزيارتكم!</div>
<div class="mt-1">Please keep your receipt</div>
<div class="arabic-text">يرجى الاحتفاظ بالإيصال</div>
</div>
<script>
window.onload = function() {
// Uncomment for automatic print dialog
// window.print();
// Check if we are in the print iframe
if (window.location.search.includes('id=')) {
// Auto print is handled by the parent in pos.php
}
}
</script>
</body>

347
reports.php Normal file
View File

@ -0,0 +1,347 @@
<?php
$title = 'reports';
require_once __DIR__ . '/includes/header.php';
if ($current_role === 'cashier') {
echo '<div class="alert alert-danger">' . __('Access Denied') . '</div>';
require_once __DIR__ . '/includes/footer.php';
exit;
}
$from_date = $_GET['from_date'] ?? date('Y-m-01');
$to_date = $_GET['to_date'] ?? date('Y-m-t');
$branch_filter = $_GET['branch_id'] ?? 'all';
$user_filter = $_GET['user_id'] ?? 'all';
// Base queries
$where = "WHERE DATE(o.created_at) BETWEEN ? AND ?";
$params = [$from_date, $to_date];
if ($branch_filter !== 'all') {
$where .= " AND o.branch_id = ?";
$params[] = $branch_filter;
}
if ($user_filter !== 'all') {
$where .= " AND o.user_id = ?";
$params[] = $user_filter;
}
// Summary Stats
$stmt = db()->prepare("SELECT
SUM(total_price) as total_revenue,
COUNT(*) as total_orders,
AVG(total_price) as avg_order_value
FROM orders o $where");
$stmt->execute($params);
$summary = $stmt->fetch();
// Revenue by Branch
$stmt = db()->prepare("SELECT
b.name_en, b.name_ar, SUM(o.total_price) as revenue
FROM orders o
JOIN branches b ON o.branch_id = b.id
$where
GROUP BY o.branch_id");
$stmt->execute($params);
$revenue_by_branch = $stmt->fetchAll();
// Revenue by Cashier
$stmt = db()->prepare("SELECT
u.full_name_en, u.full_name_ar, SUM(o.total_price) as revenue
FROM orders o
JOIN users u ON o.user_id = u.id
$where
GROUP BY o.user_id");
$stmt->execute($params);
$revenue_by_user = $stmt->fetchAll();
// Orders by Status
$stmt = db()->prepare("SELECT
status, COUNT(*) as count
FROM orders o
$where
GROUP BY status");
$stmt->execute($params);
$orders_by_status = $stmt->fetchAll();
// Revenue by Payment Method
$stmt = db()->prepare("SELECT
p.payment_method, SUM(p.amount) as revenue
FROM payments p
JOIN orders o ON p.order_id = o.id
$where
GROUP BY p.payment_method");
$stmt->execute($params);
$revenue_by_method = $stmt->fetchAll();
// Top Items
$stmt = db()->prepare("SELECT
i.name_en, i.name_ar, SUM(oi.quantity) as total_qty, SUM(oi.subtotal) as total_revenue
FROM order_items oi
JOIN orders o ON oi.order_id = o.id
JOIN items i ON oi.item_id = i.id
$where
GROUP BY oi.item_id
ORDER BY total_qty DESC LIMIT 10");
$stmt->execute($params);
$top_items = $stmt->fetchAll();
// Top Services
$stmt = db()->prepare("SELECT
s.name_en, s.name_ar, SUM(oi.quantity) as total_qty, SUM(oi.subtotal) as total_revenue
FROM order_items oi
JOIN orders o ON oi.order_id = o.id
JOIN services s ON oi.service_id = s.id
$where
GROUP BY oi.service_id
ORDER BY total_qty DESC LIMIT 10");
$stmt->execute($params);
$top_services = $stmt->fetchAll();
// Fetch branches and users for filters
$branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll();
$users = db()->query("SELECT id, full_name_en, full_name_ar FROM users")->fetchAll();
?>
<div class="card p-4 mb-4">
<form method="GET" class="row g-3 align-items-end">
<div class="col-md-2">
<label class="form-label small fw-bold"><?= __('from_date') ?></label>
<input type="date" name="from_date" class="form-control" value="<?= $from_date ?>">
</div>
<div class="col-md-2">
<label class="form-label small fw-bold"><?= __('to_date') ?></label>
<input type="date" name="to_date" class="form-control" value="<?= $to_date ?>">
</div>
<?php if ($current_role === 'super_admin'): ?>
<div class="col-md-3">
<label class="form-label small fw-bold"><?= __('outlet') ?></label>
<select name="branch_id" class="form-select">
<option value="all"><?= __('all_branches') ?></option>
<?php foreach($branches as $b): ?>
<option value="<?= $b['id'] ?>" <?= $branch_filter == $b['id'] ? 'selected' : '' ?>>
<?= is_arabic() ? $b['name_ar'] : $b['name_en'] ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<div class="col-md-3">
<label class="form-label small fw-bold"><?= __('cashier') ?></label>
<select name="user_id" class="form-select">
<option value="all"><?= __('all') ?></option>
<?php foreach($users as $u): ?>
<option value="<?= $u['id'] ?>" <?= $user_filter == $u['id'] ? 'selected' : '' ?>>
<?= is_arabic() ? $u['full_name_ar'] : $u['full_name_en'] ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-filter me-2"></i> <?= __('generate_report') ?>
</button>
</div>
</form>
</div>
<div class="row g-4 mb-4">
<div class="col-md-4">
<div class="card p-4 text-center border-start border-primary border-5">
<div class="text-muted small mb-1"><?= __('total_revenue') ?></div>
<div class="fs-3 fw-bold text-primary"><?= format_amount($summary['total_revenue'] ?? 0) ?></div>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 text-center border-start border-success border-5">
<div class="text-muted small mb-1"><?= __('total_orders') ?></div>
<div class="fs-3 fw-bold text-success"><?= $summary['total_orders'] ?? 0 ?></div>
</div>
</div>
<div class="col-md-4">
<div class="card p-4 text-center border-start border-info border-5">
<div class="text-muted small mb-1"><?= __('average_order_value') ?></div>
<div class="fs-3 fw-bold text-info"><?= format_amount($summary['avg_order_value'] ?? 0) ?></div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('revenue_by_outlet') ?></h6>
<canvas id="revenueByBranchChart"></canvas>
</div>
</div>
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('orders_by_status') ?></h6>
<canvas id="ordersByStatusChart"></canvas>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('revenue_by_cashier') ?></h6>
<canvas id="revenueByCashierChart"></canvas>
</div>
</div>
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('revenue_by_payment_method') ?? 'Revenue by Payment Method' ?></h6>
<canvas id="revenueByMethodChart"></canvas>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('top_items') ?></h6>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th><?= __('item') ?></th>
<th class="text-center"><?= __('qty') ?></th>
<th class="text-end"><?= __('revenue') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($top_items as $item): ?>
<tr>
<td><?= is_arabic() ? $item['name_ar'] : $item['name_en'] ?></td>
<td class="text-center"><?= $item['total_qty'] ?></td>
<td class="text-end"><?= format_amount($item['total_revenue']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card p-4 h-100">
<h6 class="fw-bold mb-4"><?= __('top_services') ?></h6>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th><?= __('service') ?></th>
<th class="text-center"><?= __('qty') ?></th>
<th class="text-end"><?= __('revenue') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($top_services as $service): ?>
<tr>
<td><?= is_arabic() ? $service['name_ar'] : $service['name_en'] ?></td>
<td class="text-center"><?= $service['total_qty'] ?></td>
<td class="text-end"><?= format_amount($service['total_revenue']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Revenue by Branch Chart
new Chart(document.getElementById('revenueByBranchChart'), {
type: 'bar',
data: {
labels: <?= json_encode(array_map(function($b) use ($lang) {
return $lang === 'ar' ? $b['name_ar'] : $b['name_en'];
}, $revenue_by_branch)) ?>,
datasets: [{
label: '<?= __('revenue') ?>',
data: <?= json_encode(array_column($revenue_by_branch, 'revenue')) ?>,
backgroundColor: 'rgba(54, 162, 235, 0.5)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true }
}
}
});
// Orders by Status Chart
new Chart(document.getElementById('ordersByStatusChart'), {
type: 'doughnut',
data: {
labels: <?= json_encode(array_map(function($s) {
return __($s['status']);
}, $orders_by_status)) ?>,
datasets: [{
data: <?= json_encode(array_column($orders_by_status, 'count')) ?>,
backgroundColor: [
'#6c757d', '#0d6efd', '#198754', '#212529', '#dc3545'
]
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom' }
}
}
});
// Revenue by Cashier Chart
new Chart(document.getElementById('revenueByCashierChart'), {
type: 'bar',
data: {
labels: <?= json_encode(array_map(function($u) use ($lang) {
return $lang === 'ar' ? $u['full_name_ar'] : $u['full_name_en'];
}, $revenue_by_user)) ?>,
datasets: [{
label: '<?= __('revenue') ?>',
data: <?= json_encode(array_column($revenue_by_user, 'revenue')) ?>,
backgroundColor: 'rgba(75, 192, 192, 0.5)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true }
}
}
});
// Revenue by Method Chart
new Chart(document.getElementById('revenueByMethodChart'), {
type: 'pie',
data: {
labels: <?= json_encode(array_map(function($m) {
return __($m['payment_method']);
}, $revenue_by_method)) ?>,
datasets: [{
data: <?= json_encode(array_column($revenue_by_method, 'revenue')) ?>,
backgroundColor: [
'#ffc107', '#0dcaf0', '#6610f2'
]
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom' }
}
}
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

135
used_keys.txt Normal file
View File

@ -0,0 +1,135 @@
Access Denied
actions
active_orders
add_branch
add_item
add_new
add_new_branch
add_new_category
add_new_customer
add_new_service
add_new_user
add_payment
add_user
address_ar
address_en
all_branches
all_payments
all_status
amount
are_you_sure
branch
branches
cancel
cancelled
card
cash
categories
categories_management
category
change_password
close
company
company_profile
confirm_password
ctr_no
currency
customer
customer_details
customer_name
customers
customers_list
dashboard
date
delete
delivered
edit
edit_category
edit_item
edit_service
email
error_update
exactly_3_letters
favicon
from_date
full_name_ar
full_name_en
image
initial_letters
item
items
items_management
lab
language
login
logo
logout
manage_orders_across_outlets
method
name
name_ar
name_en
new_customers
new_order
next
no_orders_found
no_payments
order
order_date
order_number
orders
orders_list
outlet
paid
partially_paid
password
payment_method
payment_status
payments
phone
pos
previous
price
pricing_services
print_invoice
processing
profile_picture
quantity
quick_actions
ready
ready_orders
received
recent_orders
remaining_amount
reports
role
save
save_changes
search
search_placeholder
select_category
service
service_pricing
services
services_management
status
subtotal
success_update
thermal_receipt
to_date
today_revenue
total
transfer
unpaid
update
update_profile
update_status
user_profile
username
users
vat
vat_no
vat_percent
vat_total
view
view_items