update migrations

This commit is contained in:
Flatlogic Bot 2026-05-02 17:38:03 +00:00
parent 7781b48bf8
commit 66989c3877
4 changed files with 403 additions and 278 deletions

View File

@ -123,3 +123,14 @@
2026-03-20 21:56:39 - Items case hit
2026-03-20 21:56:47 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-03-20 21:56:51 - Items case hit
2026-05-02 17:20:57 - Items case hit
2026-05-02 17:23:36 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:29:22 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:29:31 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:29:39 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:29:45 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:32:00 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:32:02 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:32:10 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:32:11 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}
2026-05-02 17:32:54 - Requesting AI. UUID: [e1f9b5b3-fcef-4c8d-87d2-8630b1f72491] CFG: {"base_url":"https:\/\/flatlogic.com","responses_path":"\/projects\/38471\/ai-request","project_id":"38471","project_uuid":"e1f9b5b3-fcef-4c8d-87d2-8630b1f72491","project_header":"Project-UUID","default_model":"gpt-4o-mini","timeout":30,"verify_tls":true}

View File

@ -0,0 +1,181 @@
<?php
require_once __DIR__ . '/../ai/LocalAIApi.php';
if (!function_exists('app_translate_text')) {
function app_translate_text(string $text, string $target): array
{
$cleanText = trim($text);
$cleanTarget = strtolower(trim($target)) === 'en' ? 'en' : 'ar';
if ($cleanText === '') {
return [
'success' => false,
'error' => 'No text provided',
];
}
if (function_exists('mb_substr')) {
$cleanText = mb_substr($cleanText, 0, 500);
} else {
$cleanText = substr($cleanText, 0, 500);
}
$skipAiUntil = isset($_SESSION['translation_ai_disabled_until']) ? (int) $_SESSION['translation_ai_disabled_until'] : 0;
$shouldTryAi = $skipAiUntil <= time();
$aiResult = [
'success' => false,
'error' => 'AI translation unavailable',
'provider' => 'ai',
];
if ($shouldTryAi) {
$aiResult = app_translate_text_with_ai($cleanText, $cleanTarget);
if (!empty($aiResult['success']) && !empty($aiResult['translated'])) {
unset($_SESSION['translation_ai_disabled_until']);
return $aiResult;
}
$aiError = strtolower((string) ($aiResult['error'] ?? ''));
if ($aiError !== '' && (strpos($aiError, '401') !== false || strpos($aiError, 'unauthorized') !== false || strpos($aiError, 'timeout') !== false)) {
$_SESSION['translation_ai_disabled_until'] = time() + 900;
}
}
$fallbackResult = app_translate_text_with_google($cleanText, $cleanTarget);
if (!empty($fallbackResult['success']) && !empty($fallbackResult['translated'])) {
if (!empty($aiResult['error']) && $aiResult['error'] !== 'AI translation unavailable') {
$fallbackResult['warning'] = (string) $aiResult['error'];
}
return $fallbackResult;
}
if (!empty($aiResult['error']) && $aiResult['error'] !== 'AI translation unavailable') {
return $aiResult;
}
return $fallbackResult;
}
}
if (!function_exists('app_translate_text_with_ai')) {
function app_translate_text_with_ai(string $text, string $target): array
{
$prompt = "Translate the following product name to " . ($target === 'ar' ? 'Arabic' : 'English') . ". Return ONLY the translation, no extra text or explanations.\n\nText: " . $text;
$resp = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are a translation assistant. You translate product names between English and Arabic. Keep brand names, sizes, numbers, and SKUs accurate. Return only the translated text.'],
['role' => 'user', 'content' => $prompt],
],
], [
'poll_interval' => 1,
'poll_timeout' => 12,
'timeout' => 20,
]);
if (!empty($resp['success'])) {
$translated = trim(LocalAIApi::extractText($resp));
if ($translated !== '') {
return [
'success' => true,
'translated' => $translated,
'provider' => 'ai',
];
}
}
return [
'success' => false,
'error' => (string) ($resp['error'] ?? 'AI translation failed'),
'provider' => 'ai',
];
}
}
if (!function_exists('app_translate_text_with_google')) {
function app_translate_text_with_google(string $text, string $target): array
{
if (!function_exists('curl_init')) {
return [
'success' => false,
'error' => 'Translation service unavailable: PHP cURL is missing',
'provider' => 'fallback',
];
}
$query = http_build_query([
'client' => 'gtx',
'sl' => 'auto',
'tl' => $target,
'dt' => 't',
'q' => $text,
]);
$url = 'https://translate.googleapis.com/translate_a/single?' . $query;
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_HTTPGET => true,
CURLOPT_FAILONERROR => false,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'User-Agent: Mozilla/5.0',
],
]);
$body = curl_exec($ch);
if ($body === false) {
$error = curl_error($ch) ?: 'Unknown translation request error';
curl_close($ch);
return [
'success' => false,
'error' => $error,
'provider' => 'fallback',
];
}
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status < 200 || $status >= 300 || $body === '' || $body === null) {
return [
'success' => false,
'error' => 'Fallback translation request failed',
'provider' => 'fallback',
];
}
$decoded = json_decode($body, true);
if (!is_array($decoded) || empty($decoded[0]) || !is_array($decoded[0])) {
return [
'success' => false,
'error' => 'Fallback translation returned an unexpected response',
'provider' => 'fallback',
];
}
$parts = [];
foreach ($decoded[0] as $segment) {
if (is_array($segment) && isset($segment[0]) && is_string($segment[0])) {
$parts[] = $segment[0];
}
}
$translated = trim(implode('', $parts));
if ($translated === '') {
return [
'success' => false,
'error' => 'Fallback translation returned empty text',
'provider' => 'fallback',
];
}
return [
'success' => true,
'translated' => $translated,
'provider' => 'fallback',
];
}
}

479
index.php
View File

@ -1112,30 +1112,27 @@ if (isset($_GET['action']) || isset($_POST['action'])) {
if ($action === 'translate') {
header('Content-Type: application/json');
require_once __DIR__ . '/ai/LocalAIApi.php';
$text = $_POST['text'] ?? '';
$target = $_POST['target'] ?? 'ar';
if (empty($text)) {
echo json_encode(['success' => false, 'error' => 'No text provided']);
require_once __DIR__ . '/includes/translation_helper.php';
$text = trim((string) ($_POST['text'] ?? ''));
$target = strtolower(trim((string) ($_POST['target'] ?? ''))) === 'en' ? 'en' : 'ar';
if ($text === '') {
echo json_encode(['success' => false, 'error' => 'No text provided'], JSON_UNESCAPED_UNICODE);
exit;
}
$prompt = "Translate the following product name to " . ($target === 'ar' ? 'Arabic' : 'English') . ". Return ONLY the translation, no extra text or explanations.\n\nText: " . $text;
$resp = LocalAIApi::createResponse([
'input' => [
['role' => 'system', 'content' => 'You are a translation assistant. You translate product names between English and Arabic.'],
['role' => 'user', 'content' => $prompt],
],
]);
if (!empty($resp['success'])) {
$translated = trim(LocalAIApi::extractText($resp));
echo json_encode(['success' => true, 'translated' => $translated]);
$translationResult = app_translate_text($text, $target);
if (!empty($translationResult['success']) && !empty($translationResult['translated'])) {
echo json_encode([
'success' => true,
'translated' => $translationResult['translated'],
'provider' => $translationResult['provider'] ?? 'unknown',
], JSON_UNESCAPED_UNICODE);
} else {
error_log("Translation failed for text '$text': " . json_encode($resp));
echo json_encode(['success' => false, 'error' => $resp['error'] ?? 'Translation failed']);
$errorMessage = (string) ($translationResult['error'] ?? 'Translation failed');
error_log("Translation failed for text '$text': " . json_encode($translationResult, JSON_UNESCAPED_UNICODE));
echo json_encode(['success' => false, 'error' => $errorMessage], JSON_UNESCAPED_UNICODE);
}
exit;
}
@ -1407,11 +1404,18 @@ function getPromotionalPrice($item) {
db()->query("UPDATE stock_items SET is_promotion = 0 WHERE is_promotion = 1 AND promotion_end IS NOT NULL AND promotion_end < '" . date('Y-m-d') . "'");
if (isset($_POST['add_category'])) {
db()->prepare("INSERT INTO stock_categories (name_en, name_ar, outlet_id) VALUES (?, ?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', current_outlet_id()]);
$current_oid = current_outlet_id();
db()->prepare("INSERT INTO stock_categories (name_en, name_ar, outlet_id) VALUES (?, ?, ?)")
->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $current_oid]);
redirectWithMessage("Category added!");
}
if (isset($_POST['add_unit'])) {
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar, outlet_id) VALUES (?, ?, ?, ?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $_POST['short_en'] ?? '', $_POST['short_ar'] ?? '', current_outlet_id()]);
$current_oid = current_outlet_id();
$unitNameEn = trim((string)($_POST['name_en'] ?? ''));
$unitNameAr = trim((string)($_POST['name_ar'] ?? ''));
if ($unitNameAr === '') $unitNameAr = $unitNameEn;
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar, outlet_id) VALUES (?, ?, ?, ?, ?)")
->execute([$unitNameEn, $unitNameAr, $unitNameEn, $unitNameAr, $current_oid]);
redirectWithMessage("Unit added!");
}
@ -1424,7 +1428,10 @@ function getPromotionalPrice($item) {
redirectWithMessage("Category deleted!");
}
if (isset($_POST['edit_unit'])) {
db()->prepare("UPDATE stock_units SET name_en = ?, name_ar = ?, short_name_en = ?, short_name_ar = ? WHERE id = ?")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $_POST['short_en'] ?? '', $_POST['short_ar'] ?? '', (int)$_POST['id']]);
$unitNameEn = trim((string)($_POST['name_en'] ?? ''));
$unitNameAr = trim((string)($_POST['name_ar'] ?? ''));
if ($unitNameAr === '') $unitNameAr = $unitNameEn;
db()->prepare("UPDATE stock_units SET name_en = ?, name_ar = ?, short_name_en = ?, short_name_ar = ? WHERE id = ?")->execute([$unitNameEn, $unitNameAr, $unitNameEn, $unitNameAr, (int)$_POST['id']]);
redirectWithMessage("Unit updated!");
}
if (isset($_POST['delete_unit'])) {
@ -2089,13 +2096,14 @@ function getPromotionalPrice($item) {
}
if (isset($rows[0][0]) && stripos($rows[0][0], 'name') !== false) array_shift($rows);
$current_oid = current_outlet_id();
foreach ($rows as $row) {
if (empty($row[0])) continue;
$name_en = trim((string)$row[0]);
$name_ar = trim((string)($row[1] ?? $name_en));
db()->prepare("INSERT INTO stock_categories (name_en, name_ar, outlet_id) VALUES (?, ?, ?)")
->execute([$name_en, $name_ar, current_outlet_id()]);
->execute([$name_en, $name_ar, $current_oid]);
$count++;
}
redirectWithMessage("Import categories completed! $count processed.", "index.php?page=categories");
@ -2121,12 +2129,11 @@ function getPromotionalPrice($item) {
foreach ($rows as $row) {
if (empty($row[0])) continue;
$name_en = trim((string)$row[0]);
$name_ar = trim((string)($row[1] ?? $name_en));
$short_en = trim((string)($row[2] ?? ''));
$short_ar = trim((string)($row[3] ?? ''));
$name_ar = trim((string)($row[1] ?? ''));
if ($name_ar === '') $name_ar = $name_en;
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar, outlet_id) VALUES (?, ?, ?, ?, ?)")
->execute([$name_en, $name_ar, $short_en, $short_ar, current_outlet_id()]);
->execute([$name_en, $name_ar, $name_en, $name_ar, current_outlet_id()]);
$count++;
}
redirectWithMessage("Import units completed! $count processed.", "index.php?page=units");
@ -3678,12 +3685,14 @@ if ($page === 'export') {
$headers = ['LPO #', 'Supplier', 'Date', 'Total', 'Status'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'categories') {
$stmt = db()->query("SELECT id, name_en, name_ar FROM stock_categories WHERE outlet_id = ".current_outlet_id()." ORDER BY id DESC");
$stmt = db()->prepare("SELECT id, name_en, name_ar FROM stock_categories WHERE outlet_id = ? ORDER BY id DESC");
$stmt->execute([current_outlet_id()]);
$headers = ['ID', 'Name (EN)', 'Name (AR)'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'units') {
$stmt = db()->query("SELECT id, name_en, name_ar, short_name_en, short_name_ar FROM stock_units WHERE outlet_id = ".current_outlet_id()." ORDER BY id DESC");
$headers = ['ID', 'Name (EN)', 'Name (AR)', 'Short (EN)', 'Short (AR)'];
$stmt = db()->prepare("SELECT id, name_en, name_ar FROM stock_units WHERE outlet_id = ? ORDER BY id DESC");
$stmt->execute([current_outlet_id()]);
$headers = ['ID', 'Name (EN)', 'Name (AR)'];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) $rows[] = $row;
} elseif ($type === 'sales_returns') {
$salesReturnReferenceColumn = sales_return_reference_column();
@ -3736,9 +3745,16 @@ if ($page === 'export') {
}
// Global data for modals
$data['categories'] = db()->query("SELECT * FROM stock_categories WHERE outlet_id = ".current_outlet_id()." ORDER BY name_en ASC")->fetchAll();
$data['units'] = db()->query("SELECT * FROM stock_units WHERE outlet_id = ".current_outlet_id()." ORDER BY name_en ASC")->fetchAll();
$data['suppliers'] = db()->query("SELECT * FROM suppliers WHERE outlet_id = ".current_outlet_id()." ORDER BY name ASC")->fetchAll();
$current_oid = current_outlet_id();
$stmt = db()->prepare("SELECT * FROM stock_categories WHERE outlet_id = ? ORDER BY name_en ASC");
$stmt->execute([$current_oid]);
$data['categories'] = $stmt->fetchAll();
$stmt = db()->prepare("SELECT * FROM stock_units WHERE outlet_id = ? ORDER BY name_en ASC");
$stmt->execute([$current_oid]);
$data['units'] = $stmt->fetchAll();
$stmt = db()->prepare("SELECT * FROM suppliers WHERE outlet_id = ? ORDER BY name ASC");
$stmt->execute([$current_oid]);
$data['suppliers'] = $stmt->fetchAll();
$data['accounts'] = db()->query("SELECT * FROM acc_accounts ORDER BY code ASC")->fetchAll();
$data['customers_list'] = db()->query("SELECT * FROM customers ORDER BY name ASC")->fetchAll();
$customers = $data['customers_list']; // For backward compatibility in some modals
@ -3861,7 +3877,7 @@ switch ($page) {
$data['current_page'] = $page_num;
$oid = current_outlet_id();
$stmt = db()->prepare("SELECT i.*, i.stock_quantity, c.name_en as cat_en, c.name_ar as cat_ar, u.short_name_en as unit_en, u.short_name_ar as unit_ar, s.name as supplier_name
$stmt = db()->prepare("SELECT i.*, i.stock_quantity, c.name_en as cat_en, c.name_ar as cat_ar, COALESCE(NULLIF(u.name_en, ''), u.short_name_en) as unit_en, COALESCE(NULLIF(u.name_ar, ''), u.short_name_ar, u.name_en, u.short_name_en) as unit_ar, s.name as supplier_name
FROM stock_items i
LEFT JOIN stock_categories c ON i.category_id = c.id
@ -5895,231 +5911,152 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
</div>
<?php elseif ($page === 'units'): ?>
<?php
$unitsTotal = count($data['units'] ?? []);
$unitsArabicReady = 0;
$unitsReceiptReady = 0;
foreach (($data['units'] ?? []) as $unitSummary) {
if (!empty($unitSummary['name_ar'])) {
$unitsArabicReady++;
}
if (!empty($unitSummary['short_name_en']) && !empty($unitSummary['short_name_ar'])) {
$unitsReceiptReady++;
}
}
?>
<div class="card p-4 units-page-card">
<div class="units-shell">
<section class="units-hero">
<div class="d-flex flex-column flex-xl-row justify-content-between gap-4 align-items-xl-start">
<div class="units-hero__copy">
<span class="units-eyebrow" data-en="Stock setup" data-ar="إعدادات المخزون">Stock setup</span>
<h5 class="m-0" data-en="Units built for clean inventory, receipts, and invoices" data-ar="وحدات مهيأة لمخزون منظم وإيصالات وفواتير أوضح">Units built for clean inventory, receipts, and invoices</h5>
<p class="text-muted mt-3 mb-0" data-en="Keep names bilingual and short labels compact so item forms, tables, and printouts stay consistent." data-ar="حافظ على الأسماء ثنائية اللغة والاختصارات القصيرة حتى تبقى النماذج والجداول والمطبوعات متناسقة.">Keep names bilingual and short labels compact so item forms, tables, and printouts stay consistent.</p>
</div>
<div class="units-toolbar">
<?php if (can('units_add')): ?>
<a href="index.php?page=export&type=units&format=excel" class="btn btn-outline-success">
<i class="bi bi-download"></i> <span data-en="Export" data-ar="تصدير">Export</span>
</a>
<button class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#importUnitsModal">
<i class="bi bi-file-earmark-arrow-up"></i> <span data-en="Import" data-ar="استيراد">Import</span>
</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addUnitModal">
<i class="bi bi-plus-lg"></i> <span data-en="Add Unit" data-ar="إضافة وحدة">Add Unit</span>
</button>
<?php endif; ?>
</div>
</div>
<div class="units-stats">
<article class="units-stat">
<span class="units-stat__label" data-en="Total units" data-ar="إجمالي الوحدات">Total units</span>
<strong><?= $unitsTotal ?></strong>
<small data-en="Reusable across items and reports" data-ar="قابلة لإعادة الاستخدام عبر الأصناف والتقارير">Reusable across items and reports</small>
</article>
<article class="units-stat">
<span class="units-stat__label" data-en="Arabic-ready" data-ar="جاهزة بالعربية">Arabic-ready</span>
<strong><?= $unitsArabicReady ?></strong>
<small><?= $unitsTotal > 0 ? $unitsArabicReady . ' / ' . $unitsTotal : '0 / 0' ?></small>
</article>
<article class="units-stat">
<span class="units-stat__label" data-en="Receipt-ready" data-ar="جاهزة للإيصالات">Receipt-ready</span>
<strong><?= $unitsReceiptReady ?></strong>
<small data-en="Has both short labels" data-ar="تحتوي على الاختصارين">Has both short labels</small>
</article>
</div>
</section>
<div class="card p-4">
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-3 mb-4">
<div>
<h5 class="m-0"><span data-en="Units" data-ar="الوحدات">Units</span> (<?= count($data['units'] ?? []) ?>)</h5>
<p class="text-muted small mb-0" data-en="Manage the English and Arabic unit names used across items, invoices, and reports." data-ar="قم بإدارة أسماء الوحدات بالإنجليزية والعربية المستخدمة في الأصناف والفواتير والتقارير.">Manage the English and Arabic unit names used across items, invoices, and reports.</p>
</div>
<?php if (can('units_add')): ?>
<div class="d-flex flex-wrap gap-2">
<a href="index.php?page=export&type=units&format=excel" class="btn btn-outline-success">
<i class="bi bi-download"></i> <span data-en="Export" data-ar="تصدير">Export</span>
</a>
<button class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#importUnitsModal">
<i class="bi bi-file-earmark-arrow-up"></i> <span data-en="Import" data-ar="استيراد">Import</span>
</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addUnitModal">
<i class="bi bi-plus-lg"></i> <span data-en="Add Unit" data-ar="إضافة وحدة">Add Unit</span>
</button>
</div>
<?php endif; ?>
</div>
<section class="units-table-card">
<div class="units-table-card__header">
<div>
<h6 class="mb-1" data-en="Units catalog" data-ar="دليل الوحدات">Units catalog</h6>
<p class="text-muted small mb-0" data-en="Use clear full names and compact short codes for faster item entry." data-ar="استخدم أسماء واضحة واختصارات مختصرة لتسريع إدخال الأصناف.">Use clear full names and compact short codes for faster item entry.</p>
</div>
<span class="units-helper-pill" data-en="Tip: keep short labels under 6 characters" data-ar="نصيحة: اجعل الاختصار أقل من 6 أحرف">Tip: keep short labels under 6 characters</span>
</div>
<div class="table-responsive">
<table class="table align-middle units-table mb-0">
<thead>
<tr>
<th data-en="Unit" data-ar="الوحدة">Unit</th>
<th data-en="Short labels" data-ar="الاختصارات">Short labels</th>
<th data-en="Readiness" data-ar="الجاهزية">Readiness</th>
<th data-en="Actions" data-ar="الإجراءات" class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($data['units'])): ?>
<tr>
<td colspan="4" class="text-center py-5">
<div class="units-empty-state">
<div class="units-empty-state__icon"><i class="bi bi-rulers"></i></div>
<h6 class="mb-0" data-en="No units yet" data-ar="لا توجد وحدات بعد">No units yet</h6>
<p class="text-muted mb-0" data-en="Add your first unit so items can reuse the same labels in invoices and reports." data-ar="أضف أول وحدة حتى تستخدم الأصناف نفس التسميات في الفواتير والتقارير.">Add your first unit so items can reuse the same labels in invoices and reports.</p>
<?php if (can('units_add')): ?>
<button class="btn btn-primary mt-2" data-bs-toggle="modal" data-bs-target="#addUnitModal">
<i class="bi bi-plus-lg"></i> <span data-en="Create first unit" data-ar="إنشاء أول وحدة">Create first unit</span>
</button>
<?php endif; ?>
</div>
</td>
</tr>
<?php else: ?>
<?php foreach ($data['units'] as $u): ?>
<?php
$unitNameEn = trim((string)($u['name_en'] ?? ''));
$unitNameAr = trim((string)($u['name_ar'] ?? ''));
$unitShortEn = trim((string)($u['short_name_en'] ?? ''));
$unitShortAr = trim((string)($u['short_name_ar'] ?? ''));
$hasBilingualName = $unitNameEn !== '' && $unitNameAr !== '';
$hasReceiptShorts = $unitShortEn !== '' && $unitShortAr !== '';
?>
<tr>
<td>
<div class="units-name-stack">
<span class="units-name-stack__primary"><?= htmlspecialchars($unitNameEn !== '' ? $unitNameEn : '---') ?></span>
<span class="units-name-stack__secondary"><?= htmlspecialchars($unitNameAr !== '' ? $unitNameAr : '---') ?></span>
</div>
</td>
<td>
<div class="units-short-stack">
<span class="units-short-badge"><?= htmlspecialchars($unitShortEn !== '' ? $unitShortEn : '---') ?></span>
<span class="units-short-badge units-short-badge--muted"><?= htmlspecialchars($unitShortAr !== '' ? $unitShortAr : '---') ?></span>
</div>
</td>
<td>
<div class="units-readiness">
<span class="badge units-status-badge <?= $hasBilingualName ? 'is-ready' : 'is-pending' ?>" data-en="Bilingual" data-ar="ثنائي اللغة">Bilingual</span>
<span class="badge units-status-badge <?= $hasReceiptShorts ? 'is-ready' : 'is-pending' ?>" data-en="Receipt ready" data-ar="جاهزة للإيصال">Receipt ready</span>
</div>
</td>
<td class="text-end">
<div class="units-actions justify-content-end">
<?php if (can('units_edit')): ?>
<button class="btn btn-outline-primary btn-sm" title="Edit" data-bs-toggle="modal" data-bs-target="#editUnitModal<?= $u['id'] ?>"><i class="bi bi-pencil"></i></button>
<?php endif; ?>
<?php if (can('units_delete')): ?>
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure?')">
<input type="hidden" name="id" value="<?= $u['id'] ?>">
<button type="submit" name="delete_unit" class="btn btn-outline-danger btn-sm" title="Delete"><i class="bi bi-trash"></i></button>
</form>
<?php endif; ?>
</div>
<div class="table-responsive border rounded-3 bg-white">
<table class="table align-middle units-table mb-0">
<thead>
<tr>
<th data-en="Name (EN)" data-ar="الاسم (إنجليزي)">Name (EN)</th>
<th data-en="Name (AR)" data-ar="الاسم (عربي)">Name (AR)</th>
<th data-en="Actions" data-ar="الإجراءات" class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($data['units'])): ?>
<tr>
<td colspan="3" class="text-center py-5">
<div class="units-empty-state">
<div class="units-empty-state__icon"><i class="bi bi-rulers"></i></div>
<h6 class="mb-0" data-en="No units yet" data-ar="لا توجد وحدات بعد">No units yet</h6>
<p class="text-muted mb-0" data-en="Add your first unit so items can reuse the same labels in invoices and reports." data-ar="أضف أول وحدة حتى تستخدم الأصناف نفس التسميات في الفواتير والتقارير.">Add your first unit so items can reuse the same labels in invoices and reports.</p>
<?php if (can('units_add')): ?>
<button class="btn btn-primary mt-2" data-bs-toggle="modal" data-bs-target="#addUnitModal">
<i class="bi bi-plus-lg"></i> <span data-en="Create first unit" data-ar="إنشاء أول وحدة">Create first unit</span>
</button>
<?php endif; ?>
</div>
</td>
</tr>
<?php else: ?>
<?php foreach ($data['units'] as $u): ?>
<?php
$unitNameEn = trim((string)($u['name_en'] ?? ''));
$unitNameAr = trim((string)($u['name_ar'] ?? ''));
?>
<tr>
<td><?= htmlspecialchars($unitNameEn !== '' ? $unitNameEn : '---') ?></td>
<td><span lang="ar" dir="rtl" class="d-inline-block"><?= htmlspecialchars($unitNameAr !== '' ? $unitNameAr : '---') ?></span></td>
<td class="text-end">
<div class="units-actions justify-content-end">
<?php if (can('units_edit')): ?>
<button class="btn btn-outline-primary btn-sm" title="Edit" data-bs-toggle="modal" data-bs-target="#editUnitModal<?= $u['id'] ?>"><i class="bi bi-pencil"></i></button>
<?php endif; ?>
<?php if (can('units_delete')): ?>
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure?')">
<input type="hidden" name="id" value="<?= $u['id'] ?>">
<button type="submit" name="delete_unit" class="btn btn-outline-danger btn-sm" title="Delete"><i class="bi bi-trash"></i></button>
</form>
<?php endif; ?>
</div>
<div class="modal fade" id="editUnitModal<?= $u['id'] ?>" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content border-0 shadow-lg text-start unit-modal">
<div class="modal-header">
<div>
<span class="units-modal-kicker" data-en="Refine labels" data-ar="تحسين التسميات">Refine labels</span>
<h5 class="modal-title" data-en="Edit Unit" data-ar="تعديل الوحدة">Edit Unit</h5>
<p class="text-muted small mb-0" data-en="Update the bilingual names and short codes used across stock items and invoices." data-ar="حدّث الأسماء الثنائية والاختصارات المستخدمة في الأصناف والفواتير.">Update the bilingual names and short codes used across stock items and invoices.</p>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST">
<input type="hidden" name="id" value="<?= $u['id'] ?>">
<div class="modal-body pt-4">
<div class="unit-form-shell">
<section class="unit-form-section">
<div class="unit-form-section__header">
<div>
<h6 class="mb-1" data-en="Display names" data-ar="الأسماء الكاملة">Display names</h6>
<p class="text-muted small mb-0" data-en="Shown in item forms, reports, and detailed views." data-ar="تظهر في نماذج الأصناف والتقارير والعروض التفصيلية.">Shown in item forms, reports, and detailed views.</p>
</div>
<span class="units-helper-pill" data-en="Bilingual" data-ar="ثنائي اللغة">Bilingual</span>
</div>
<div class="unit-form-grid">
<div class="unit-field">
<label class="form-label" data-en="Name (EN)" data-ar="الاسم (إنجليزي)">Name (EN)</label>
<div class="input-group">
<input type="text" name="name_en" id="edit_unit_name_en_<?= $u['id'] ?>" class="form-control" value="<?= htmlspecialchars($unitNameEn) ?>" required>
<button class="btn btn-outline-secondary btn-translate" type="button" data-source="edit_unit_name_ar_<?= $u['id'] ?>" data-target="edit_unit_name_en_<?= $u['id'] ?>" data-to="en">
<i class="bi bi-translate"></i>
</button>
</div>
<div class="form-text" data-en="Example: Kilogram" data-ar="مثال: Kilogram">Example: Kilogram</div>
</div>
<div class="unit-field">
<label class="form-label" data-en="Name (AR)" data-ar="الاسم (عربي)">Name (AR)</label>
<div class="input-group">
<input type="text" name="name_ar" id="edit_unit_name_ar_<?= $u['id'] ?>" class="form-control" value="<?= htmlspecialchars($unitNameAr) ?>" required>
<button class="btn btn-outline-secondary btn-translate" type="button" data-source="edit_unit_name_en_<?= $u['id'] ?>" data-target="edit_unit_name_ar_<?= $u['id'] ?>" data-to="ar">
<i class="bi bi-translate"></i>
</button>
</div>
<div class="form-text" data-en="Example: كيلوغرام" data-ar="مثال: كيلوغرام">Example: كيلوغرام</div>
</div>
</div>
</section>
<section class="unit-form-section">
<div class="unit-form-section__header">
<div>
<h6 class="mb-1" data-en="Short labels" data-ar="الاختصارات">Short labels</h6>
<p class="text-muted small mb-0" data-en="Keep these compact for receipts, tables, and barcode labels." data-ar="اجعلها مختصرة للإيصالات والجداول وملصقات الباركود.">Keep these compact for receipts, tables, and barcode labels.</p>
</div>
<span class="units-helper-pill" data-en="Receipts" data-ar="الإيصالات">Receipts</span>
</div>
<div class="unit-form-grid">
<div class="unit-field">
<label class="form-label" data-en="Short (EN)" data-ar="الاختصار (إنجليزي)">Short (EN)</label>
<input type="text" name="short_en" class="form-control" value="<?= htmlspecialchars($unitShortEn) ?>" maxlength="12" required placeholder="Kg">
<div class="form-text" data-en="Best for item tables and invoice lines." data-ar="مثالي لصفوف الأصناف في الفواتير والجداول.">Best for item tables and invoice lines.</div>
</div>
<div class="unit-field">
<label class="form-label" data-en="Short (AR)" data-ar="الاختصار (عربي)">Short (AR)</label>
<input type="text" name="short_ar" class="form-control" value="<?= htmlspecialchars($unitShortAr) ?>" maxlength="12" required placeholder="كجم">
<div class="form-text" data-en="Keep it easy to read when printed." data-ar="اجعله سهل القراءة عند الطباعة.">Keep it easy to read when printed.</div>
</div>
</div>
<div class="unit-preview-card mt-3">
<span class="unit-preview-card__label" data-en="Current preview" data-ar="المعاينة الحالية">Current preview</span>
<div class="unit-preview-row">
<span class="unit-preview-chip"><?= htmlspecialchars($unitNameEn !== '' ? $unitNameEn : 'Name (EN)') ?></span>
<span class="unit-preview-chip is-muted"><?= htmlspecialchars($unitShortEn !== '' ? $unitShortEn : 'Kg') ?></span>
<span class="unit-preview-chip"><?= htmlspecialchars($unitNameAr !== '' ? $unitNameAr : 'الاسم') ?></span>
<span class="unit-preview-chip is-muted"><?= htmlspecialchars($unitShortAr !== '' ? $unitShortAr : 'كجم') ?></span>
</div>
</div>
</section>
</div>
</div>
<div class="modal-footer pt-0 border-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Cancel" data-ar="إلغاء">Cancel</button>
<button type="submit" name="edit_unit" class="btn btn-primary" data-en="Update" data-ar="تحديث">Update</button>
</div>
</form>
<div class="modal fade" id="editUnitModal<?= $u['id'] ?>" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content border-0 shadow-lg text-start unit-modal">
<div class="modal-header">
<div>
<span class="units-modal-kicker" data-en="Refine labels" data-ar="تحسين التسميات">Refine labels</span>
<h5 class="modal-title" data-en="Edit Unit" data-ar="تعديل الوحدة">Edit Unit</h5>
<p class="text-muted small mb-0" data-en="Update the English and Arabic unit names used across stock items and invoices." data-ar="حدّث أسماء الوحدة بالإنجليزية والعربية المستخدمة في الأصناف والفواتير.">Update the English and Arabic unit names used across stock items and invoices.</p>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST">
<input type="hidden" name="id" value="<?= $u['id'] ?>">
<div class="modal-body pt-4">
<div class="unit-form-shell">
<section class="unit-form-section">
<div class="unit-form-section__header">
<div>
<h6 class="mb-1" data-en="Display names" data-ar="الأسماء الكاملة">Display names</h6>
<p class="text-muted small mb-0" data-en="Shown in item forms, reports, and detailed views." data-ar="تظهر في نماذج الأصناف والتقارير والعروض التفصيلية.">Shown in item forms, reports, and detailed views.</p>
</div>
<span class="units-helper-pill" data-en="Bilingual" data-ar="ثنائي اللغة">Bilingual</span>
</div>
<div class="unit-form-grid">
<div class="unit-field">
<label class="form-label" data-en="Name (EN)" data-ar="الاسم (إنجليزي)">Name (EN)</label>
<div class="input-group">
<input type="text" name="name_en" id="edit_unit_name_en_<?= $u['id'] ?>" class="form-control" value="<?= htmlspecialchars($unitNameEn) ?>" required>
<button class="btn btn-outline-secondary btn-translate" type="button" data-source="edit_unit_name_ar_<?= $u['id'] ?>" data-target="edit_unit_name_en_<?= $u['id'] ?>" data-to="en">
<i class="bi bi-translate"></i>
</button>
</div>
<div class="form-text" data-en="Example: Kilogram" data-ar="مثال: Kilogram">Example: Kilogram</div>
</div>
<div class="unit-field">
<label class="form-label" data-en="Name (AR)" data-ar="الاسم (عربي)">Name (AR)</label>
<div class="input-group">
<input type="text" name="name_ar" id="edit_unit_name_ar_<?= $u['id'] ?>" class="form-control" value="<?= htmlspecialchars($unitNameAr) ?>" required>
<button class="btn btn-outline-secondary btn-translate" type="button" data-source="edit_unit_name_en_<?= $u['id'] ?>" data-target="edit_unit_name_ar_<?= $u['id'] ?>" data-to="ar">
<i class="bi bi-translate"></i>
</button>
</div>
<div class="form-text" data-en="Example: كيلوغرام" data-ar="مثال: كيلوغرام">Example: كيلوغرام</div>
</div>
</div>
</section>
<section class="unit-form-section">
<div class="unit-form-section__header">
<div>
<h6 class="mb-1" data-en="Name preview" data-ar="معاينة الأسماء">Name preview</h6>
<p class="text-muted small mb-0" data-en="This is how the unit name will appear across the app." data-ar="هكذا سيظهر اسم الوحدة في جميع أجزاء التطبيق.">This is how the unit name will appear across the app.</p>
</div>
<span class="units-helper-pill" data-en="Live label" data-ar="التسمية النهائية">Live label</span>
</div>
<div class="unit-preview-card mt-0">
<span class="unit-preview-card__label" data-en="Current preview" data-ar="المعاينة الحالية">Current preview</span>
<div class="unit-preview-row">
<span class="unit-preview-chip"><?= htmlspecialchars($unitNameEn !== '' ? $unitNameEn : 'Name (EN)') ?></span>
<span class="unit-preview-chip"><?= htmlspecialchars($unitNameAr !== '' ? $unitNameAr : 'الاسم') ?></span>
</div>
</div>
</section>
</div>
</div>
<div class="modal-footer pt-0 border-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Cancel" data-ar="إلغاء">Cancel</button>
<button type="submit" name="edit_unit" class="btn btn-primary" data-en="Update" data-ar="تحديث">Update</button>
</div>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</section>
</div>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?= renderPagination($data['current_page'] ?? 1, $data['total_pages'] ?? 1) ?>
@ -6311,7 +6248,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
<div class="col-md-4"><label class="form-label">Name (AR)</label><div class="input-group"><input type="text" name="name_ar" id="editItemNameAr<?= $item['id'] ?>" class="form-control" value="<?= htmlspecialchars((string)($item['name_ar'] ?? '')) ?>" required><button class="btn btn-outline-secondary btn-translate" type="button" data-source="editItemNameEn<?= $item['id'] ?>" data-target="editItemNameAr<?= $item['id'] ?>" data-to="ar"><i class="bi bi-translate"></i> AR</button></div></div>
<div class="col-md-4"><label class="form-label">SKU</label><input type="text" name="sku" class="form-control" value="<?= htmlspecialchars((string)($item['sku'] ?? '')) ?>"><div class="form-text">13-digit scale barcodes with the reserved prefixes are not allowed here. Save the 5-digit scale item code instead.</div></div>
<div class="col-md-4"><label class="form-label" data-en="Category" data-ar="الفئة">Category</label><select name="category_id" class="form-select"><option value="">---</option><?php foreach ($data['categories'] ?? [] as $c): ?><option value="<?= $c['id'] ?>" <?= $c['id'] == $item['category_id'] ? 'selected' : '' ?>><?= htmlspecialchars($c['name_en']) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label">Unit</label><select name="unit_id" class="form-select"><option value="">---</option><?php foreach ($data['units'] ?? [] as $u): ?><option value="<?= $u['id'] ?>" <?= $u['id'] == $item['unit_id'] ? 'selected' : '' ?>><?= htmlspecialchars($u['short_name_en']) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label">Unit</label><select name="unit_id" class="form-select"><option value="">---</option><?php foreach ($data['units'] ?? [] as $u): ?><option value="<?= $u['id'] ?>" <?= $u['id'] == $item['unit_id'] ? 'selected' : '' ?>><?= htmlspecialchars((trim((string)($u['name_en'] ?? '')) !== '' ? $u['name_en'] : ($u['short_name_en'] ?? '---')) . ' / ' . (trim((string)($u['name_ar'] ?? '')) !== '' ? $u['name_ar'] : ($u['short_name_ar'] ?? '---'))) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label" data-en="Supplier" data-ar="المورد">Supplier</label><select name="supplier_id" class="form-select"><option value="">---</option><?php foreach ($data['suppliers'] ?? [] as $s): ?><option value="<?= $s['id'] ?>" <?= $s['id'] == $item['supplier_id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label">Sale Price</label><input type="number" step="0.001" name="sale_price" class="form-control" value="<?= (float)$item['sale_price'] ?>"></div>
<div class="col-md-4"><label class="form-label">Purchase Price</label><input type="number" step="0.001" name="purchase_price" class="form-control" value="<?= (float)$item['purchase_price'] ?>"></div>
@ -11320,7 +11257,7 @@ function loadSessionReport(id) {
<div class="col-md-4"><label class="form-label" data-en="Name (AR)" data-ar="الاسم (عربي)">Name (AR)</label><div class="input-group"><input type="text" name="name_ar" id="addItemNameAr" class="form-control" required><button class="btn btn-outline-secondary btn-translate" type="button" data-source="addItemNameEn" data-target="addItemNameAr" data-to="ar"><i class="bi bi-translate"></i> AR</button></div></div>
<div class="col-md-4"><label class="form-label" data-en="SKU / Barcode" data-ar="الباركود">SKU / Barcode</label><div class="input-group"><input type="text" name="sku" id="skuInput" class="form-control"><button class="btn btn-outline-secondary" type="button" id="suggestSkuBtn" data-en="Suggest" data-ar="اقتراح">Suggest</button></div><div class="form-text" data-en="Reserved 13-digit scale barcodes cannot be saved here. Use the 5-digit scale item code for weighing products." data-ar="لا يمكن حفظ باركود الميزان الكامل المكوّن من 13 رقمًا هنا. استخدم كود الصنف المكوّن من 5 أرقام لأصناف الميزان.">Reserved 13-digit scale barcodes cannot be saved here. Use the 5-digit scale item code for weighing products.</div></div>
<div class="col-md-4"><label class="form-label" data-en="Category" data-ar="الفئة">Category</label><select name="category_id" class="form-select"><option value="">---</option><?php foreach ($data['categories'] ?? [] as $c): ?><option value="<?= $c['id'] ?>"><?= htmlspecialchars($c['name_en']) ?> / <?= htmlspecialchars($c['name_ar']) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label" data-en="Unit" data-ar="الوحدة">Unit</label><select name="unit_id" class="form-select"><option value="">---</option><?php foreach ($data['units'] ?? [] as $u): ?><option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['short_name_en']) ?> / <?= htmlspecialchars($u['short_name_ar']) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label" data-en="Unit" data-ar="الوحدة">Unit</label><select name="unit_id" class="form-select"><option value="">---</option><?php foreach ($data['units'] ?? [] as $u): ?><option value="<?= $u['id'] ?>"><?= htmlspecialchars(trim((string)($u['name_en'] ?? '')) !== '' ? $u['name_en'] : ($u['short_name_en'] ?? '---')) ?> / <?= htmlspecialchars(trim((string)($u['name_ar'] ?? '')) !== '' ? $u['name_ar'] : ($u['short_name_ar'] ?? '---')) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label" data-en="Supplier" data-ar="المورد">Supplier</label><select name="supplier_id" class="form-select"><option value="">---</option><?php foreach ($data['suppliers'] ?? [] as $s): ?><option value="<?= $s['id'] ?>"><?= htmlspecialchars($s['name']) ?></option><?php endforeach; ?></select></div>
<div class="col-md-4"><label class="form-label" data-en="Sale Price" data-ar="سعر البيع">Sale Price</label><input type="number" step="0.001" name="sale_price" class="form-control" value="0.000"></div>
<div class="col-md-4"><label class="form-label" data-en="Purchase Price" data-ar="سعر الشراء">Purchase Price</label><input type="number" step="0.001" name="purchase_price" class="form-control" value="0.000"></div>
@ -11479,9 +11416,9 @@ function loadSessionReport(id) {
<div class="unit-import-note mb-3">
<strong data-en="Supported columns" data-ar="الأعمدة المدعومة">Supported columns</strong>
<ul class="mt-2 small">
<li data-en="Name (EN), Name (AR), Short (EN), Short (AR)" data-ar="الاسم (إنجليزي)، الاسم (عربي)، الاختصار (إنجليزي)، الاختصار (عربي)">Name (EN), Name (AR), Short (EN), Short (AR)</li>
<li data-en="Name (EN), Name (AR)" data-ar="الاسم (إنجليزي)، الاسم (عربي)">Name (EN), Name (AR)</li>
<li data-en="CSV and XLSX files are accepted." data-ar="يتم قبول ملفات CSV و XLSX.">CSV and XLSX files are accepted.</li>
<li data-en="Keep short labels concise for invoices and receipts." data-ar="اجعل الاختصارات قصيرة للفواتير والإيصالات.">Keep short labels concise for invoices and receipts.</li>
<li data-en="If Arabic is blank, the English name will be reused automatically." data-ar="إذا كان الاسم العربي فارغاً فسيتم استخدام الاسم الإنجليزي تلقائياً.">If Arabic is blank, the English name will be reused automatically.</li>
</ul>
</div>
<div class="unit-form-section">
@ -11548,7 +11485,7 @@ function loadSessionReport(id) {
<div>
<span class="units-modal-kicker" data-en="Create unit" data-ar="إنشاء وحدة">Create unit</span>
<h5 class="modal-title" data-en="Add Unit" data-ar="إضافة وحدة">Add Unit</h5>
<p class="text-muted small mb-0" data-en="Use full bilingual names and short labels that stay readable on invoices and item cards." data-ar="استخدم أسماء ثنائية اللغة واختصارات قصيرة تبقى واضحة في الفواتير وبطاقات الأصناف.">Use full bilingual names and short labels that stay readable on invoices and item cards.</p>
<p class="text-muted small mb-0" data-en="Use the full unit name in English and Arabic so stock screens stay consistent." data-ar="استخدم اسم الوحدة الكامل بالإنجليزية والعربية حتى تبقى شاشات المخزون متناسقة.">Use the full unit name in English and Arabic so stock screens stay consistent.</p>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
@ -11589,30 +11526,16 @@ function loadSessionReport(id) {
<section class="unit-form-section">
<div class="unit-form-section__header">
<div>
<h6 class="mb-1" data-en="Short labels" data-ar="الاختصارات">Short labels</h6>
<p class="text-muted small mb-0" data-en="Keep these compact for receipts, tables, and barcode labels." data-ar="اجعلها مختصرة للإيصالات والجداول وملصقات الباركود.">Keep these compact for receipts, tables, and barcode labels.</p>
<h6 class="mb-1" data-en="Name preview" data-ar="معاينة الأسماء">Name preview</h6>
<p class="text-muted small mb-0" data-en="This is how the unit name will appear across the app." data-ar="هكذا سيظهر اسم الوحدة في جميع أجزاء التطبيق.">This is how the unit name will appear across the app.</p>
</div>
<span class="units-helper-pill" data-en="Receipts" data-ar="الإيصالات">Receipts</span>
<span class="units-helper-pill" data-en="Live label" data-ar="التسمية النهائية">Live label</span>
</div>
<div class="unit-form-grid">
<div class="unit-field">
<label class="form-label" data-en="Short (EN)" data-ar="الاختصار (إنجليزي)">Short (EN)</label>
<input type="text" name="short_en" class="form-control" maxlength="12" required placeholder="Kg">
<div class="form-text" data-en="Best for item tables and invoice lines." data-ar="مثالي لصفوف الأصناف في الفواتير والجداول.">Best for item tables and invoice lines.</div>
</div>
<div class="unit-field">
<label class="form-label" data-en="Short (AR)" data-ar="الاختصار (عربي)">Short (AR)</label>
<input type="text" name="short_ar" class="form-control" maxlength="12" required placeholder="كجم">
<div class="form-text" data-en="Keep it easy to read when printed." data-ar="اجعله سهل القراءة عند الطباعة.">Keep it easy to read when printed.</div>
</div>
</div>
<div class="unit-preview-card mt-3">
<div class="unit-preview-card mt-0">
<span class="unit-preview-card__label" data-en="Example preview" data-ar="معاينة مثال">Example preview</span>
<div class="unit-preview-row">
<span class="unit-preview-chip">Kilogram</span>
<span class="unit-preview-chip is-muted">Kg</span>
<span class="unit-preview-chip">كيلوغرام</span>
<span class="unit-preview-chip is-muted">كجم</span>
</div>
</div>
</section>

View File

@ -174,3 +174,13 @@
2026-05-02 04:56:12 - POST: {"username":"admin","password":"admin","login":"1"}
2026-05-02 04:56:19 - POST: {"username":"admin","password":"admin","login":"1"}
2026-05-02 04:56:42 - POST: {"id":"3","name_en":"Bank Transfer","name_ar":"\u062a\u062d\u0648\u064a\u0644 \u0628\u0646\u0643\u064a","edit_payment_method":""}
2026-05-02 17:20:51 - POST: {"name_en":"piece","name_ar":"\u062d\u0628\u0629","add_category":""}
2026-05-02 17:23:36 - POST: {"action":"translate","text":"piece","target":"ar"}
2026-05-02 17:29:22 - POST: {"action":"translate","text":"piece","target":"ar"}
2026-05-02 17:29:31 - POST: {"action":"translate","text":"piece","target":"ar"}
2026-05-02 17:29:44 - POST: {"name_en":"piece","name_ar":"\u062d\u0628\u0629","add_unit":""}
2026-05-02 17:32:00 - POST: {"action":"translate","text":"piece","target":"ar"}
2026-05-02 17:32:02 - POST: {"action":"translate","text":"%D9%82%D8%B7%D8%B9%D8%A9","target":"en"}
2026-05-02 17:32:10 - POST: {"action":"translate","text":"\u0642\u0637\u0639\u0629","target":"en"}
2026-05-02 17:32:11 - POST: {"action":"translate","text":"LAMING PINEAPPLE 454G","target":"ar"}
2026-05-02 17:32:54 - POST: {"action":"translate","text":"onion","target":"ar"}